feat: 发布 2.2 版本
1. 移除 sec_ch_ua_platform 参数 2. 移除 sec_ch_ua 参数 3. 优化请求延时间隔 4. 优化并发下载功能 5. 修正英语翻译错误 6. 新增并发下载限制 7. 修正命令行模式错误 8. 简化数据请求头 Closes #86 Closes #87 Closes #93 Closes #98 Closes #105 Closes #109 Closes #110 Closes #140 Closes #152 Closes #154 Closes #157 Closes #159 Closes #160 Closes #162 Closes #164 Closes #165
@ -1,6 +1,6 @@
|
||||
FROM python:3.12.4-slim
|
||||
|
||||
LABEL name="XHS-Downloader" version="2.2 Beta" authors="JoeanAmier"
|
||||
LABEL name="XHS-Downloader" version="2.2" authors="JoeanAmier"
|
||||
|
||||
COPY locale /locale
|
||||
COPY source /source
|
||||
|
||||
55
README.md
@ -55,6 +55,7 @@
|
||||
<h1>🔗 支持链接</h1>
|
||||
<ul>
|
||||
<li><code>https://www.xiaohongshu.com/explore/作品ID</code></li>
|
||||
<li><code>https://www.xiaohongshu.com/explore/作品ID?xsec_token=XXX</code></li>
|
||||
<li><code>https://www.xiaohongshu.com/discovery/item/作品ID</code></li>
|
||||
<li><code>https://xhslink.com/分享码</code></li>
|
||||
<br/>
|
||||
@ -64,7 +65,7 @@
|
||||
<p>⭐ 推荐使用 <a href="https://learn.microsoft.com/zh-cn/windows/terminal/install">Windows 终端</a> (Windows 11 默认终端)运行程序以便获得最佳显示效果!</p>
|
||||
<h1>🥣 使用方法</h1>
|
||||
<p>如果仅需下载无水印作品文件,建议选择 <b>程序运行</b> 或 <b>Docker 运行</b>;如果有其他需求,建议选择 <b>源码运行</b>!</p>
|
||||
<p>建议自行设置 <code>cookie</code> 参数,若不设置该参数,程序功能可能无法正常使用!</p>
|
||||
<p><del>建议自行设置 <code>cookie</code> 参数,若不设置该参数,程序功能可能无法正常使用!</del></p>
|
||||
<h2>🖱 程序运行</h2>
|
||||
<p>Mac OS、Windows 10 及以上用户可前往 <a href="https://github.com/JoeanAmier/XHS-Downloader/releases/latest">Releases</a> 下载程序压缩包,解压后打开程序文件夹,双击运行 <code>main</code> 即可使用。</p>
|
||||
<p><strong>注意:Mac OS 平台可执行文件 <code>main</code> 可能需要从终端命令行启动;受设备限制,Mac OS 平台可执行文件尚未经过测试,无法保证可用性!</strong></p>
|
||||
@ -93,7 +94,7 @@
|
||||
</ol>
|
||||
<h1>🛠 命令行模式</h1>
|
||||
<p>项目支持命令行运行模式,若想要下载图文作品的部分图片,可以使用此模式设置需要下载的图片序号!</p>
|
||||
<p>可以使用命令行<b>从浏览器读取 Cookie 并写入配置文件</b>!注意需要关闭浏览器才能读取数据!</p>
|
||||
<p>可以使用命令行 <b>从浏览器读取 Cookie 并写入配置文件!</b></p>
|
||||
<p>命令示例:<code>python .\main.py --browser_cookie Chrome --update_settings</code></p>
|
||||
<p><code>bool</code> 类型参数支持使用 <code>true</code>、<code>false</code>、<code>1</code>、<code>0</code>、<code>yes</code>、<code>no</code>、<code>on</code> 或 <code>off</code>(不区分大小写)来设置。</p>
|
||||
<hr>
|
||||
@ -180,10 +181,10 @@ 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
|
||||
# sec_ch_ua = "" # 请求头 Sec-Ch-Ua
|
||||
# sec_ch_ua_platform = "" # 请求头 Sec-Ch-Ua-Platform
|
||||
user_agent = "" # User-Agent
|
||||
cookie = "" # 小红书网页版 Cookie,无需登录,必需参数,登录状态对数据采集有影响
|
||||
cookie = "" # 小红书网页版 Cookie,无需登录,可选参数,登录状态对数据采集有影响
|
||||
proxy = None # 网络代理
|
||||
timeout = 5 # 请求数据超时限制,单位:秒,默认值:10
|
||||
chunk = 1024 * 1024 * 10 # 下载文件时,每次从服务器获取的数据块大小,单位:字节
|
||||
@ -193,26 +194,27 @@ async def example():
|
||||
folder_mode = False # 是否将每个作品的文件储存至单独的文件夹
|
||||
# async with XHS() as xhs:
|
||||
# pass # 使用默认参数
|
||||
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,
|
||||
timeout=timeout,
|
||||
chunk=chunk,
|
||||
max_retry=max_retry,
|
||||
record_data=record_data,
|
||||
image_format=image_format,
|
||||
folder_mode=folder_mode,
|
||||
) as xhs: # 使用自定义参数
|
||||
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,
|
||||
timeout=timeout,
|
||||
chunk=chunk,
|
||||
max_retry=max_retry,
|
||||
record_data=record_data,
|
||||
image_format=image_format,
|
||||
folder_mode=folder_mode,
|
||||
) as xhs: # 使用自定义参数
|
||||
download = True # 是否下载作品文件,默认值:False
|
||||
# 返回作品详细信息,包括下载地址
|
||||
# 获取数据失败时返回空字典
|
||||
print(await xhs.extract(error_link, download, ))
|
||||
print(await xhs.extract(demo_link, download, ))
|
||||
print(await xhs.extract(demo_link, download, index=[1, 2]))
|
||||
# 支持传入多个作品链接
|
||||
print(await xhs.extract(multiple_links, download, ))
|
||||
</pre>
|
||||
@ -254,13 +256,13 @@ async def example():
|
||||
<td align="center"><code>发布时间 作者昵称 作品标题</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">sec_ch_ua</td>
|
||||
<td align="center"><del>sec_ch_ua</del>(已废弃)</td>
|
||||
<td align="center">str</td>
|
||||
<td align="center">浏览器请求头 Sec-Ch-Ua</td>
|
||||
<td align="center">内置 Chrome Sec-Ch-Ua</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">sec_ch_ua_platform</td>
|
||||
<td align="center"><del>sec_ch_ua_platform</del>(已废弃)</td>
|
||||
<td align="center">str</td>
|
||||
<td align="center">浏览器请求头 Sec-Ch-Ua-Platform</td>
|
||||
<td align="center">内置 Chrome Sec-Ch-Ua-Platform</td>
|
||||
@ -274,12 +276,12 @@ async def example():
|
||||
<tr>
|
||||
<td align="center">cookie</td>
|
||||
<td align="center">str</td>
|
||||
<td align="center">小红书网页版 Cookie,<b>无需登录</b></td>
|
||||
<td align="center">小红书网页版 Cookie,<b>无需登录,非必需参数!</b></td>
|
||||
<td align="center">无</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">proxy</td>
|
||||
<td align="center">str|dict</td>
|
||||
<td align="center">str | dict</td>
|
||||
<td align="center">设置程序代理</td>
|
||||
<td align="center">null</td>
|
||||
</tr>
|
||||
@ -351,9 +353,10 @@ async def example():
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><b>其他说明:<code>sec_ch_ua</code>、<code>sec_ch_ua_platform</code>、<code>user_agent</code>参数获取示例,仅当程序获取数据失败时需要自行设置!</b></p>
|
||||
<p><b>其他说明:<code>user_agent</code>参数获取示例;强烈建议根据实际浏览器信息进行设置!</b></p>
|
||||
<img src="static/screenshot/请求头示例图.png" alt="">
|
||||
<h1>🌐 Cookie</h1>
|
||||
<p><code>2.2</code> 版本开始,项目功能无异常的情况下,无需额外处理 Cookie!</p>
|
||||
<ol>
|
||||
<li>打开浏览器(可选无痕模式启动),访问 <code>https://www.xiaohongshu.com/explore</code></li>
|
||||
<li>登录小红书账号(可跳过)</li>
|
||||
|
||||
90
README_EN.md
@ -18,30 +18,31 @@
|
||||
<p>⭐ Due to the author's limited energy, I was unable to update the English document in a timely manner, and the content may have become outdated, partial translation is machine translation, the translation result may be incorrect, Suggest referring to Chinese documentation. If you want to contribute to translation, we warmly welcome you.</p>
|
||||
<h1>📑 Project Features</h1>
|
||||
<ul><b>Program Features</b>
|
||||
<li>✅ Collect Xiaohongshu content information</li>
|
||||
<li>✅ Extract Xiaohongshu content download addresses</li>
|
||||
<li>✅ Download Xiaohongshu watermark-free content files</li>
|
||||
<li>✅ Collect Xiaohongshu works information</li>
|
||||
<li>✅ Extract Xiaohongshu works download addresses</li>
|
||||
<li>✅ Download Xiaohongshu watermark-free works files</li>
|
||||
<li>✅ Download Xiaohongshu livePhoto files (non-watermark-free)</li>
|
||||
<li>✅ Automatically skip already downloaded content files</li>
|
||||
<li>✅ Content file integrity handling mechanism</li>
|
||||
<li>✅ Customizable text and image content file download format</li>
|
||||
<li>✅ Persistently store content information to files</li>
|
||||
<li>✅ Store content files to a separate folder</li>
|
||||
<li>✅ Background clipboard monitoring for content download</li>
|
||||
<li>✅ Record downloaded content IDs</li>
|
||||
<li>✅ Support command line for downloading content files</li>
|
||||
<li>✅ Automatically skip already downloaded works files</li>
|
||||
<li>✅ works file integrity handling mechanism</li>
|
||||
<li>✅ Customizable image works file download format</li>
|
||||
<li>✅ Persistently store works information to files</li>
|
||||
<li>✅ Store works files to a separate folder</li>
|
||||
<li>✅ Background clipboard monitoring for works download</li>
|
||||
<li>✅ Record downloaded works IDs</li>
|
||||
<li>✅ Support command line for downloading works files</li>
|
||||
<li>✅ Read cookies from browser</li>
|
||||
<li>✅ Customizable file name format</li>
|
||||
<li>✅ Support API call functionality</li>
|
||||
<li>✅ Support file breakpoint resume download</li>
|
||||
</ul>
|
||||
<ul><b>Script Features</b>
|
||||
<li>✅ Download Xiaohongshu watermark-free content files</li>
|
||||
<li>✅ Extract discovery page content links</li>
|
||||
<li>✅ Extract account-published content links</li>
|
||||
<li>✅ Extract account-favorited content links</li>
|
||||
<li>✅ Extract account-liked content links</li>
|
||||
<li>✅ Extract search result content links</li>
|
||||
<li>✅ Download Xiaohongshu watermark-free works files</li>
|
||||
<li>✅ Extract discovery page works links</li>
|
||||
<li>✅ Extract account-published works links</li>
|
||||
<li>✅ Extract account-favorited works links</li>
|
||||
<li>✅ Extract account-liked works links</li>
|
||||
<li>✅ Extract account-board works links</li>
|
||||
<li>✅ Extract search result works links</li>
|
||||
<li>✅ Extract search result user links</li>
|
||||
</ul>
|
||||
<p>⭐ The development plan and progress of XHS-Downloader can be found at <a href="https://github.com/users/JoeanAmier/projects/5">Projects</a></p>
|
||||
@ -55,6 +56,7 @@
|
||||
<h1>🔗 Supported Links</h1>
|
||||
<ul>
|
||||
<li><code>https://www.xiaohongshu.com/explore/WorksID</code></li>
|
||||
<li><code>https://www.xiaohongshu.com/explore/WorksID?xsec_token=XXX</code></li>
|
||||
<li><code>https://www.xiaohongshu.com/discovery/item/WorksID</code></li>
|
||||
<li><code>https://xhslink.com/ShareCode</code></li>
|
||||
<br/>
|
||||
@ -64,7 +66,7 @@
|
||||
<p>⭐ It is recommended to use the <a href="https://learn.microsoft.com/en-us/windows/terminal/install">Windows Terminal</a> (default terminal for Windows 11) to run the program for the best display effect!</p>
|
||||
<h1>🥣 Usage</h1>
|
||||
<p>If you only need to download watermark-free content files, it is recommended to choose <b>Program Run</b>; if you have other needs, it is recommended to choose <b>Source Code Run</b>!</p>
|
||||
<p>It is recommended to set the <code>cookie</code> parameter manually; if this parameter is not set, the program functions may not work properly!</p>
|
||||
<p><del>It is recommended to set the <code>cookie</code> parameter manually; if this parameter is not set, the program functions may not work properly!</del></p>
|
||||
<h2>🖱 Program Run</h2>
|
||||
<p>Mac OS, Windows 10 and above users can go to <a href="https://github.com/JoeanAmier/XHS-Downloader/releases/latest">Releases</a> to download the program package, unzip it, open the program folder, and double-click to run <code>main</code> to use.</p>
|
||||
<p><strong>Note: The executable file <code>main</code> for Mac OS may need to be launched from the terminal command line; Due to device limitations, the Mac OS executable file has not been tested and its availability cannot be guaranteed!</strong></p>
|
||||
@ -93,7 +95,7 @@
|
||||
</ol>
|
||||
<h1>🛠 Command Line Mode</h1>
|
||||
<p>The project supports command line mode. If you want to download specific images from a text and image work, you can use this mode to set the image sequence number you want to download!</p>
|
||||
<p>You can use the command line to <b>read cookies from the browser and write to the configuration file</b>! Note that you need to close the browser to read the data!</p>
|
||||
<p>You can use the command line to <b>read cookies from the browser and write to the configuration file!</b></p>
|
||||
<p>Command example: <code>python .\main.py --browser_cookie Chrome --update_settings</code></p>
|
||||
<p>The <code>bool</code> type parameters support setting with <code>true</code>, <code>false</code>, <code>1</code>, <code>0</code>, <code>yes</code>, <code>no</code>, <code>on</code> or <code>off</code> (case insensitive).</p>
|
||||
<hr>
|
||||
@ -183,10 +185,10 @@ 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
|
||||
# sec_ch_ua = "" # 请求头 Sec-Ch-Ua
|
||||
# sec_ch_ua_platform = "" # 请求头 Sec-Ch-Ua-Platform
|
||||
user_agent = "" # User-Agent
|
||||
cookie = "" # 小红书网页版 Cookie,无需登录,必需参数,登录状态对数据采集有影响
|
||||
cookie = "" # 小红书网页版 Cookie,无需登录,可选参数,登录状态对数据采集有影响
|
||||
proxy = None # 网络代理
|
||||
timeout = 5 # 请求数据超时限制,单位:秒,默认值:10
|
||||
chunk = 1024 * 1024 * 10 # 下载文件时,每次从服务器获取的数据块大小,单位:字节
|
||||
@ -196,26 +198,27 @@ async def example():
|
||||
folder_mode = False # 是否将每个作品的文件储存至单独的文件夹
|
||||
# async with XHS() as xhs:
|
||||
# pass # 使用默认参数
|
||||
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,
|
||||
timeout=timeout,
|
||||
chunk=chunk,
|
||||
max_retry=max_retry,
|
||||
record_data=record_data,
|
||||
image_format=image_format,
|
||||
folder_mode=folder_mode,
|
||||
) as xhs: # 使用自定义参数
|
||||
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,
|
||||
timeout=timeout,
|
||||
chunk=chunk,
|
||||
max_retry=max_retry,
|
||||
record_data=record_data,
|
||||
image_format=image_format,
|
||||
folder_mode=folder_mode,
|
||||
) as xhs: # 使用自定义参数
|
||||
download = True # 是否下载作品文件,默认值:False
|
||||
# 返回作品详细信息,包括下载地址
|
||||
# 获取数据失败时返回空字典
|
||||
print(await xhs.extract(error_link, download, ))
|
||||
print(await xhs.extract(demo_link, download, ))
|
||||
print(await xhs.extract(demo_link, download, index=[1, 2]))
|
||||
# 支持传入多个作品链接
|
||||
print(await xhs.extract(multiple_links, download, ))
|
||||
</pre>
|
||||
@ -257,13 +260,13 @@ async def example():
|
||||
<td align="center"><code>publish_time author_nickname title</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">sec_ch_ua</td>
|
||||
<td align="center"><del>sec_ch_ua</del>(Deprecated)</td>
|
||||
<td align="center">str</td>
|
||||
<td align="center">Browser request header Sec-Ch-Ua</td>
|
||||
<td align="center">Built-in Chrome Sec-Ch-Ua</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">sec_ch_ua_platform</td>
|
||||
<td align="center"><del>sec_ch_ua_platform</del>(Deprecated)</td>
|
||||
<td align="center">str</td>
|
||||
<td align="center">Browser request header Sec-Ch-Ua-Platform</td>
|
||||
<td align="center">Built-in Chrome Sec-Ch-Ua-Platform</td>
|
||||
@ -277,12 +280,12 @@ async def example():
|
||||
<tr>
|
||||
<td align="center">cookie</td>
|
||||
<td align="center">str</td>
|
||||
<td align="center">Xiaohongshu web version cookie, <b>login not required</b></td>
|
||||
<td align="center">Xiaohongshu web version cookie, <b>No login required, non essential parameters!</b></td>
|
||||
<td align="center">None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">proxy</td>
|
||||
<td align="center">str|dict</td>
|
||||
<td align="center">str | dict</td>
|
||||
<td align="center">Set program proxy</td>
|
||||
<td align="center">null</td>
|
||||
</tr>
|
||||
@ -354,9 +357,10 @@ async def example():
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><b>Additional Notes: The parameters <code>sec_ch_ua</code>, <code>sec_ch_ua_platform</code>, and <code>user_agent</code> examples are provided for reference, and need to be set manually only if the program fails to fetch data!</b></p>
|
||||
<p><b>Additional Notes: The parameters <code>user_agent</code> examples are provided for reference; Strongly recommend setting according to actual browser information!</b></p>
|
||||
<img src="static/screenshot/请求头示例图.png" alt="">
|
||||
<h1>🌐 Cookie</h1>
|
||||
<p>Starting from version <code>2.2</code>, if there are no abnormalities in project functionality, there is no need to handle cookies separately!</p>
|
||||
<ol>
|
||||
<li>Open the browser (optional: start in incognito mode) and visit <code>https://www.xiaohongshu.com/explore</code></li>
|
||||
<li>Log in to your Xiaohongshu account (can be skipped)</li>
|
||||
|
||||
39
main.py
@ -19,10 +19,10 @@ 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
|
||||
# sec_ch_ua = "" # 请求头 Sec-Ch-Ua
|
||||
# sec_ch_ua_platform = "" # 请求头 Sec-Ch-Ua-Platform
|
||||
user_agent = "" # User-Agent
|
||||
cookie = "" # 小红书网页版 Cookie,无需登录,必需参数,登录状态对数据采集有影响
|
||||
cookie = "" # 小红书网页版 Cookie,无需登录,可选参数,登录状态对数据采集有影响
|
||||
proxy = None # 网络代理
|
||||
timeout = 5 # 请求数据超时限制,单位:秒,默认值:10
|
||||
chunk = 1024 * 1024 * 10 # 下载文件时,每次从服务器获取的数据块大小,单位:字节
|
||||
@ -32,26 +32,27 @@ async def example():
|
||||
folder_mode = False # 是否将每个作品的文件储存至单独的文件夹
|
||||
# async with XHS() as xhs:
|
||||
# pass # 使用默认参数
|
||||
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,
|
||||
timeout=timeout,
|
||||
chunk=chunk,
|
||||
max_retry=max_retry,
|
||||
record_data=record_data,
|
||||
image_format=image_format,
|
||||
folder_mode=folder_mode,
|
||||
) as xhs: # 使用自定义参数
|
||||
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,
|
||||
timeout=timeout,
|
||||
chunk=chunk,
|
||||
max_retry=max_retry,
|
||||
record_data=record_data,
|
||||
image_format=image_format,
|
||||
folder_mode=folder_mode,
|
||||
) as xhs: # 使用自定义参数
|
||||
download = True # 是否下载作品文件,默认值:False
|
||||
# 返回作品详细信息,包括下载地址
|
||||
# 获取数据失败时返回空字典
|
||||
print(await xhs.extract(error_link, download, ))
|
||||
print(await xhs.extract(demo_link, download, ))
|
||||
print(await xhs.extract(demo_link, download, index=[1, 2]))
|
||||
# 支持传入多个作品链接
|
||||
print(await xhs.extract(multiple_links, download, ))
|
||||
|
||||
|
||||
@ -122,9 +122,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")),
|
||||
# ("--sec_ch_ua", "-su", "str", _("Sec-Ch-Ua")),
|
||||
# ("--sec_ch_ua_platform", "-sp", "str", _("Sec-Ch-Ua-Platform")),
|
||||
("--user_agent", "-ua", "str", _("User-Agent")),
|
||||
("--cookie", "-ck", "str", _("小红书网页版 Cookie,无需登录")),
|
||||
("--proxy", "-p", "str", _("网络代理")),
|
||||
("--timeout", "-t", "int", _("请求数据超时限制,单位:秒")),
|
||||
@ -133,6 +133,7 @@ class CLI:
|
||||
("--record_data", "-rd", "bool", _("是否记录作品数据至文件")),
|
||||
("--image_format", "-if", "choice", _("图文作品文件下载格式,支持:PNG、WEBP")),
|
||||
("--live_download", "-ld", "bool", _("图文动图文件下载开关")),
|
||||
("--download_record", "-dr", "bool", _("作品下载记录开关")),
|
||||
("--folder_mode", "-fm", "bool", _("是否将每个作品的文件储存至单独的文件夹")),
|
||||
("--language", "-l", "choice", _("设置程序语言,目前支持:zh_CN、en_GB")),
|
||||
("--settings", "-s", "str", _("读取指定配置文件")),
|
||||
@ -165,8 +166,8 @@ class CLI:
|
||||
)
|
||||
@option("--folder_name", "-fn", )
|
||||
@option("--name_format", "-nf", )
|
||||
@option("--sec_ch_ua", "-su", )
|
||||
@option("--sec_ch_ua_platform", "-sp", )
|
||||
# @option("--sec_ch_ua", "-su", )
|
||||
# @option("--sec_ch_ua_platform", "-sp", )
|
||||
@option("--user_agent", "-ua", )
|
||||
@option("--cookie", "-ck", )
|
||||
@option("--proxy", "-p", )
|
||||
@ -176,12 +177,14 @@ class CLI:
|
||||
@option("--record_data", "-rd", type=bool, )
|
||||
@option("--image_format", "-if", type=Choice(["png", "PNG", "webp", "WEBP"]), )
|
||||
@option("--live_download", "-ld", type=bool, )
|
||||
@option("--download_record", "-dr", type=bool, )
|
||||
@option("--folder_mode", "-fm", type=bool, )
|
||||
@option("--language", "-l",
|
||||
type=Choice(["zh_CN", "en_GB"]), )
|
||||
@option("--settings", "-s", type=Path(dir_okay=False), )
|
||||
@option("--browser_cookie", "-bc", type=Choice(list(BrowserCookie.SUPPORT_BROWSER.keys()
|
||||
) + [str(i) for i in range(1, 11)]), callback=CLI.read_cookie, )
|
||||
@option("--browser_cookie", "-bc", type=Choice(
|
||||
list(BrowserCookie.SUPPORT_BROWSER.keys()
|
||||
) + [str(i) for i in range(1, len(BrowserCookie.SUPPORT_BROWSER) + 1)]), callback=CLI.read_cookie, )
|
||||
@option("--update_settings", "-us", type=bool,
|
||||
is_flag=True, )
|
||||
@option("-h",
|
||||
|
||||
@ -39,13 +39,13 @@ 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("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,
|
||||
id="user_agent", ),
|
||||
@ -80,13 +80,14 @@ class Setting(Screen):
|
||||
Container(
|
||||
Select.from_values(
|
||||
("PNG", "WEBP"),
|
||||
value=self.data["image_format"],
|
||||
value=self.data["image_format"].upper(),
|
||||
allow_blank=False,
|
||||
id="image_format"),
|
||||
Select.from_values(["zh_CN", "en_GB"],
|
||||
value=self.data["language"],
|
||||
allow_blank=False,
|
||||
id="language", ),
|
||||
Select.from_values(
|
||||
["zh_CN", "en_GB"],
|
||||
value=self.data["language"],
|
||||
allow_blank=False,
|
||||
id="language", ),
|
||||
classes="horizontal-layout"),
|
||||
Container(
|
||||
Button(self.message("保存配置"), id="save", ),
|
||||
@ -109,8 +110,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,
|
||||
# "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,
|
||||
|
||||
@ -38,6 +38,7 @@ from source.module import (
|
||||
)
|
||||
from source.module import Translate
|
||||
from source.module import logging
|
||||
from source.module import sleep_time
|
||||
from .download import Download
|
||||
from .explore import Explore
|
||||
from .image import Image
|
||||
@ -79,8 +80,8 @@ class XHS:
|
||||
work_path="",
|
||||
folder_name="Download",
|
||||
name_format="发布时间 作者昵称 作品标题",
|
||||
sec_ch_ua: str = "",
|
||||
sec_ch_ua_platform: str = "",
|
||||
# sec_ch_ua: str = "",
|
||||
# sec_ch_ua_platform: str = "",
|
||||
user_agent: str = None,
|
||||
cookie: str = None,
|
||||
proxy: str | dict = None,
|
||||
@ -109,8 +110,8 @@ class XHS:
|
||||
folder_name,
|
||||
name_format,
|
||||
chunk,
|
||||
sec_ch_ua,
|
||||
sec_ch_ua_platform,
|
||||
# sec_ch_ua,
|
||||
# sec_ch_ua_platform,
|
||||
user_agent,
|
||||
self.read_browser_cookie(read_cookie) or cookie,
|
||||
proxy,
|
||||
@ -210,11 +211,11 @@ class XHS:
|
||||
|
||||
async def extract_cli(
|
||||
self,
|
||||
url: str,
|
||||
download=True,
|
||||
index: list | tuple = None,
|
||||
log=None,
|
||||
bar=None,
|
||||
url: str,
|
||||
download=True,
|
||||
index: list | tuple = None,
|
||||
log=None,
|
||||
bar=None,
|
||||
data=False,
|
||||
) -> None:
|
||||
url = await self.__extract_links(url, log)
|
||||
@ -271,6 +272,7 @@ class XHS:
|
||||
data["下载地址"] = []
|
||||
await self.__download_files(data, download, index, log, bar)
|
||||
logging(log, self.message("作品处理完成:{0}").format(i))
|
||||
await sleep_time()
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
# from asyncio import gather
|
||||
from asyncio import Semaphore
|
||||
from asyncio import gather
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
@ -6,6 +7,7 @@ from aiofiles import open
|
||||
from httpx import HTTPError
|
||||
|
||||
from source.module import ERROR
|
||||
# from source.module import WARNING
|
||||
from source.module import Manager
|
||||
from source.module import logging
|
||||
from source.module import retry as re_download
|
||||
@ -18,6 +20,7 @@ __all__ = ['Download']
|
||||
|
||||
|
||||
class Download:
|
||||
SEMAPHORE = Semaphore(4)
|
||||
CONTENT_TYPE_MAP = {
|
||||
"image/png": "png",
|
||||
"image/jpeg": "jpg",
|
||||
@ -53,7 +56,7 @@ class Download:
|
||||
type_: str,
|
||||
log,
|
||||
bar,
|
||||
) -> tuple[Path, list]:
|
||||
) -> tuple[Path, tuple[bool, ...]]:
|
||||
path = self.__generate_path(name)
|
||||
match type_:
|
||||
case "视频":
|
||||
@ -63,8 +66,8 @@ class Download:
|
||||
urls, lives, index, path, name, log)
|
||||
case _:
|
||||
raise ValueError
|
||||
result = [
|
||||
await self.__download(
|
||||
tasks = [
|
||||
self.__download(
|
||||
url,
|
||||
path,
|
||||
name,
|
||||
@ -73,8 +76,8 @@ class Download:
|
||||
bar,
|
||||
) for url, name, format_ in tasks
|
||||
]
|
||||
# result = await gather(*tasks)
|
||||
return path, result
|
||||
tasks = await gather(*tasks)
|
||||
return path, tasks
|
||||
|
||||
def __generate_path(self, name: str):
|
||||
path = self.manager.archive(self.folder, name, self.folder_mode)
|
||||
@ -137,58 +140,63 @@ class Download:
|
||||
log,
|
||||
bar,
|
||||
):
|
||||
headers = self.headers.copy()
|
||||
try:
|
||||
length, suffix = await self.__head_file(url, headers, format_, )
|
||||
except HTTPError as error:
|
||||
logging(
|
||||
log,
|
||||
self.message(
|
||||
"网络异常,{0} 请求失败,错误信息: {1}").format(name, repr(error)),
|
||||
ERROR,
|
||||
)
|
||||
# logging(
|
||||
# log,
|
||||
# f"{url} Head Headers: {headers.get("Range")}",
|
||||
# WARNING,
|
||||
# )
|
||||
return False
|
||||
temp = self.temp.joinpath(f"{name}.{suffix}")
|
||||
real = path.joinpath(f"{name}.{suffix}")
|
||||
self.__update_headers_range(headers, temp, )
|
||||
try:
|
||||
async with self.client.stream("GET", url, headers=headers, ) as response:
|
||||
await sleep_time()
|
||||
response.raise_for_status()
|
||||
# self.__create_progress(
|
||||
# bar,
|
||||
# int(
|
||||
# response.headers.get(
|
||||
# 'content-length', 0)) or None,
|
||||
async with self.SEMAPHORE:
|
||||
headers = self.headers.copy()
|
||||
try:
|
||||
length, suffix = await self.__head_file(
|
||||
url,
|
||||
headers,
|
||||
format_,
|
||||
)
|
||||
except HTTPError as error:
|
||||
logging(
|
||||
log,
|
||||
self.message(
|
||||
"网络异常,{0} 请求失败,错误信息: {1}").format(name, repr(error)),
|
||||
ERROR,
|
||||
)
|
||||
# logging(
|
||||
# log,
|
||||
# f"{url} Head Headers: {headers.get("Range")}",
|
||||
# WARNING,
|
||||
# )
|
||||
async with open(temp, "ab") as f:
|
||||
async for chunk in response.aiter_bytes(self.chunk):
|
||||
await f.write(chunk)
|
||||
# self.__update_progress(bar, len(chunk))
|
||||
self.manager.move(temp, real)
|
||||
# self.__create_progress(bar, None)
|
||||
logging(log, self.message("文件 {0} 下载成功").format(real.name))
|
||||
return True
|
||||
except HTTPError as error:
|
||||
# self.manager.delete(temp)
|
||||
# self.__create_progress(bar, None)
|
||||
logging(
|
||||
log,
|
||||
self.message(
|
||||
"网络异常,{0} 下载失败,错误信息: {1}").format(name, repr(error)),
|
||||
ERROR,
|
||||
)
|
||||
# logging(
|
||||
# log,
|
||||
# f"{url} Stream Headers: {headers.get("Range")}",
|
||||
# WARNING,
|
||||
# )
|
||||
return False
|
||||
return False
|
||||
temp = self.temp.joinpath(f"{name}.{suffix}")
|
||||
real = path.joinpath(f"{name}.{suffix}")
|
||||
self.__update_headers_range(headers, temp, )
|
||||
try:
|
||||
async with self.client.stream("GET", url, headers=headers, ) as response:
|
||||
await sleep_time()
|
||||
response.raise_for_status()
|
||||
# self.__create_progress(
|
||||
# bar,
|
||||
# int(
|
||||
# response.headers.get(
|
||||
# 'content-length', 0)) or None,
|
||||
# )
|
||||
async with open(temp, "ab") as f:
|
||||
async for chunk in response.aiter_bytes(self.chunk):
|
||||
await f.write(chunk)
|
||||
# self.__update_progress(bar, len(chunk))
|
||||
self.manager.move(temp, real)
|
||||
# self.__create_progress(bar, None)
|
||||
logging(log, self.message("文件 {0} 下载成功").format(real.name))
|
||||
return True
|
||||
except HTTPError as error:
|
||||
# self.manager.delete(temp)
|
||||
# self.__create_progress(bar, None)
|
||||
logging(
|
||||
log,
|
||||
self.message(
|
||||
"网络异常,{0} 下载失败,错误信息: {1}").format(name, repr(error)),
|
||||
ERROR,
|
||||
)
|
||||
# logging(
|
||||
# log,
|
||||
# f"{url} Stream Headers: {headers.get("Range")}",
|
||||
# WARNING,
|
||||
# )
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def __create_progress(bar, total: int | None, completed=0, ):
|
||||
@ -204,11 +212,13 @@ class Download:
|
||||
def __extract_type(cls, content: str) -> str:
|
||||
return cls.CONTENT_TYPE_MAP.get(content, "")
|
||||
|
||||
async def __head_file(self,
|
||||
url: str,
|
||||
headers: dict[str, str],
|
||||
suffix: str,
|
||||
) -> [int, str]:
|
||||
async def __head_file(
|
||||
self,
|
||||
url: str,
|
||||
headers: dict[str, str],
|
||||
suffix: str,
|
||||
# sleep_args: tuple[int, int],
|
||||
) -> [int, str]:
|
||||
response = await self.client.head(
|
||||
url,
|
||||
headers=headers,
|
||||
|
||||
@ -29,7 +29,7 @@ class Image:
|
||||
|
||||
@staticmethod
|
||||
def __generate_png_link(token: str) -> str:
|
||||
return f"https://ci.xiaohongshu.com/{token}?imageView2/2/w/format/png"
|
||||
return f"https://ci.xiaohongshu.com/{token}?imageView2/format/png"
|
||||
|
||||
@staticmethod
|
||||
def __extract_image_token(url: str) -> str:
|
||||
|
||||
@ -4,6 +4,7 @@ from source.module import ERROR
|
||||
from source.module import Manager
|
||||
from source.module import logging
|
||||
from source.module import retry
|
||||
from source.module import sleep_time
|
||||
|
||||
__all__ = ["Html"]
|
||||
|
||||
@ -29,10 +30,12 @@ class Html:
|
||||
match content:
|
||||
case True:
|
||||
response = await self.__request_url_get(url, headers, **kwargs, )
|
||||
await sleep_time()
|
||||
response.raise_for_status()
|
||||
return response.text
|
||||
case False:
|
||||
response = await self.__request_url_head(url, headers, **kwargs, )
|
||||
await sleep_time()
|
||||
return str(response.url)
|
||||
except HTTPError as error:
|
||||
logging(
|
||||
|
||||
@ -11,13 +11,13 @@ from httpx import RequestError
|
||||
from httpx import TimeoutException
|
||||
from httpx import get
|
||||
|
||||
from source.expansion import remove_empty_directories
|
||||
from .static import HEADERS
|
||||
from .static import SEC_CH_UA
|
||||
from .static import SEC_CH_UA_PLATFORM
|
||||
# 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
|
||||
from source.expansion import remove_empty_directories
|
||||
|
||||
__all__ = ["Manager"]
|
||||
|
||||
@ -54,8 +54,8 @@ class Manager:
|
||||
folder: str,
|
||||
name_format: str,
|
||||
chunk: int,
|
||||
sec_ch_ua: str,
|
||||
sec_ch_ua_platform: str,
|
||||
# sec_ch_ua: str,
|
||||
# sec_ch_ua_platform: str,
|
||||
user_agent: str,
|
||||
cookie: str,
|
||||
proxy: str | dict,
|
||||
@ -78,12 +78,12 @@ class Manager:
|
||||
self.folder = self.__check_folder(folder)
|
||||
self.message = transition
|
||||
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,
|
||||
'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,
|
||||
'cookie': cookie,
|
||||
}
|
||||
self.retry = retry
|
||||
self.chunk = chunk
|
||||
@ -97,7 +97,8 @@ class Manager:
|
||||
self.print_proxy_tip(_print, )
|
||||
self.request_client = AsyncClient(
|
||||
headers=self.headers | {
|
||||
"Referer": "https://www.xiaohongshu.com/explore", },
|
||||
'referer': 'https://www.xiaohongshu.com/',
|
||||
},
|
||||
timeout=timeout,
|
||||
verify=False,
|
||||
follow_redirects=True,
|
||||
|
||||
@ -4,8 +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 SEC_CH_UA
|
||||
# from .static import SEC_CH_UA_PLATFORM
|
||||
from .static import USERAGENT
|
||||
|
||||
__all__ = ['Settings']
|
||||
@ -16,8 +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,
|
||||
# "sec_ch_ua": SEC_CH_UA,
|
||||
# "sec_ch_ua_platform": SEC_CH_UA_PLATFORM,
|
||||
"user_agent": USERAGENT,
|
||||
"cookie": "",
|
||||
"proxy": None,
|
||||
|
||||
@ -25,7 +25,7 @@ __all__ = [
|
||||
|
||||
VERSION_MAJOR = 2
|
||||
VERSION_MINOR = 2
|
||||
VERSION_BETA = True
|
||||
VERSION_BETA = False
|
||||
ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
PROJECT = f"XHS-Downloader V{VERSION_MAJOR}.{
|
||||
VERSION_MINOR}{" Beta" if VERSION_BETA else ""}"
|
||||
@ -36,29 +36,27 @@ RELEASES = "https://github.com/JoeanAmier/XHS-Downloader/releases/latest"
|
||||
|
||||
USERSCRIPT = "https://raw.githubusercontent.com/JoeanAmier/XHS-Downloader/master/static/XHS-Downloader.js"
|
||||
|
||||
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\""
|
||||
USERAGENT = ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 '
|
||||
'Safari/537.36 Edg/128.0.0.0')
|
||||
SEC_CH_UA = '"Chromium";v="128", "Not;A=Brand";v="24", "Microsoft Edge";v="128"'
|
||||
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,"
|
||||
"application/signed-exchange;v=b3;q=0.7",
|
||||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||
"Accept-Language": "zh-SG,zh-CN;q=0.9,zh;q=0.8",
|
||||
"Cookie": "",
|
||||
"Dnt": "1",
|
||||
# "Priority": "u=0, i",
|
||||
"Sec-Ch-Ua": SEC_CH_UA,
|
||||
"Sec-Ch-Ua-Mobile": "?0",
|
||||
"Sec-Ch-Ua-Platform": SEC_CH_UA_PLATFORM,
|
||||
"Sec-Fetch-Dest": "document",
|
||||
"Sec-Fetch-Mode": "navigate",
|
||||
"Sec-Fetch-Site": "none",
|
||||
"Sec-Fetch-User": "?1",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"User-Agent": USERAGENT,
|
||||
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||
# 'accept-language': 'zh-CN,zh;q=0.9',
|
||||
'cache-control': 'no-cache',
|
||||
# 'dnt': '1',
|
||||
'pragma': 'no-cache',
|
||||
# 'priority': 'u=0, i',
|
||||
# 'sec-ch-ua': SEC_CH_UA,
|
||||
# 'sec-ch-ua-mobile': '?0',
|
||||
# 'sec-ch-ua-platform': SEC_CH_UA_PLATFORM,
|
||||
# 'sec-fetch-dest': 'document',
|
||||
# 'sec-fetch-mode': 'navigate',
|
||||
# 'sec-fetch-site': 'none',
|
||||
# 'sec-fetch-user': '?1',
|
||||
# 'upgrade-insecure-requests': '1',
|
||||
'user-agent': USERAGENT,
|
||||
}
|
||||
|
||||
MASTER = "b #fff200"
|
||||
|
||||
@ -6,8 +6,6 @@ from rich.text import Text
|
||||
|
||||
from .static import INFO
|
||||
|
||||
__all__ = ["retry", "logging", ]
|
||||
|
||||
|
||||
def retry(function):
|
||||
async def inner(self, *args, **kwargs):
|
||||
@ -30,7 +28,7 @@ def logging(log, text, style=INFO):
|
||||
|
||||
|
||||
async def sleep_time(
|
||||
min_time: int = 1,
|
||||
max_time: int = 3,
|
||||
min_time: int | float = 1,
|
||||
max_time: int | float = 3,
|
||||
):
|
||||
await sleep(uniform(min_time, max_time))
|
||||
|
||||
@ -3,26 +3,34 @@
|
||||
1. 使用 rookiepy 替代 browser-cookie3
|
||||
2. 修复 record_data 参数无效的问题
|
||||
3. 新增文件名称非法字符处理机制
|
||||
4. 新增自动删除空文件夹功能
|
||||
5. 优化重定向链接获取逻辑
|
||||
6. 引入 aiofiles 库操作文件
|
||||
7. 修复 ScannerError 错误
|
||||
8. 更新 chunk 参数默认值
|
||||
9. 优化作品数据提取逻辑
|
||||
10. 优化作品数据返回格式
|
||||
11. 优化项目运行信息提示
|
||||
12. 新增作品下载记录开关
|
||||
13. 新增文件断点续传功能
|
||||
14. 新增文件下载延时处理
|
||||
15. 移除文件并发下载功能
|
||||
16. 默认开启局域网访问
|
||||
17. 修复其他已知问题
|
||||
18. 更新英语翻译
|
||||
19. 其他细节优化
|
||||
4. 移除 sec_ch_ua_platform 参数
|
||||
5. 新增自动删除空文件夹功能
|
||||
6. 优化重定向链接获取逻辑
|
||||
7. 引入 aiofiles 库操作文件
|
||||
8. 修复 ScannerError 错误
|
||||
9. 修改 chunk 参数默认值
|
||||
10. 优化作品数据提取逻辑
|
||||
11. 优化作品数据返回格式
|
||||
12. 优化项目运行信息提示
|
||||
13. 新增作品下载记录开关
|
||||
14. 新增文件断点续传功能
|
||||
15. 新增获取数据延时处理
|
||||
16. 移除文件并发下载功能
|
||||
17. 移除 sec_ch_ua 参数
|
||||
18. 默认开启局域网访问
|
||||
19. 新增下载并发限制
|
||||
20. 修复其他已知问题
|
||||
21. 更新英语翻译
|
||||
22. 其他细节优化
|
||||
|
||||
<p><strong>建议删除旧版配置文件 <code>settings.json</code>,删除后重新运行程序会自动生成新的默认配置文件!</strong></p>
|
||||
|
||||
<p><strong>注意:Mac OS 平台可执行文件 <code>main</code> 可能需要从终端命令行启动;受设备限制,Mac OS 平台可执行文件尚未经过测试,无法保证可用性!</strong></p>
|
||||
|
||||
<hr>
|
||||
|
||||
**用户脚本更新内容:**
|
||||
|
||||
1. 新增专辑作品链接提取功能
|
||||
2. 更新作品链接提取功能
|
||||
3. 其他细节优化
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name XHS-Downloader
|
||||
// @namespace https://github.com/JoeanAmier/XHS-Downloader
|
||||
// @version 1.6.1
|
||||
// @version 1.6.2
|
||||
// @description 提取小红书作品/用户链接,下载小红书无水印图文/视频作品文件
|
||||
// @author JoeanAmier
|
||||
// @match http*://xhslink.com/*
|
||||
@ -149,7 +149,7 @@
|
||||
images.forEach((item) => {
|
||||
let match = item.urlDefault.match(regex);
|
||||
if (match && match[1]) {
|
||||
urls.push(`https://ci.xiaohongshu.com/${match[1]}?imageView2/2/w/format/png`);
|
||||
urls.push(`https://ci.xiaohongshu.com/${match[1]}?imageView2/format/png`);
|
||||
}
|
||||
})
|
||||
return urls
|
||||
|
||||
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 143 KiB |