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 @@
| index |
-str |
-下载指定序号的图片文件,仅对图文作品生效;多个序号之间使用空格分隔;download 参数设置为 false 时不生效 |
+list[int] |
+下载指定序号的图片文件,仅对图文作品生效;download 参数设置为 false 时不生效 |
null |
@@ -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():
发布时间 作者昵称 作品标题 |
+| sec_ch_ua |
+str |
+浏览器请求头 Sec-Ch-Ua |
+内置 Chrome Sec-Ch-Ua |
+
+
+| sec_ch_ua_platform |
+str |
+浏览器请求头 Sec-Ch-Ua-Platform |
+内置 Chrome Sec-Ch-Ua-Platform |
+
+
| user_agent |
str |
-浏览器 User-Agent |
-内置 chrome user-agent |
+浏览器 User Agent |
+内置 Chrome User Agent |
| cookie |
@@ -295,6 +319,8 @@ async def example():
+其他说明:sec_ch_ua、sec_ch_ua_platform、user_agent参数获取示例,仅当程序获取数据失败时需要自行设置!
+
🌐 Cookie
- 打开浏览器(可选无痕模式启动),访问
https://www.xiaohongshu.com/explore
@@ -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