diff --git a/README.md b/README.md
index bf05a0c..5317690 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@
✅ 支持命令行下载作品文件
✅ 从浏览器读取 Cookie
✅ 自定义文件名称格式
-☑️ 支持 API 调用功能
+✅ 支持 API 调用功能
脚本功能
- ✅ 下载小红书无水印作品文件
@@ -70,14 +70,66 @@
- 运行
main.py 即可使用
🛠 命令行模式
-项目支持命令行运行模式,若想要下载图文作品的部分图片,可以使用此模式传入需要下载的图片序号!
-可以使用命令行从浏览器读取 Cookie 并写入配置文件!注意需要关闭对应浏览器才能读取数据!
-bool 类型参数支持使用 true、false、1、0、yes、no、on 或 off(不区分大小写)来设置。
+项目支持命令行运行模式,若想要下载图文作品的部分图片,可以使用此模式设置需要下载的图片序号!
+可以使用命令行从浏览器读取 Cookie 并写入配置文件!注意需要关闭浏览器才能读取数据!
命令示例:python .\main.py --browser_cookie Chrome --update_settings
+bool 类型参数支持使用 true、false、1、0、yes、no、on 或 off(不区分大小写)来设置。
+🖥 服务器模式
+启动:运行命令:python .\main.py server
+关闭:按下 Ctrl + C 关闭服务器
+请求接口:/xhs/
+请求类型:POST
+请求参数:
+
+
+
+| 参数 |
+类型 |
+含义 |
+默认值 |
+
+
+
+
+| url |
+str |
+小红书作品链接,自动提取,不支持多链接 |
+无 |
+
+
+| download |
+bool |
+是否下载作品文件;设置为 true 将会耗费更多时间 |
+false |
+
+
+| index |
+str |
+下载指定序号的图片文件,仅对图文作品生效;多个序号之间使用空格分隔;download 参数设置为 false 时不生效 |
+null |
+
+
+| skip |
+bool |
+是否跳过存在下载记录的作品;设置为 true 将不会返回存在下载记录的作品数据 |
+false |
+
+
+
+代码示例:
+
+def api_demo():
+ server = "http://127.0.0.1:8080"
+ data = {
+ "url": "https://www.xiaohongshu.com/explore/123456789",
+ }
+ response = requests.post(server, data=data)
+ print(response.json())
+
🕹 用户脚本
@@ -235,6 +287,7 @@ async def example():
🌐 Cookie
- 打开浏览器(可选无痕模式启动),访问
https://www.xiaohongshu.com/explore
+- 登录小红书账号(可跳过)
- 按下
F12 打开开发人员工具
- 选择
网络 选项卡
- 勾选
保留日志
diff --git a/locale/en_GB/LC_MESSAGES/xhs.mo b/locale/en_GB/LC_MESSAGES/xhs.mo
index 366c654..8a5b173 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 a65a1f2..83950ca 100644
--- a/locale/en_GB/LC_MESSAGES/xhs.po
+++ b/locale/en_GB/LC_MESSAGES/xhs.po
@@ -255,3 +255,18 @@ msgstr "Format of works file name"
msgid "邀请链接:"
msgstr "Invitation link: "
+
+msgid "获取小红书作品数据成功"
+msgstr "Successfully obtained data on Xiaohongshu's works"
+
+msgid "获取小红书作品数据失败"
+msgstr "Failed to obtain data on Xiaohongshu's works"
+
+msgid "Web API 服务器已启动!"
+msgstr "Web API server started!"
+
+msgid "Web API 服务器已关闭!"
+msgstr "Web API server has been shut down!"
+
+msgid "服务器主机及端口: {0}"
+msgstr "Server host and port: {0}"
diff --git a/locale/zh_CN/LC_MESSAGES/xhs.po b/locale/zh_CN/LC_MESSAGES/xhs.po
index f7de05c..4ab8049 100644
--- a/locale/zh_CN/LC_MESSAGES/xhs.po
+++ b/locale/zh_CN/LC_MESSAGES/xhs.po
@@ -255,3 +255,18 @@ msgstr ""
msgid "邀请链接:"
msgstr ""
+
+msgid "获取小红书作品数据成功"
+msgstr ""
+
+msgid "获取小红书作品数据失败"
+msgstr ""
+
+msgid "Web API 服务器已启动!"
+msgstr ""
+
+msgid "Web API 服务器已关闭!"
+msgstr ""
+
+msgid "服务器主机及端口: {0}"
+msgstr ""
diff --git a/main.py b/main.py
index c34611e..6c15b48 100644
--- a/main.py
+++ b/main.py
@@ -1,6 +1,7 @@
from asyncio import run
from sys import argv
+from source import Settings
from source import XHS
from source import XHSDownloader
from source import cli
@@ -47,13 +48,20 @@ async def example():
print(await xhs.extract(multiple_links, download, ))
-async def main():
+async def app():
async with XHSDownloader() as xhs:
await xhs.run_async()
+async def server():
+ async with XHS(**Settings().run()) as xhs:
+ await xhs.run_server()
+
+
if __name__ == '__main__':
- if len(argv) > 1:
- cli()
+ if len(argv) == 1:
+ run(app())
+ elif argv[1] == "server":
+ run(server())
else:
- run(main())
+ cli()
diff --git a/source/TUI/setting.py b/source/TUI/setting.py
index c28d324..ddff758 100644
--- a/source/TUI/setting.py
+++ b/source/TUI/setting.py
@@ -107,7 +107,7 @@ class Setting(Screen):
"language": self.query_one("#language").value,
"image_download": self.query_one("#image_download").value,
"video_download": self.query_one("#video_download").value,
- "server": False,
+ # "server": False,
})
@on(Button.Pressed, "#abandon")
diff --git a/source/__init__.py b/source/__init__.py
index 6551c0e..3818bec 100644
--- a/source/__init__.py
+++ b/source/__init__.py
@@ -1,5 +1,6 @@
from .CLI import cli
from .TUI import XHSDownloader
from .application import XHS
+from .module import Settings
-__all__ = ['XHS', 'XHSDownloader', 'cli']
+__all__ = ['XHS', 'XHSDownloader', 'cli', 'Settings', ]
diff --git a/source/application/app.py b/source/application/app.py
index 5800aa3..c512105 100644
--- a/source/application/app.py
+++ b/source/application/app.py
@@ -1,3 +1,4 @@
+from asyncio import CancelledError
from asyncio import Event
from asyncio import Queue
from asyncio import QueueEmpty
@@ -9,6 +10,7 @@ from re import compile
from typing import Callable
from urllib.parse import urlparse
+from aiohttp import web
from pyperclip import paste
from source.expansion import BrowserCookie
@@ -22,6 +24,7 @@ from source.module import (
ERROR,
WARNING,
MASTER,
+ REPOSITORY,
)
from source.module import Translate
from source.module import logging
@@ -62,7 +65,7 @@ class XHS:
video_download=True,
folder_mode=False,
language="zh_CN",
- server=False,
+ # server=False,
transition: Callable[[str], str] = None,
read_cookie: int | str = None,
*args,
@@ -85,6 +88,7 @@ class XHS:
image_download,
video_download,
folder_mode,
+ # server,
self.message,
)
self.html = Html(self.manager)
@@ -98,7 +102,8 @@ class XHS:
self.clipboard_cache: str = ""
self.queue = Queue()
self.event = Event()
- self.server = server
+ self.runner = self.init_server()
+ self.site = None
def __extract_image(self, container: dict, data: Namespace):
container["下载地址"] = self.image.get_image_link(
@@ -279,3 +284,50 @@ class XHS:
def read_browser_cookie(value: str | int) -> str:
return BrowserCookie.get(
value, domain="xiaohongshu.com") if value else ""
+
+ @staticmethod
+ async def index(request):
+ return web.HTTPFound(REPOSITORY)
+
+ async def handle(self, request):
+ data = await request.post()
+ url = data.get("url")
+ download = data.get("download", False)
+ index = data.get("index")
+ skip = data.get("skip", False)
+ url = await self.__extract_links(url, None)
+ if not url:
+ msg = self.message("提取小红书作品链接失败")
+ data = None
+ else:
+ if data := await self.__deal_extract(url[0], download, index, None, None, not skip, ):
+ msg = self.message("获取小红书作品数据成功")
+ else:
+ msg = self.message("获取小红书作品数据失败")
+ data = None
+ return web.json_response(dict(message=msg, url=url[0], data=data))
+
+ def init_server(self, ):
+ app = web.Application(debug=True)
+ app.router.add_get('/', self.index)
+ app.router.add_post('/xhs/', self.handle)
+ return web.AppRunner(app)
+
+ async def run_server(self, log=None, ):
+ try:
+ await self.start_server(log)
+ while True:
+ await sleep(3600) # 保持服务器运行
+ except (CancelledError, KeyboardInterrupt):
+ await self.close_server(log)
+
+ async def start_server(self, log=None, ):
+ await self.runner.setup()
+ self.site = web.TCPSite(self.runner, "0.0.0.0")
+ await self.site.start()
+ logging(log, self.message("Web API 服务器已启动!"))
+ logging(log, self.message("服务器主机及端口: {0}".format(self.site.name, )))
+
+ async def close_server(self, log=None, ):
+ await self.runner.cleanup()
+ logging(log, self.message("Web API 服务器已关闭!"))
diff --git a/source/application/video.py b/source/application/video.py
index 89cf042..e8367ce 100644
--- a/source/application/video.py
+++ b/source/application/video.py
@@ -13,5 +13,5 @@ class Video:
@classmethod
def get_video_link(cls, data: Namespace) -> list:
- return [Html.format_url(f"https://sns-video-hw.xhscdn.com/{t}")] if (
+ return [Html.format_url(f"https://sns-video-bd.xhscdn.com/{t}")] if (
t := data.safe_extract(".".join(cls.VIDEO_LINK))) else []
diff --git a/source/module/manager.py b/source/module/manager.py
index 6ed4cdf..94fd878 100644
--- a/source/module/manager.py
+++ b/source/module/manager.py
@@ -50,6 +50,7 @@ class Manager:
image_download: bool,
video_download: bool,
folder_mode: bool,
+ # server: bool,
transition: Callable[[str], str],
):
self.root = root
@@ -77,6 +78,7 @@ class Manager:
self.message = transition
self.image_download = self.check_bool(image_download, True)
self.video_download = self.check_bool(video_download, True)
+ # self.server = self.check_bool(server, False)
def __check_path(self, path: str) -> Path:
if not path:
diff --git a/source/module/settings.py b/source/module/settings.py
index c4a10a1..0c0580e 100644
--- a/source/module/settings.py
+++ b/source/module/settings.py
@@ -3,6 +3,8 @@ from json import load
from pathlib import Path
from platform import system
+from .static import ROOT
+
__all__ = ['Settings']
@@ -23,11 +25,11 @@ class Settings:
"video_download": True,
"folder_mode": False,
"language": "zh_CN",
- "server": False,
+ # "server": False,
}
encode = "UTF-8-SIG" if system() == "Windows" else "UTF-8"
- def __init__(self, root: Path):
+ def __init__(self, root: Path = ROOT):
self.file = root.joinpath("./settings.json")
def run(self):
diff --git a/static/XHS-Downloader.js b/static/XHS-Downloader.js
index 020f956..a1d9c63 100644
--- a/static/XHS-Downloader.js
+++ b/static/XHS-Downloader.js
@@ -1,7 +1,7 @@
// ==UserScript==
// @name XHS-Downloader
// @namespace https://github.com/JoeanAmier/XHS-Downloader
-// @version 1.4.3
+// @version 1.4.4
// @description 提取小红书作品/用户链接,下载小红书无水印图文/视频作品文件
// @author JoeanAmier
// @match http*://xhslink.com/*
@@ -42,7 +42,7 @@
2. 提取账号发布、收藏、点赞作品链接时,脚本会尝试自动滚动屏幕直至加载全部作品,滚动检测间隔:2.5 秒
3. 提取搜索结果作品、用户链接时,脚本会自动滚动屏幕以尝试加载更多内容,滚动屏幕次数:10 次
4. 可以修改滚动检测间隔、滚动屏幕次数,修改后立即生效;亦可关闭自动滚动屏幕功能,手动滚动屏幕加载内容
-5. XHS-Downloader 用户脚本仅实现可见即可得的数据采集功能,无任何破解功能
+5. XHS-Downloader 用户脚本仅实现可见即可得的数据采集功能,无任何收费功能和破解功能
项目开源地址:https://github.com/JoeanAmier/XHS-Downloader
`
@@ -132,7 +132,7 @@
const generateVideoUrl = note => {
try {
- return [`https://sns-video-hw.xhscdn.com/${note.video.consumer.originVideoKey}`];
+ return [`https://sns-video-bd.xhscdn.com/${note.video.consumer.originVideoKey}`];
} catch (error) {
console.error("Error generating video URL:", error);
return [];