From cc72e543022681d4a084b89e8dd00791b2607ec1 Mon Sep 17 00:00:00 2001 From: JoeamAmier Date: Sat, 2 Dec 2023 10:33:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=A1=B9=E7=9B=AE=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 38 +++------- source/{Download.py => Downloader.py} | 48 +++++------- source/Explore.py | 4 +- source/Html.py | 9 ++- source/Image.py | 4 +- source/Manager.py | 8 ++ source/Settings.py | 5 +- source/Video.py | 9 +-- source/__init__.py | 71 +++++++++--------- ...e\347\244\272\346\204\217\345\233\276.png" | Bin 24477 -> 0 bytes 10 files changed, 90 insertions(+), 106 deletions(-) rename source/{Download.py => Downloader.py} (55%) delete mode 100644 "static/\350\216\267\345\217\226Cookie\347\244\272\346\204\217\345\233\276.png" diff --git a/README.md b/README.md index 68def23..56d1176 100644 --- a/README.md +++ b/README.md @@ -25,46 +25,44 @@

🥣 使用方法

如果仅需下载作品文件,选择 直接运行 或者 源码运行 均可,如果需要获取作品信息,则需要进行二次开发进行调用。

🖱 直接运行

-

前往 Releases 下载程序压缩包,解压后打开程序文件夹,双击运行 main.exe 即可使用。

+

前往 Releases 下载程序压缩包,解压后打开程序文件夹,双击运行 main.exe 即可使用。

⌨️ 源码运行

  1. 安装版本号不低于 3.12 的 Python 解释器
  2. 运行 pip install -r requirements.txt 命令安装程序所需模块
  3. -
  4. 下载本项目最新的源码或 Releases 发布的源码至本地
  5. +
  6. 下载本项目最新的源码或 Releases 发布的源码至本地
  7. 运行 main.py 即可使用

💻 二次开发

如果想要获取小红书图文/视频作品信息,可以根据 main.py 的注释提示进行代码调用。

 # 测试链接
-error_demo = "https://github.com/JoeanAmier/XHS-Downloader"
+error_demo = "https://github.com/JoeanAmier/XHS_Downloader"
 image_demo = "https://www.xiaohongshu.com/explore/63b275a30000000019020185"
 video_demo = "https://www.xiaohongshu.com/explore/64edb460000000001f03cadc"
+multiple_demo = f"{image_demo} {video_demo}"
 # 实例对象
-path = ""  # 作品下载储存根路径,默认值:当前路径
+path = "D:\\"  # 作品下载储存根路径,默认值:当前路径
 folder = "Download"  # 作品下载文件夹名称(自动创建),默认值:Download
-cookie = ""  # 小红书网页版 Cookie
 proxies = None  # 网络代理
 timeout = 5  # 网络请求超时限制,默认值:10
 chunk = 1024 * 1024  # 下载文件时,每次从服务器获取的数据块大小,单位字节
 xhs = XHS(
     path=path,
     folder=folder,
-    cookie=cookie,
     proxies=proxies,
     timeout=timeout,
     chunk=chunk, )  # 使用自定义参数
 # xhs = XHS()  # 使用默认参数
-download = True  # 是否下载作品文件
+download = True  # 是否下载作品文件,默认值:False
 # 返回作品详细信息,包括下载地址
 print(xhs.extract(error_demo))  # 获取数据失败时返回空字典
 print(xhs.extract(image_demo, download=download))
 print(xhs.extract(video_demo, download=download))
+print(xhs.extract(multiple_demo, download=download))
 
-

⛓ 批量下载

-

在程序所在文件夹创建一个 xhs.txt 文本文件,然后将待处理的作品链接输入文件,每行输入一个作品链接,编辑完成后保存文件,然后运行程序,点击 读取 xhs.txt 文件并批量下载作品 按钮,程序会批量下载每个链接对应的作品文件。

⚙️ 配置文件

-

根目录下的 settings.json 文件,可以自定义部分运行参数。

+

项目根目录下的 settings.json 文件,可以自定义部分运行参数。

@@ -88,12 +86,6 @@ print(xhs.extract(video_demo, download=download)) - - - - - - @@ -109,20 +101,10 @@ print(xhs.extract(video_demo, download=download)) - +
Download
cookiestr小红书网页版 Cookie,无需登录;建议自行设置内置 Cookie
proxies str 设置代理chunk int 下载文件时,每次从服务器获取的数据块大小,单位:字节262144(256KB)1048576(1 MB)
-

🌐 Cookie

-
    -
  1. 打开浏览器(可选无痕模式启动),访问小红书任意网页
  2. -
  3. F12 打开开发人员工具
  4. -
  5. 选择 控制台 选项卡
  6. -
  7. 输入 document.cookie 后回车确认
  8. -
  9. 输出内容即为所需 Cookie
  10. -
-
-

♥️ 支持项目

如果 XHS-Downloader 对您有帮助,请考虑为它点个 Star ⭐,感谢您的支持!

@@ -141,7 +123,7 @@ print(xhs.extract(video_demo, download=download))

如果您愿意,可以考虑提供资助为 XHS-Downloader 提供额外的支持!

✉️ 联系作者

diff --git a/source/Download.py b/source/Downloader.py similarity index 55% rename from source/Download.py rename to source/Downloader.py index e503c7e..2976a61 100644 --- a/source/Download.py +++ b/source/Downloader.py @@ -3,31 +3,31 @@ from requests import exceptions from requests import get -from .Manager import Manager - __all__ = ['Download'] class Download: - manager = Manager() def __init__( self, + manager, root: Path, path: str, folder: str, - headers: dict, proxies=None, - chunk=1024 * 1024, ): - self.temp = root.joinpath("./temp") + chunk=1024 * 1024, + timeout=10, ): + self.manager = manager + self.temp = manager.temp + self.headers = manager.headers self.root = self.__init_root(root, path, folder) - self.headers = self.__delete_cookie(headers) self.proxies = { "http": proxies, "https": proxies, "ftp": proxies, } self.chunk = chunk + self.timeout = timeout def __init_root(self, root: Path, path: str, folder: str) -> Path: if path and (r := Path(path)).is_dir(): @@ -38,39 +38,29 @@ def __init_root(self, root: Path, path: str, folder: str) -> Path: self.temp.mkdir(exist_ok=True) return root - def run(self, urls: list, name: str, type_: int, log): + def run(self, urls: list, name: str, type_: int): if type_ == 0: - self.__download(urls[0], f"{name}.mp4", log) + self.__download(urls[0], f"{name}.mp4") elif type_ == 1: for index, url in enumerate(urls): - self.__download(url, f"{name}_{index + 1}.jpeg", log) + self.__download(url, f"{name}_{index + 1}.png") - def __download(self, url: str, name: str, log): + def __download(self, url: str, name: str): temp = self.temp.joinpath(name) file = self.root.joinpath(name) if self.manager.is_exists(file): - self.output_prompt(f"文件 {name} 已存在,跳过下载!", log) return try: - with get(url, headers=self.headers, proxies=self.proxies, stream=True) as response: + with get(url, headers=self.headers, proxies=self.proxies, stream=True, timeout=self.timeout) as response: with temp.open("wb") as f: for chunk in response.iter_content(chunk_size=self.chunk): f.write(chunk) self.manager.move(temp, file) - self.output_prompt(f"文件 {name} 下载成功!", log) - except exceptions.ChunkedEncodingError: + except ( + exceptions.ProxyError, + exceptions.SSLError, + exceptions.ChunkedEncodingError, + exceptions.ConnectionError, + exceptions.ReadTimeout, + ): self.manager.delete(temp) - self.output_prompt(f"网络异常,文件 {name} 下载失败!", log) - - @staticmethod - def __delete_cookie(headers: dict) -> dict: - download_headers = headers.copy() - del download_headers["Cookie"] - return download_headers - - @staticmethod - def output_prompt(tip: str, log): - if log: - log.write_line(tip) - else: - print(tip) diff --git a/source/Explore.py b/source/Explore.py index dedee19..0a25d5b 100644 --- a/source/Explore.py +++ b/source/Explore.py @@ -15,8 +15,8 @@ def run(self, html: str) -> dict: return self.__extract_data(data) def __get_json_data(self, html: str) -> dict: - data = self.explore_data.findall(html) - return {} if len(data) != 1 else loads(data[0]) + data = self.explore_data.search(html) + return loads(data.group(1)) if data else {} def __extract_data(self, data: dict) -> dict: result = {} diff --git a/source/Html.py b/source/Html.py index 51521f4..aad8339 100644 --- a/source/Html.py +++ b/source/Html.py @@ -19,11 +19,12 @@ def __init__( } self.timeout = timeout - def get_html( + def request_url( self, url: str, params=None, - headers=None, ) -> str: + headers=None, + text=True, ) -> str: try: response = get( url, @@ -38,9 +39,9 @@ def get_html( exceptions.ConnectionError, exceptions.ReadTimeout, ): - print("获取网页源码失败,请尝试设置 Cookie 后重试!") + print("网络异常,获取网页源码失败!") return "" - return response.text + return response.text if text else response.url @staticmethod def format_url(url: str) -> str: diff --git a/source/Image.py b/source/Image.py index fcc6e73..f36df8f 100644 --- a/source/Image.py +++ b/source/Image.py @@ -1,5 +1,6 @@ from json import loads from re import compile + __all__ = ['Image'] @@ -25,8 +26,7 @@ def __generate_image_link(token: str) -> str: return f"https://ci.xiaohongshu.com/{token}?imageView2/2/w/format/png" def __extract_image_token(self, url: str) -> str: - return self.__generate_image_link(token[0]) if len( - token := self.IMAGE_TOKEN.findall(url)) == 1 else "" + return self.__generate_image_link(token.group(1)) if (token := self.IMAGE_TOKEN.search(url)) else "" def __extract_image_urls(self, data: list[dict]) -> list[str]: urls = [] diff --git a/source/Manager.py b/source/Manager.py index a37ce2c..340c780 100644 --- a/source/Manager.py +++ b/source/Manager.py @@ -5,6 +5,14 @@ class Manager: + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/119.0.0.0 Safari/537.36", + } + + def __init__(self, root: Path): + self.temp = root.joinpath("./temp") + @staticmethod def is_exists(path: Path) -> bool: return path.exists() diff --git a/source/Settings.py b/source/Settings.py index 10444d3..f705536 100644 --- a/source/Settings.py +++ b/source/Settings.py @@ -6,16 +6,17 @@ class Settings: - file = Path(__file__).resolve().parent.parent.joinpath("./settings.json") default = { "path": "", "folder": "Download", - "cookie": "", "proxies": None, "timeout": 10, "chunk": 1024 * 1024, } + def __init__(self, root: Path): + self.file = root.joinpath("./settings.json") + def run(self): return self.read() if self.file.is_file() else self.create() diff --git a/source/Video.py b/source/Video.py index 8747430..f3d7cfa 100644 --- a/source/Video.py +++ b/source/Video.py @@ -1,12 +1,11 @@ from re import compile -from .Html import Html - __all__ = ['Video'] class Video: - VIDEO_ID = compile(r'"masterUrl":"(.*?)"') + VIDEO_TOKEN = compile(r'"originVideoKey":"pre_post\\u002F(\S+?)"') - def get_video_link(self, html: str): - return [Html.format_url(u) for u in self.VIDEO_ID.findall(html)] + def get_video_link(self, html: str) -> list: + return [f"https://sns-video-hw.xhscdn.com/pre_post/{ + t.group(1)}"] if (t := self.VIDEO_TOKEN.search(html)) else [] diff --git a/source/__init__.py b/source/__init__.py index a427dbb..fa6a13b 100644 --- a/source/__init__.py +++ b/source/__init__.py @@ -14,10 +14,11 @@ from textual.widgets import Label from textual.widgets import Log -from .Download import Download +from .Downloader import Download from .Explore import Explore from .Html import Html from .Image import Image +from .Manager import Manager from .Settings import Settings from .Video import Video @@ -26,78 +27,79 @@ class XHS: ROOT = Path(__file__).resolve().parent.parent - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome" - "/116.0.0.0 Safari/537.36", - "Cookie": "abRequestId=c76828f5-4f37-5b3b-8cc3-036eb91b2edb; webBuild=3.14.1; xsecappid=xhs-pc-web; " - "a1=18ba9b2b23co9uwihz4adkebwsw05g8upycgsldyj50000141248; webId=23ee7745020025247828cf8d6d0decff; " - "websectiga=6169c1e84f393779a5f7de7303038f3b47a78e47be716e7bec57ccce17d45f99; " - "sec_poison_id=ae001863-a9db-4463-ad78-ede3aac4e5b1; gid=yYD0jDJDWyU4yYD0jDJDJv1fqSlj7E3xu40fSvVTd" - "DEMEk2882kY7M888y4yJ4Y8D8SK0iiK; web_session=030037a2797dde5008c3e66f32224a8af75429; ", - } - links = compile(r"https://www.xiaohongshu.com/explore/[0-9a-z]+") + link = compile(r"https://www\.xiaohongshu\.com/explore/[a-z0-9]+") + share = compile(r"https://www\.xiaohongshu\.com/discovery/item/[a-z0-9]+") + short = compile(r"https://xhslink\.com/[A-Za-z0-9]+") def __init__( self, path="", folder="Download", - cookie=None, proxies=None, timeout=10, chunk=1024 * 1024, + **kwargs, ): - self.__update_cookie(cookie) - self.html = Html(self.headers, proxies, timeout) + self.manager = Manager(self.ROOT) + self.html = Html(self.manager.headers, proxies, timeout) self.image = Image() self.video = Video() self.explore = Explore() self.download = Download( + self.manager, self.ROOT, path, folder, - self.headers, proxies, - chunk) + chunk, + timeout) - def __get_image(self, container: dict, html: str, download, log): + def __get_image(self, container: dict, html: str, download): urls = self.image.get_image_link(html) if download: - self.download.run(urls, self.__naming_rules(container), 1, log) + self.download.run(urls, self.__naming_rules(container), 1) container["下载地址"] = urls - def __get_video(self, container: dict, html: str, download, log): + def __get_video(self, container: dict, html: str, download): url = self.video.get_video_link(html) if download: - self.download.run(url, self.__naming_rules(container), 0, log) + self.download.run(url, self.__naming_rules(container), 0) container["下载地址"] = url - def extract(self, url: str, download=False, log=None) -> dict | list[dict]: - if not self.__check(url): - return {} - html = self.html.get_html(url) + def extract(self, url: str, download=False) -> list[dict]: + urls = self.__deal_links(url) + # return urls + return [self.__deal_extract(i, download) for i in urls] + + def __deal_links(self, url: str) -> list: + urls = [] + for i in url.split(): + if u := self.short.search(i): + i = self.html.request_url(u.group(), headers=self.manager.headers, text=False) + if u := self.share.search(i): + urls.append(u.group()) + elif u := self.link.search(i): + urls.append(u.group()) + return urls + + def __deal_extract(self, url: str, download: bool): + html = self.html.request_url(url) if not html: return {} data = self.explore.run(html) if not data: return {} if data["作品类型"] == "视频": - self.__get_video(data, html, download, log) + self.__get_video(data, html, download) else: - self.__get_image(data, html, download, log) + self.__get_image(data, html, download) return data - def __check(self, url: str): - return self.links.match(url) - @staticmethod def __naming_rules(data: dict) -> str: """下载文件默认使用作品 ID 作为文件名,可修改此方法自定义文件名格式""" return data["作品ID"] - def __update_cookie(self, cookie: str) -> None: - if cookie and isinstance(cookie, str): - self.headers["Cookie"] = cookie - class XHSDownloader(App): VERSION = 1.6 @@ -109,7 +111,8 @@ class XHSDownloader(App): Binding(key="q", action="quit", description="退出程序"), ("d", "toggle_dark", "切换主题"), ] - APP = XHS(**Settings().run()) + + # APP = XHS(**Settings().run()) def compose(self) -> ComposeResult: yield Header() diff --git "a/static/\350\216\267\345\217\226Cookie\347\244\272\346\204\217\345\233\276.png" "b/static/\350\216\267\345\217\226Cookie\347\244\272\346\204\217\345\233\276.png" deleted file mode 100644 index 27f0540acd99d19bc89a31c9ac42646b013dfbd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24477 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