diff options
| author | 2025-07-31 01:22:07 -0400 | |
|---|---|---|
| committer | 2025-07-31 01:22:07 -0400 | |
| commit | d9539f96cc7ac112b7d8faad022190fbbc88c745 (patch) | |
| tree | 471249d60b9202c00d7d82abec8b296fc881292e /gallery_dl/extractor/naverblog.py | |
| parent | 889fc15f272118bf277737b6fac29d3faeffc641 (diff) | |
| parent | a6e995c093de8aae2e91a0787281bb34c0b871eb (diff) | |
Update upstream source from tag 'upstream/1.30.2'
Update to upstream version '1.30.2'
with Debian dir f0dcd28a671f8600479182ff128e05ba8904a0d8
Diffstat (limited to 'gallery_dl/extractor/naverblog.py')
| -rw-r--r-- | gallery_dl/extractor/naverblog.py | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/gallery_dl/extractor/naverblog.py b/gallery_dl/extractor/naverblog.py new file mode 100644 index 0000000..302cb63 --- /dev/null +++ b/gallery_dl/extractor/naverblog.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- + +# Copyright 2019-2025 Mike Fährmann +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. + +"""Extractors for https://blog.naver.com/""" + +from .common import GalleryExtractor, Extractor, Message +from .. import text, util +import datetime +import time + + +class NaverBlogBase(): + """Base class for blog.naver.com extractors""" + category = "naver-blog" + root = "https://blog.naver.com" + + +class NaverBlogPostExtractor(NaverBlogBase, GalleryExtractor): + """Extractor for blog posts on blog.naver.com""" + subcategory = "post" + filename_fmt = "{num:>03}.{extension}" + directory_fmt = ("{category}", "{blog[user]} {blog[id]}", + "{post[date]:%Y-%m-%d} {post[title]}") + archive_fmt = "{blog[id]}_{post[num]}_{num}" + pattern = (r"(?:https?://)?blog\.naver\.com/" + r"(?:PostView\.n(?:aver|hn)\?blogId=(\w+)&logNo=(\d+)|" + r"(\w+)/(\d+)/?$)") + example = "https://blog.naver.com/BLOGID/12345" + + def __init__(self, match): + if blog_id := match[1]: + self.blog_id = blog_id + self.post_id = match[2] + else: + self.blog_id = match[3] + self.post_id = match[4] + + url = (f"{self.root}/PostView.nhn" + f"?blogId={self.blog_id}&logNo={self.post_id}") + GalleryExtractor.__init__(self, match, url) + + def metadata(self, page): + extr = text.extract_from(page) + data = { + "post": { + "title" : text.unescape(extr( + '"og:title" content="', '"')), + "description": text.unescape(extr( + '"og:description" content="', '"')).replace(" ", " "), + "num" : text.parse_int(self.post_id), + }, + "blog": { + "id" : self.blog_id, + "num" : text.parse_int(extr("var blogNo = '", "'")), + "user" : extr("var nickName = '", "'"), + }, + } + + data["post"]["date"] = self._parse_datetime( + extr('se_publishDate pcol2">', '<') or + extr('_postAddDate">', '<')) + + return data + + def _parse_datetime(self, date_string): + if "전" in date_string: + ts = time.gmtime() + return datetime.datetime(ts.tm_year, ts.tm_mon, ts.tm_mday) + return text.parse_datetime(date_string, "%Y. %m. %d. %H:%M") + + def images(self, page): + files = [] + self._extract_images(files, page) + if self.config("videos", True): + self._extract_videos(files, page) + return files + + def _extract_images(self, files, page): + for url in text.extract_iter(page, 'data-lazy-src="', '"'): + url = url.replace("://post", "://blog", 1).partition("?")[0] + if "\ufffd" in text.unquote(url): + url = text.unquote(url, encoding="EUC-KR") + files.append((url, None)) + + def _extract_videos(self, files, page): + for module in text.extract_iter(page, " data-module='", "'></"): + if '"v2_video"' not in module: + continue + media = util.json_loads(module)["data"] + try: + self._extract_media(files, media) + except Exception as exc: + self.log.warning("%s: Failed to extract video '%s' (%s: %s)", + self.post_id, media.get("vid"), + exc.__class__.__name__, exc) + + def _extract_media(self, files, media): + url = ("https://apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/" + + media["vid"]) + params = { + "key" : media["inkey"], + "sid" : "2", + # "pid": "00000000-0000-0000-0000-000000000000", + "nonce": int(time.time()), + "devt" : "html5_pc", + "prv" : "N", + "aup" : "N", + "stpb" : "N", + "cpl" : "ko_KR", + "providerEnv": "real", + "adt" : "glad", + "lc" : "ko_KR", + } + data = self.request_json(url, params=params) + video = max(data["videos"]["list"], + key=lambda v: v.get("size") or 0) + files.append((video["source"], video)) + + +class NaverBlogBlogExtractor(NaverBlogBase, Extractor): + """Extractor for a user's blog on blog.naver.com""" + subcategory = "blog" + categorytransfer = True + pattern = (r"(?:https?://)?blog\.naver\.com/" + r"(?:PostList\.n(?:aver|hn)\?(?:[^&#]+&)*blogId=([^&#]+)|" + r"(\w+)/?$)") + example = "https://blog.naver.com/BLOGID" + + def __init__(self, match): + Extractor.__init__(self, match) + self.blog_id = match[1] or match[2] + + def items(self): + # fetch first post number + url = f"{self.root}/PostList.nhn?blogId={self.blog_id}" + post_num = text.extr( + self.request(url).text, 'gnFirstLogNo = "', '"', + ) + + # setup params for API calls + url = f"{self.root}/PostViewBottomTitleListAsync.nhn" + params = { + "blogId" : self.blog_id, + "logNo" : post_num or "0", + "viewDate" : "", + "categoryNo" : "", + "parentCategoryNo" : "", + "showNextPage" : "true", + "showPreviousPage" : "false", + "sortDateInMilli" : "", + "isThumbnailViewType": "false", + "countPerPage" : "", + } + + # loop over all posts + while True: + data = self.request_json(url, params=params) + + for post in data["postList"]: + post["url"] = (f"{self.root}/PostView.nhn?blogId=" + f"{self.blog_id}&logNo={post['logNo']}") + post["_extractor"] = NaverBlogPostExtractor + yield Message.Queue, post["url"], post + + if not data["hasNextPage"]: + return + params["logNo"] = data["nextIndexLogNo"] + params["sortDateInMilli"] = data["nextIndexSortDate"] |
