diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ae933b8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.12.4-slim + +LABEL name="XHS-Downloader" version="2.1 Beta" authors="JoeanAmier" + +COPY locale /locale +COPY source /source +COPY static /static +COPY LICENSE /LICENSE +COPY main.py /main.py +COPY README.md /README.md +COPY README_EN.md /README_EN.md +COPY requirements.txt /requirements.txt + +RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt + +CMD ["python", "main.py"] diff --git a/README.md b/README.md index 3038328..b5c7c1e 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@
  • ✅ 提取搜索结果作品链接
  • ✅ 提取搜索结果用户链接
  • +

    ⭐ XHS-Downloader 开发计划及进度可前往 Projects 查阅

    📸 程序截图

    🎥 点击图片观看演示视频

    @@ -83,7 +84,8 @@

    启动:运行命令:python .\main.py server

    关闭:按下 Ctrl + C 关闭服务器

    请求接口:/xhs/

    -

    请求类型:POST

    +

    请求方法:POST

    +

    请求格式:JSON

    请求参数:

    @@ -109,8 +111,8 @@ - - + + @@ -124,11 +126,17 @@

    代码示例:

     def api_demo():
    -    server = "http://127.0.0.1:8080"
    +    server = "http://127.0.0.1:8000/xhs/"
         data = {
             "url": "https://www.xiaohongshu.com/explore/123456789",
    +        "download": True,
    +        "index": [
    +            3,
    +            6,
    +            9,
    +        ],
         }
    -    response = requests.post(server, data=data)
    +    response = requests.post(server, json=data)
         print(response.json())
     

    🕹 用户脚本

    @@ -152,6 +160,8 @@ async def example(): work_path = "D:\\" # 作品数据/文件保存根路径,默认值:项目根路径 folder_name = "Download" # 作品文件储存文件夹名称(自动创建),默认值:Download name_format = "作品标题 作品描述" + sec_ch_ua = "" # 请求头 Sec-Ch-Ua + sec_ch_ua_platform = "" # 请求头 Sec-Ch-Ua-Platform user_agent = "" # User-Agent cookie = "" # 小红书网页版 Cookie,无需登录,必需参数,登录状态对数据采集有影响 proxy = None # 网络代理 @@ -166,6 +176,8 @@ async def example(): async with XHS(work_path=work_path, folder_name=folder_name, name_format=name_format, + sec_ch_ua=sec_ch_ua, + sec_ch_ua_platform=sec_ch_ua_platform, user_agent=user_agent, cookie=cookie, proxy=proxy, @@ -216,10 +228,22 @@ async def example(): + + + + + + + + + + + + - - + + @@ -295,6 +319,8 @@ async def example():
    indexstr下载指定序号的图片文件,仅对图文作品生效;多个序号之间使用空格分隔;download 参数设置为 false 时不生效list[int]下载指定序号的图片文件,仅对图文作品生效;download 参数设置为 false 时不生效 null
    发布时间 作者昵称 作品标题
    sec_ch_uastr浏览器请求头 Sec-Ch-Ua内置 Chrome Sec-Ch-Ua
    sec_ch_ua_platformstr浏览器请求头 Sec-Ch-Ua-Platform内置 Chrome Sec-Ch-Ua-Platform
    user_agent str浏览器 User-Agent内置 chrome user-agent浏览器 User Agent内置 Chrome User Agent
    cookie
    +

    其他说明:sec_ch_uasec_ch_ua_platformuser_agent参数获取示例,仅当程序获取数据失败时需要自行设置!

    +

    🌐 Cookie

    1. 打开浏览器(可选无痕模式启动),访问 https://www.xiaohongshu.com/explore
    2. @@ -361,6 +387,7 @@ async def example(): * https://github.com/encode/httpx/ * https://github.com/tiangolo/fastapi +* https://github.com/textualize/textual/ * https://textual.textualize.io/ * https://aiosqlite.omnilib.dev/en/stable/ * https://click.palletsprojects.com/en/8.1.x/ diff --git a/locale/en_GB/LC_MESSAGES/xhs.mo b/locale/en_GB/LC_MESSAGES/xhs.mo index 398ddee..7781957 100644 Binary files a/locale/en_GB/LC_MESSAGES/xhs.mo and b/locale/en_GB/LC_MESSAGES/xhs.mo differ diff --git a/locale/en_GB/LC_MESSAGES/xhs.po b/locale/en_GB/LC_MESSAGES/xhs.po index 2b9a049..669a1fb 100644 --- a/locale/en_GB/LC_MESSAGES/xhs.po +++ b/locale/en_GB/LC_MESSAGES/xhs.po @@ -271,7 +271,13 @@ msgstr "Web API server has been shut down!" msgid "服务器主机及端口: {0}" msgstr "Server host and port: {0}" -msgid "内置 Chrome User-Agent" +msgid "内置 Chrome Sec-Ch-Ua" +msgstr "Built in Chrome Sec-Ch-Ua" + +msgid "内置 Chrome Sec-Ch-Ua-Platform" +msgstr "Built in Chrome Sec-Ch-Ua-Platform" + +msgid "内置 Chrome User Agent" msgstr "Built in Chrome User Agent" msgid "proxy 参数 {0} 设置错误,程序将不会使用代理" diff --git a/locale/zh_CN/LC_MESSAGES/xhs.po b/locale/zh_CN/LC_MESSAGES/xhs.po index c3f4b28..c46a963 100644 --- a/locale/zh_CN/LC_MESSAGES/xhs.po +++ b/locale/zh_CN/LC_MESSAGES/xhs.po @@ -271,7 +271,13 @@ msgstr "" msgid "服务器主机及端口: {0}" msgstr "" -msgid "内置 Chrome User-Agent" +msgid "内置 Chrome Sec-Ch-Ua" +msgstr "" + +msgid "内置 Chrome Sec-Ch-Ua-Platform" +msgstr "" + +msgid "内置 Chrome User Agent" msgstr "" msgid "proxy 参数 {0} 设置错误,程序将不会使用代理" diff --git a/main.py b/main.py index ae60a5f..f679f82 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,6 @@ from asyncio import run +from asyncio.exceptions import CancelledError +from contextlib import suppress from sys import argv from source import Settings @@ -17,6 +19,8 @@ async def example(): work_path = "D:\\" # 作品数据/文件保存根路径,默认值:项目根路径 folder_name = "Download" # 作品文件储存文件夹名称(自动创建),默认值:Download name_format = "作品标题 作品描述" + sec_ch_ua = "" # 请求头 Sec-Ch-Ua + sec_ch_ua_platform = "" # 请求头 Sec-Ch-Ua-Platform user_agent = "" # User-Agent cookie = "" # 小红书网页版 Cookie,无需登录,必需参数,登录状态对数据采集有影响 proxy = None # 网络代理 @@ -31,6 +35,8 @@ async def example(): async with XHS(work_path=work_path, folder_name=folder_name, name_format=name_format, + sec_ch_ua=sec_ch_ua, + sec_ch_ua_platform=sec_ch_ua_platform, user_agent=user_agent, cookie=cookie, proxy=proxy, @@ -55,16 +61,19 @@ async def app(): await xhs.run_async() -async def server(): +async def server(host="127.0.0.1", port=8000, log_level="info", ): async with XHS(**Settings().run()) as xhs: - await xhs.run_server() + await xhs.run_server(host, port, log_level, ) if __name__ == '__main__': - if len(argv) == 1: - run(app()) - elif argv[1] == "server": - print("该模式重构中!") - # run(server()) - else: - cli() + with suppress( + KeyboardInterrupt, + CancelledError, + ): + if len(argv) == 1: + run(app()) + elif argv[1] == "server": + run(server()) + else: + cli() diff --git a/requirements.txt b/requirements.txt index 25b8099..b3fcf1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ -textual>=0.47.1 -pyperclip>=1.8.2 -lxml>=5.1.0 +textual>=0.70.0 +pyperclip>=1.9.0 +lxml>=5.2.2 PyYAML>=6.0.1 aiosqlite>=0.20.0 click>=8.1.7 browser_cookie3>=0.19.1 httpx>=0.27.0 -fastapi>=0.110.0 -uvicorn>=0.24.0 +fastapi>=0.111.0 +uvicorn>=0.30.1 diff --git a/source/CLI/main.py b/source/CLI/main.py index 4bb5e5c..55566b2 100644 --- a/source/CLI/main.py +++ b/source/CLI/main.py @@ -123,6 +123,9 @@ class CLI: ("--work_path", "-wp", "str", _("作品数据 / 文件保存根路径")), ("--folder_name", "-fn", "str", _("作品文件储存文件夹名称")), ("--name_format", "-nf", "str", _("作品文件名称格式")), + ("--sec_ch_ua", "-su", "str", _("Sec-Ch-Ua")), + ("--sec_ch_ua_platform", "-sp", "str", _("User-Agent")), + ("--user_agent", "-ua", "str", _("Sec-Ch-Ua-Platform")), ("--cookie", "-ck", "str", _("小红书网页版 Cookie,无需登录")), ("--proxy", "-p", "str", _("网络代理")), ("--timeout", "-t", "int", _("请求数据超时限制,单位:秒")), @@ -163,6 +166,9 @@ class CLI: ) @option("--folder_name", "-fn", ) @option("--name_format", "-nf", ) +@option("--sec_ch_ua", "-su", ) +@option("--sec_ch_ua_platform", "-sp", ) +@option("--user_agent", "-ua", ) @option("--cookie", "-ck", ) @option("--proxy", "-p", ) @option("--timeout", "-t", type=int, ) diff --git a/source/TUI/about.py b/source/TUI/about.py index 38a6457..9b7b66b 100644 --- a/source/TUI/about.py +++ b/source/TUI/about.py @@ -21,15 +21,15 @@ __all__ = ["About"] class About(Screen): BINDINGS = [ Binding( - key="q", + key="Q", action="quit", description="退出程序/Quit"), Binding( - key="u", - action="check_update_about", + key="U", + action="check_update", description="检查更新/Update"), Binding( - key="b", + key="B", action="index", description="返回首页/Back"), ] @@ -53,3 +53,12 @@ class About(Screen): def on_mount(self) -> None: self.title = PROJECT + + async def action_quit(self) -> None: + await self.app.action_quit() + + async def action_index(self): + await self.app.push_screen("index") + + async def action_check_update(self): + await self.app.run_action("update_and_return") diff --git a/source/TUI/app.py b/source/TUI/app.py index 6de0422..d2c4315 100644 --- a/source/TUI/app.py +++ b/source/TUI/app.py @@ -14,7 +14,6 @@ from source.module import logging from .about import About from .index import Index from .loading import Loading -from .monitor import Monitor from .record import Record from .setting import Setting from .update import Update @@ -78,15 +77,6 @@ class XHSDownloader(App): await self.push_screen("setting", save_settings) - async def action_about(self): - await self.push_screen("about") - - async def action_index(self): - await self.push_screen("index") - - async def action_record(self): - await self.push_screen("record") - async def refresh_screen(self): self.pop_screen() await self.close_database() @@ -117,13 +107,10 @@ class XHSDownloader(App): async def action_check_update(self): await self.push_screen(Update(self.APP, self.message), callback=self.update_result) - async def action_check_update_about(self): + async def action_update_and_return(self): await self.push_screen("index") await self.action_check_update() - async def action_monitor(self): - await self.push_screen(Monitor(self.APP, self.message)) - async def close_database(self): await self.APP.id_recorder.cursor.close() await self.APP.id_recorder.database.close() diff --git a/source/TUI/index.py b/source/TUI/index.py index 1168bcf..aab15ce 100644 --- a/source/TUI/index.py +++ b/source/TUI/index.py @@ -27,18 +27,19 @@ from source.module import ( REPOSITORY, GENERAL, ) +from .monitor import Monitor __all__ = ["Index"] class Index(Screen): BINDINGS = [ - Binding(key="q", action="quit", description="退出程序/Quit"), - Binding(key="u", action="check_update", description="检查更新/Update"), - Binding(key="s", action="settings", description="程序设置/Settings"), - Binding(key="r", action="record", description="下载记录/Record"), - Binding(key="m", action="monitor", description="开启监听/Monitor"), - Binding(key="a", action="about", description="关于项目/About"), + Binding(key="Q", action="quit", description="退出程序/Quit"), + Binding(key="U", action="update", description="检查更新/Update"), + Binding(key="S", action="settings", description="程序设置/Settings"), + Binding(key="R", action="record", description="下载记录/Record"), + Binding(key="M", action="monitor", description="开启监听/Monitor"), + Binding(key="A", action="about", description="关于项目/About"), ] def __init__(self, app: XHS, message: Callable[[str], str]): @@ -73,7 +74,7 @@ class Index(Screen): Button(self.message("清空输入框"), id="reset"), ), ) - yield RichLog(markup=True, wrap=True) + yield RichLog(markup=True, ) yield Footer() def on_mount(self) -> None: @@ -86,7 +87,8 @@ class Index(Screen): f"\n{ ">" * 50}", - style=MASTER), scroll_end=False) + style=MASTER), scroll_end=False, + ) self.xhs.manager.print_proxy_tip(log=self.tip, ) @on(Button.Pressed, "#deal") @@ -114,3 +116,21 @@ class Index(Screen): self.tip.write(Text(self.message("下载小红书作品文件失败"), style=ERROR)) self.tip.write(Text(">" * 50, style=GENERAL)) self.app.pop_screen() + + async def action_quit(self) -> None: + await self.app.action_quit() + + async def action_update(self) -> None: + await self.app.run_action("check_update") + + async def action_settings(self): + await self.app.run_action("settings") + + async def action_monitor(self): + await self.app.push_screen(Monitor(self.xhs, self.message)) + + async def action_about(self): + await self.app.push_screen("about") + + async def action_record(self): + await self.app.push_screen("record") diff --git a/source/TUI/monitor.py b/source/TUI/monitor.py index 14bd698..5356b12 100644 --- a/source/TUI/monitor.py +++ b/source/TUI/monitor.py @@ -24,8 +24,8 @@ __all__ = ["Monitor"] class Monitor(Screen): BINDINGS = [ - Binding(key="q", action="quit", description="退出程序/Quit"), - Binding(key="c", action="close", description="关闭监听/Close"), + Binding(key="Q", action="quit", description="退出程序/Quit"), + Binding(key="C", action="close", description="关闭监听/Close"), ] def __init__(self, app: XHS, message: Callable[[str], str]): @@ -60,3 +60,7 @@ class Monitor(Screen): def action_close(self): self.xhs.stop_monitor() self.app.pop_screen() + + async def action_quit(self) -> None: + self.action_close() + await self.app.action_quit() diff --git a/source/TUI/record.py b/source/TUI/record.py index 86002c5..0db7f08 100644 --- a/source/TUI/record.py +++ b/source/TUI/record.py @@ -15,7 +15,6 @@ __all__ = ["Record"] class Record(ModalScreen): - def __init__(self, app: XHS, message: Callable[[str], str]): super().__init__() self.xhs = app diff --git a/source/TUI/setting.py b/source/TUI/setting.py index f95e451..1e1da44 100644 --- a/source/TUI/setting.py +++ b/source/TUI/setting.py @@ -19,8 +19,8 @@ __all__ = ["Setting"] class Setting(Screen): BINDINGS = [ - Binding(key="q", action="quit", description="退出程序/Quit"), - Binding(key="b", action="index", description="返回首页/Back"), + Binding(key="Q", action="quit", description="退出程序/Quit"), + Binding(key="B", action="index", description="返回首页/Back"), ] def __init__(self, data: dict, message: Callable[[str], str]): @@ -39,8 +39,15 @@ class Setting(Screen): Label(self.message("作品文件名称格式"), classes="params", ), Input(self.data["name_format"], placeholder=self.message("发布时间 作者昵称 作品标题"), valid_empty=True, id="name_format", ), + Label(self.message("Sec-Ch-Ua"), classes="params", ), + Input(self.data["sec_ch_ua"], placeholder=self.message("内置 Chrome Sec-Ch-Ua"), valid_empty=True, + id="sec_ch_ua", ), + Label(self.message("Sec-Ch-Ua-Platform"), classes="params", ), + Input(self.data["sec_ch_ua_platform"], placeholder=self.message("内置 Chrome Sec-Ch-Ua-Platform"), + valid_empty=True, + id="sec_ch_ua_platform", ), Label(self.message("User-Agent"), classes="params", ), - Input(self.data["user_agent"], placeholder=self.message("内置 Chrome User-Agent"), valid_empty=True, + Input(self.data["user_agent"], placeholder=self.message("内置 Chrome User Agent"), valid_empty=True, id="user_agent", ), Label(self.message("小红书网页版 Cookie"), classes="params", ), Input(placeholder=self.__check_cookie(), valid_empty=True, id="cookie", ), @@ -101,6 +108,8 @@ class Setting(Screen): "work_path": self.query_one("#work_path").value, "folder_name": self.query_one("#folder_name").value, "name_format": self.query_one("#name_format").value, + "sec_ch_ua": self.query_one("#sec_ch_ua").value, + "sec_ch_ua_platform": self.query_one("#sec_ch_ua_platform").value, "user_agent": self.query_one("#user_agent").value, "cookie": self.query_one("#cookie").value or self.data["cookie"], "proxy": self.query_one("#proxy").value or None, @@ -120,3 +129,9 @@ class Setting(Screen): @on(Button.Pressed, "#abandon") def reset(self): self.dismiss(self.data) + + async def action_quit(self) -> None: + await self.app.action_quit() + + async def action_index(self): + await self.app.push_screen("index") diff --git a/source/application/app.py b/source/application/app.py index 55256c7..f8f5995 100644 --- a/source/application/app.py +++ b/source/application/app.py @@ -1,4 +1,3 @@ -# from asyncio import CancelledError from asyncio import Event from asyncio import Queue from asyncio import QueueEmpty @@ -10,13 +9,19 @@ from re import compile from typing import Callable from urllib.parse import urlparse +from fastapi import FastAPI +from fastapi.responses import RedirectResponse # from aiohttp import web from pyperclip import paste +from uvicorn import Config +from uvicorn import Server from source.expansion import BrowserCookie from source.expansion import Converter from source.expansion import Namespace from source.module import DataRecorder +from source.module import ExtractData +from source.module import ExtractParams from source.module import IDRecorder from source.module import Manager from source.module import ( @@ -24,7 +29,9 @@ from source.module import ( ERROR, WARNING, MASTER, - # REPOSITORY, + REPOSITORY, + VERSION_MAJOR, + VERSION_MINOR, ) from source.module import Translate from source.module import logging @@ -53,6 +60,8 @@ class XHS: work_path="", folder_name="Download", name_format="发布时间 作者昵称 作品标题", + sec_ch_ua: str = "", + sec_ch_ua_platform: str = "", user_agent: str = None, cookie: str = None, proxy: str | dict = None, @@ -80,6 +89,8 @@ class XHS: folder_name, name_format, chunk, + sec_ch_ua, + sec_ch_ua_platform, user_agent, self.read_browser_cookie(read_cookie) or cookie, proxy, @@ -108,6 +119,7 @@ class XHS: self.event = Event() # self.runner = self.init_server() # self.site = None + self.server = None def __extract_image(self, container: dict, data: Namespace): container["下载地址"], container["动图地址"] = self.image.get_image_link( @@ -339,3 +351,46 @@ class XHS: # async def close_server(self, log=None, ): # await self.runner.cleanup() # logging(log, self.message("Web API 服务器已关闭!")) + + async def run_server(self, host="127.0.0.1", port=8000, log_level="info", ): + self.server = FastAPI( + title="XHS-Downloader", + version=f"{VERSION_MAJOR}.{VERSION_MINOR}") + self.setup_routes() + config = Config( + self.server, + host=host, + port=port, + log_level=log_level, + ) + server = Server(config) + await server.serve() + + def setup_routes(self): + @self.server.get("/") + async def index(): + return RedirectResponse(url=REPOSITORY) + + @self.server.post("/xhs/", response_model=ExtractData, ) + async def handle(extract: ExtractParams): + url = await self.__extract_links(extract.url, None) + if not url: + msg = self.message("提取小红书作品链接失败") + data = None + else: + if data := await self.__deal_extract( + url[0], + extract.download, + extract.index, + None, + None, + not extract.skip, + ): + msg = self.message("获取小红书作品数据成功") + else: + msg = self.message("获取小红书作品数据失败") + data = None + return ExtractData( + message=msg, + url=url[0] if url else extract.url, + data=data) diff --git a/source/module/__init__.py b/source/module/__init__.py index 74793c6..db29e74 100644 --- a/source/module/__init__.py +++ b/source/module/__init__.py @@ -1,5 +1,9 @@ from .extend import Account from .manager import Manager +from .model import ( + ExtractData, + ExtractParams, +) from .recorder import DataRecorder from .recorder import IDRecorder from .settings import Settings @@ -22,38 +26,11 @@ from .static import ( HEADERS, PROJECT, USERAGENT, + SEC_CH_UA, + SEC_CH_UA_PLATFORM, ) from .tools import ( retry, logging, ) from .translator import Translate - -__all__ = [ - "Account", - "Settings", - "IDRecorder", - "Manager", - "VERSION_MAJOR", - "VERSION_MINOR", - "VERSION_BETA", - "ROOT", - "REPOSITORY", - "LICENCE", - "RELEASES", - "MASTER", - "PROMPT", - "GENERAL", - "PROGRESS", - "ERROR", - "WARNING", - "INFO", - "USERSCRIPT", - "HEADERS", - "retry", - "logging", - "PROJECT", - "Translate", - "DataRecorder", - "USERAGENT", -] diff --git a/source/module/manager.py b/source/module/manager.py index 80b4ecd..b1816c4 100644 --- a/source/module/manager.py +++ b/source/module/manager.py @@ -11,6 +11,8 @@ from httpx import TimeoutException from httpx import get from .static import HEADERS +from .static import SEC_CH_UA +from .static import SEC_CH_UA_PLATFORM from .static import USERAGENT from .static import WARNING from .tools import logging @@ -48,6 +50,8 @@ class Manager: folder: str, name_format: str, chunk: int, + sec_ch_ua: str, + sec_ch_ua_platform: str, user_agent: str, cookie: str, proxy: str | dict, @@ -68,7 +72,11 @@ class Manager: self.path = self.__check_path(path) self.folder = self.__check_folder(folder) self.message = transition - self.blank_headers = HEADERS | {"User-Agent": user_agent or USERAGENT} + self.blank_headers = HEADERS | { + "User-Agent": user_agent or USERAGENT, + "Sec-Ch-Ua": sec_ch_ua or SEC_CH_UA, + "Sec-Ch-Ua-Platform": sec_ch_ua_platform or SEC_CH_UA_PLATFORM, + } self.headers = self.blank_headers | {"Cookie": cookie} self.retry = retry self.chunk = chunk @@ -83,11 +91,13 @@ class Manager: headers=self.headers | { "Referer": "https://www.xiaohongshu.com/explore", }, timeout=timeout, + verify=False, **self.proxy, ) self.download_client = AsyncClient( headers=self.blank_headers, timeout=timeout, + verify=False, **self.proxy, ) self.image_download = self.check_bool(image_download, True) diff --git a/source/module/model.py b/source/module/model.py new file mode 100644 index 0000000..c404990 --- /dev/null +++ b/source/module/model.py @@ -0,0 +1,14 @@ +from pydantic import BaseModel + + +class ExtractParams(BaseModel): + url: str + download: bool = False + index: list = None + skip: bool = False + + +class ExtractData(BaseModel): + message: str + url: str + data: dict | None diff --git a/source/module/settings.py b/source/module/settings.py index 34cb5f0..0f20d3e 100644 --- a/source/module/settings.py +++ b/source/module/settings.py @@ -4,6 +4,8 @@ from pathlib import Path from platform import system from .static import ROOT +from .static import SEC_CH_UA +from .static import SEC_CH_UA_PLATFORM from .static import USERAGENT __all__ = ['Settings'] @@ -14,6 +16,8 @@ class Settings: "work_path": "", "folder_name": "Download", "name_format": "发布时间 作者昵称 作品标题", + "sec_ch_ua": SEC_CH_UA, + "sec_ch_ua_platform": SEC_CH_UA_PLATFORM, "user_agent": USERAGENT, "cookie": "", "proxy": None, diff --git a/source/module/static.py b/source/module/static.py index 8b473c0..c2d0903 100644 --- a/source/module/static.py +++ b/source/module/static.py @@ -19,6 +19,8 @@ __all__ = [ "HEADERS", "PROJECT", "USERAGENT", + "SEC_CH_UA", + "SEC_CH_UA_PLATFORM", ] VERSION_MAJOR = 2 @@ -37,6 +39,8 @@ USERSCRIPT = "https://raw.githubusercontent.com/JoeanAmier/XHS-Downloader/master USERAGENT = ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 " "Safari/537.36") +SEC_CH_UA = "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"" +SEC_CH_UA_PLATFORM = "\"Windows\"" HEADERS = { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8," @@ -46,9 +50,9 @@ HEADERS = { "Cookie": "", "Dnt": "1", # "Priority": "u=0, i", - # "Sec-Ch-Ua": "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"", + "Sec-Ch-Ua": SEC_CH_UA, "Sec-Ch-Ua-Mobile": "?0", - # "Sec-Ch-Ua-Platform": "\"Windows\"", + "Sec-Ch-Ua-Platform": SEC_CH_UA_PLATFORM, "Sec-Fetch-Dest": "document", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-Site": "none", diff --git a/static/screenshot/脚本安装教程.png b/static/screenshot/脚本安装教程.png new file mode 100644 index 0000000..495a9c0 Binary files /dev/null and b/static/screenshot/脚本安装教程.png differ diff --git a/static/screenshot/请求头示例图.png b/static/screenshot/请求头示例图.png new file mode 100644 index 0000000..9ac93e8 Binary files /dev/null and b/static/screenshot/请求头示例图.png differ