mirror of
https://github.com/NanmiCoder/MediaCrawler.git
synced 2026-05-08 19:47:40 +08:00
feat: 支持连接用户已有的 Chrome 浏览器进行爬取
新增 CDP_CONNECT_EXISTING 配置项,默认开启,通过 Chrome 远程调试功能 (chrome://inspect/#remote-debugging) 直接连接用户正在使用的浏览器, 复用真实的 Cookie、扩展和浏览历史,大幅降低平台风控检测风险。 主要变更: - 新增 _connect_existing_browser 方法,通过 ws:// 直接连接已有浏览器 - 支持等待用户在浏览器端确认连接对话框(60秒超时) - cleanup 时不关闭用户的浏览器进程 - 修复小红书在真实浏览器下 cookie 过多导致签名失败的问题 - 更新 README、CDP使用指南和常见问题文档
This commit is contained in:
20
README.md
20
README.md
@@ -112,13 +112,29 @@ cd MediaCrawler
|
||||
uv sync
|
||||
```
|
||||
|
||||
### 🌐 浏览器驱动安装
|
||||
### 🌐 浏览器驱动安装(可选)
|
||||
|
||||
> 如果使用默认的 CDP 模式(连接已有 Chrome 浏览器),**无需安装浏览器驱动**。仅在使用标准 Playwright 模式时需要安装。
|
||||
|
||||
```shell
|
||||
# 安装浏览器驱动
|
||||
# 仅在标准 Playwright 模式下需要安装浏览器驱动
|
||||
uv run playwright install
|
||||
```
|
||||
|
||||
### 🌍 Chrome 浏览器配置(推荐)
|
||||
|
||||
项目默认使用 CDP 模式连接用户已有的 Chrome 浏览器,可以复用浏览器已有的登录状态、Cookie、扩展等,**大幅降低平台风控检测风险**。
|
||||
|
||||
使用前需要:
|
||||
|
||||
1. **安装最新版 Chrome 浏览器**(版本 >= 144),[下载地址](https://www.google.com/chrome/)
|
||||
2. **开启远程调试功能**:在 Chrome 地址栏输入 `chrome://inspect/#remote-debugging`,勾选 **"Allow remote debugging for this browser instance"**
|
||||
3. 页面显示 `Server running at: 127.0.0.1:9222` 表示已就绪
|
||||
|
||||
> 💡 **提示**:运行爬虫后,Chrome 浏览器会弹出确认对话框,点击"接受"即可。程序会等待用户确认,60秒内操作完成即可。
|
||||
>
|
||||
> 如果不想使用 CDP 模式,可以在 `config/base_config.py` 中设置 `ENABLE_CDP_MODE = False` 切换为标准 Playwright 模式。
|
||||
|
||||
## 🚀 运行爬虫程序
|
||||
|
||||
```shell
|
||||
|
||||
@@ -48,31 +48,38 @@ HEADLESS = False
|
||||
# Whether to save login status
|
||||
SAVE_LOGIN_STATE = True
|
||||
|
||||
# ==================== CDP (Chrome DevTools Protocol) Configuration ====================
|
||||
# Whether to enable CDP mode - use the user's existing Chrome/Edge browser to crawl, providing better anti-detection capabilities
|
||||
# Once enabled, the user's Chrome/Edge browser will be automatically detected and started, and controlled through the CDP protocol.
|
||||
# This method uses the real browser environment, including the user's extensions, cookies and settings, greatly reducing the risk of detection.
|
||||
ENABLE_CDP_MODE = False
|
||||
# ==================== CDP (Chrome DevTools Protocol) 配置 ====================
|
||||
# 是否启用 CDP 模式 - 使用用户本地的 Chrome/Edge 浏览器进行爬取,具有更好的反检测能力
|
||||
# 开启后,会自动检测并启动用户的 Chrome/Edge 浏览器,通过 CDP 协议进行控制
|
||||
# 该方式使用真实浏览器环境,包括用户的扩展、Cookie 和设置,大幅降低被风控检测的风险
|
||||
ENABLE_CDP_MODE = True
|
||||
|
||||
# CDP debug port, used to communicate with the browser
|
||||
# If the port is occupied, the system will automatically try the next available port
|
||||
# CDP 调试端口,用于与浏览器通信
|
||||
# 如果端口被占用,系统会自动尝试下一个可用端口
|
||||
CDP_DEBUG_PORT = 9222
|
||||
|
||||
# Custom browser path (optional)
|
||||
# If it is empty, the system will automatically detect the installation path of Chrome/Edge
|
||||
# Windows example: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
||||
# macOS example: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
# 自定义浏览器路径(可选)
|
||||
# 如果为空,系统会自动检测 Chrome/Edge 的安装路径
|
||||
# Windows 示例: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
||||
# macOS 示例: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
CUSTOM_BROWSER_PATH = ""
|
||||
|
||||
# Whether to enable headless mode in CDP mode
|
||||
# NOTE: Even if set to True, some anti-detection features may not work well in headless mode
|
||||
# 是否在 CDP 模式下启用无头模式
|
||||
# 注意:即使设置为 True,某些反检测功能在无头模式下可能无法正常工作
|
||||
CDP_HEADLESS = False
|
||||
|
||||
# Browser startup timeout (seconds)
|
||||
# 浏览器启动超时时间(秒)
|
||||
BROWSER_LAUNCH_TIMEOUT = 60
|
||||
|
||||
# Whether to automatically close the browser when the program ends
|
||||
# Set to False to keep the browser running for easy debugging
|
||||
# 是否连接用户已打开的浏览器,而不是启动新的浏览器
|
||||
# 开启后,程序会连接一个已经启用了远程调试的浏览器
|
||||
# 用户需要在 Chrome 中开启远程调试:chrome://inspect/#remote-debugging
|
||||
# 或者使用命令行参数启动 Chrome:--remote-debugging-port=9222
|
||||
# 这种方式反检测效果最好,因为直接使用用户真实浏览器的所有 Cookie、扩展和浏览历史
|
||||
CDP_CONNECT_EXISTING = True
|
||||
|
||||
# 程序结束时是否自动关闭浏览器
|
||||
# 设置为 False 可以保持浏览器运行,方便调试
|
||||
AUTO_CLOSE_BROWSER = True
|
||||
|
||||
# Data saving type option configuration, supports: csv, db, json, jsonl, sqlite, excel, postgres. It is best to save to DB, with deduplication function.
|
||||
|
||||
@@ -12,34 +12,63 @@ CDP(Chrome DevTools Protocol)模式是一种高级的反检测爬虫技术
|
||||
4. **扩展支持**: 可以利用用户安装的广告拦截器、代理扩展等工具
|
||||
5. **更自然的行为**: 浏览器行为模式更接近真实用户
|
||||
|
||||
### 📌 两种 CDP 模式
|
||||
|
||||
CDP模式支持两种使用方式:
|
||||
|
||||
| 模式 | 说明 | 适用场景 |
|
||||
|------|------|----------|
|
||||
| **连接已有浏览器**(默认推荐) | 连接用户正在使用的 Chrome 浏览器,复用真实的 Cookie、扩展和浏览历史 | 反检测要求高,需要最大程度降低风控风险 |
|
||||
| **启动新浏览器** | 自动检测并启动一个新的 Chrome/Edge 浏览器实例 | 不需要复用浏览器状态的场景 |
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 启用CDP模式
|
||||
### 方式一:连接已有浏览器(默认推荐)
|
||||
|
||||
在 `config/base_config.py` 中设置:
|
||||
这是**默认且推荐**的方式,直接连接你正在使用的 Chrome 浏览器,反检测效果最好。
|
||||
|
||||
#### 第一步:确保 Chrome 版本
|
||||
|
||||
需要 Chrome **144 或更高版本**(2026年1月起的稳定版均支持)。在地址栏输入 `chrome://version` 查看当前版本。
|
||||
|
||||
如果版本过低,请前往 [Chrome 官网](https://www.google.com/chrome/) 下载最新版。
|
||||
|
||||
#### 第二步:开启远程调试
|
||||
|
||||
1. 在 Chrome 地址栏输入:`chrome://inspect/#remote-debugging`
|
||||
2. 勾选 **"Allow remote debugging for this browser instance"**
|
||||
3. 页面会显示 `Server running at: 127.0.0.1:9222`,表示已就绪
|
||||
|
||||
#### 第三步:运行爬虫
|
||||
|
||||
```bash
|
||||
uv run main.py --platform xhs --lt qrcode --type search
|
||||
```
|
||||
|
||||
运行后,Chrome 浏览器会**弹出确认对话框**,点击"接受"即可。程序会等待用户确认(默认60秒超时)。
|
||||
|
||||
#### 配置说明
|
||||
|
||||
`config/base_config.py` 中的默认配置:
|
||||
|
||||
```python
|
||||
# 启用CDP模式
|
||||
ENABLE_CDP_MODE = True
|
||||
|
||||
# CDP调试端口(可选,默认9222)
|
||||
# 连接已有浏览器(默认开启)
|
||||
CDP_CONNECT_EXISTING = True
|
||||
|
||||
# CDP调试端口(与 chrome://inspect 页面显示的端口一致)
|
||||
CDP_DEBUG_PORT = 9222
|
||||
|
||||
# 是否在无头模式下运行(建议设为False以获得最佳反检测效果)
|
||||
CDP_HEADLESS = False
|
||||
|
||||
# 程序结束时是否自动关闭浏览器
|
||||
AUTO_CLOSE_BROWSER = True
|
||||
```
|
||||
|
||||
### 2. 运行测试
|
||||
### 方式二:启动新浏览器
|
||||
|
||||
```bash
|
||||
# 运行CDP功能测试
|
||||
python examples/cdp_example.py
|
||||
如果不想连接已有浏览器,可以让程序自动启动一个新的浏览器实例:
|
||||
|
||||
# 运行小红书爬虫(CDP模式)
|
||||
python main.py
|
||||
```python
|
||||
ENABLE_CDP_MODE = True
|
||||
CDP_CONNECT_EXISTING = False # 关闭连接已有浏览器,改为启动新浏览器
|
||||
```
|
||||
|
||||
## 配置选项详解
|
||||
@@ -48,7 +77,8 @@ python main.py
|
||||
|
||||
| 配置项 | 类型 | 默认值 | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| `ENABLE_CDP_MODE` | bool | False | 是否启用CDP模式 |
|
||||
| `ENABLE_CDP_MODE` | bool | True | 是否启用CDP模式 |
|
||||
| `CDP_CONNECT_EXISTING` | bool | True | 是否连接已有浏览器(推荐开启) |
|
||||
| `CDP_DEBUG_PORT` | int | 9222 | CDP调试端口 |
|
||||
| `CDP_HEADLESS` | bool | False | CDP模式下的无头模式 |
|
||||
| `AUTO_CLOSE_BROWSER` | bool | True | 程序结束时是否关闭浏览器 |
|
||||
@@ -57,8 +87,8 @@ python main.py
|
||||
|
||||
| 配置项 | 类型 | 默认值 | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| `CUSTOM_BROWSER_PATH` | str | "" | 自定义浏览器路径 |
|
||||
| `BROWSER_LAUNCH_TIMEOUT` | int | 30 | 浏览器启动超时时间(秒) |
|
||||
| `CUSTOM_BROWSER_PATH` | str | "" | 自定义浏览器路径(仅启动新浏览器模式下有效) |
|
||||
| `BROWSER_LAUNCH_TIMEOUT` | int | 60 | 浏览器连接超时时间(秒) |
|
||||
|
||||
### 自定义浏览器路径
|
||||
|
||||
@@ -219,15 +249,25 @@ ps aux | grep chrome
|
||||
|
||||
## 技术原理
|
||||
|
||||
CDP模式的工作原理:
|
||||
### 连接已有浏览器模式(推荐)
|
||||
|
||||
1. **用户开启远程调试**: 在 `chrome://inspect/#remote-debugging` 中勾选启用
|
||||
2. **WebSocket连接**: 程序通过 `ws://localhost:9222/devtools/browser` 直接连接浏览器
|
||||
3. **用户确认**: Chrome 弹出确认对话框,用户点击接受后连接建立
|
||||
4. **Playwright集成**: 使用 `connectOverCDP` 方法接管浏览器控制
|
||||
5. **上下文复用**: 直接使用浏览器已有的上下文(包含用户的Cookie、登录状态等)
|
||||
|
||||
> 💡 与传统CDP模式的区别:传统方式通过 `--remote-debugging-port` 启动新浏览器,使用 HTTP 接口 `/json/version` 获取 WebSocket URL。而连接已有浏览器方式直接通过 WebSocket 连接,Chrome 新版(136+)的远程调试不提供 HTTP 接口,需要用户在浏览器端确认授权。
|
||||
|
||||
### 启动新浏览器模式
|
||||
|
||||
1. **浏览器检测**: 自动扫描系统中的Chrome/Edge安装路径
|
||||
2. **进程启动**: 使用`--remote-debugging-port`参数启动浏览器
|
||||
3. **CDP连接**: 通过WebSocket连接到浏览器的调试接口
|
||||
3. **CDP连接**: 通过 HTTP 获取 WebSocket URL,再连接到浏览器的调试接口
|
||||
4. **Playwright集成**: 使用`connectOverCDP`方法接管浏览器控制
|
||||
5. **上下文管理**: 创建或复用浏览器上下文进行操作
|
||||
|
||||
这种方式绕过了传统WebDriver的检测机制,提供了更加隐蔽的自动化能力。
|
||||
两种方式都绕过了传统WebDriver的检测机制,提供了更加隐蔽的自动化能力。连接已有浏览器模式的反检测效果更好,因为使用的是用户真实的浏览器环境。
|
||||
|
||||
## 更新日志
|
||||
|
||||
|
||||
20
docs/常见问题.md
20
docs/常见问题.md
@@ -10,7 +10,7 @@ A: windows电脑去网站下载`https://nodejs.org/en/blog/release/v16.8.0` Wind
|
||||
## xhs登录出现滑块一直验证不通过问题
|
||||
|
||||
Q: 小红书扫码登录成功后,浏览器一直在验证滑块,无法登录?<br>
|
||||
A: 这种情况一般是因为使用playwright浏览器驱动被识别出来的问题,可以尝试删除项目目录下的`brower_data`文件夹,重新走登录流程。<br>
|
||||
A: 小红书平台风控非常严格,**强烈建议使用 CDP 模式连接自己的真实浏览器**(默认配置),不要使用无痕浏览器或标准 Playwright 模式。连接真实浏览器可以复用已有的 Cookie、登录状态和浏览历史,大幅降低被风控检测的概率。如果仍然出现滑块问题,可以尝试删除项目目录下的`brower_data`文件夹,重新走登录流程。<br>
|
||||
|
||||
## 如何指定关键词
|
||||
Q: 可以指定关键词爬取吗?<br>
|
||||
@@ -43,3 +43,21 @@ A: 打开 config/base_config.py 文件, 找到`ENABLE_GET_WORDCLOUD` 以及`ENAB
|
||||
## 词云图添加禁用词和自定义词组
|
||||
Q: 如何给词云图添加禁用词和自定义词组?
|
||||
A: 打开 `docs/hit_stopwords.txt` 输入禁用词(注意一个词语一行)。打开 config/base_config.py 文件找到 `CUSTOM_WORDS `按格式添加自定义词组即可。<br>
|
||||
|
||||
## CDP 连接已有浏览器相关问题
|
||||
|
||||
Q: 运行爬虫后提示无法连接到浏览器,报错 `Cannot connect to existing browser on port 9222`?<br>
|
||||
A: 请检查以下几点:<br>
|
||||
1. 确保 Chrome 浏览器已经打开并正在运行<br>
|
||||
2. 在 Chrome 地址栏输入 `chrome://inspect/#remote-debugging`,确保已勾选 **"Allow remote debugging for this browser instance"**<br>
|
||||
3. 页面上应显示 `Server running at: 127.0.0.1:9222`,如果没有显示说明远程调试未成功开启<br>
|
||||
4. 确保 Chrome 版本 >= 144,低版本不支持此功能,在地址栏输入 `chrome://version` 查看版本号<br>
|
||||
|
||||
Q: 运行爬虫后浏览器弹出了确认对话框,需要怎么操作?<br>
|
||||
A: 这是正常行为。Chrome 连接已有浏览器时会弹出确认对话框,点击"接受"即可。程序会等待用户确认,默认超时时间为60秒,在此期间点击确认即可。<br>
|
||||
|
||||
Q: 不想连接已有浏览器,想让程序自动启动一个新的浏览器怎么办?<br>
|
||||
A: 在 `config/base_config.py` 中设置 `CDP_CONNECT_EXISTING = False`,程序会自动检测并启动一个新的 Chrome/Edge 浏览器实例。<br>
|
||||
|
||||
Q: 为什么推荐连接已有浏览器而不是启动新浏览器?<br>
|
||||
A: 连接已有浏览器可以直接复用你浏览器中真实的 Cookie、登录状态、扩展插件和浏览历史,平台很难区分这是自动化操作还是真实用户行为,**大幅降低被平台风控检测的风险**。而启动新浏览器是一个"干净"的环境,更容易被平台识别为爬虫。<br>
|
||||
|
||||
@@ -356,7 +356,9 @@ class XiaoHongShuCrawler(AbstractCrawler):
|
||||
async def create_xhs_client(self, httpx_proxy: Optional[str]) -> XiaoHongShuClient:
|
||||
"""Create Xiaohongshu client"""
|
||||
utils.logger.info("[XiaoHongShuCrawler.create_xhs_client] Begin create Xiaohongshu API client ...")
|
||||
cookie_str, cookie_dict = utils.convert_cookies(await self.browser_context.cookies())
|
||||
cookie_str, cookie_dict = utils.convert_cookies(
|
||||
await self.browser_context.cookies(self.index_url)
|
||||
)
|
||||
xhs_client_obj = XiaoHongShuClient(
|
||||
proxy=httpx_proxy,
|
||||
headers={
|
||||
|
||||
@@ -105,6 +105,10 @@ class CDPBrowserManager:
|
||||
Launch browser and connect via CDP
|
||||
"""
|
||||
try:
|
||||
if config.CDP_CONNECT_EXISTING:
|
||||
# Connect to an existing browser that already has remote debugging enabled
|
||||
return await self._connect_existing_browser(playwright, playwright_proxy, user_agent)
|
||||
|
||||
# 1. Detect browser path
|
||||
browser_path = await self._get_browser_path()
|
||||
|
||||
@@ -133,6 +137,63 @@ class CDPBrowserManager:
|
||||
await self.cleanup()
|
||||
raise
|
||||
|
||||
async def _connect_existing_browser(
|
||||
self,
|
||||
playwright: Playwright,
|
||||
playwright_proxy: Optional[Dict] = None,
|
||||
user_agent: Optional[str] = None,
|
||||
) -> BrowserContext:
|
||||
"""
|
||||
Connect to an existing browser that already has remote debugging enabled.
|
||||
User needs to enable remote debugging via chrome://inspect/#remote-debugging
|
||||
or launch Chrome with --remote-debugging-port flag.
|
||||
"""
|
||||
self.debug_port = config.CDP_DEBUG_PORT
|
||||
utils.logger.info(
|
||||
f"[CDPBrowserManager] Connecting to existing browser on port {self.debug_port}..."
|
||||
)
|
||||
utils.logger.info(
|
||||
"[CDPBrowserManager] Make sure remote debugging is enabled in your browser: "
|
||||
"chrome://inspect/#remote-debugging"
|
||||
)
|
||||
|
||||
# Wait for the browser's CDP port to become available
|
||||
# The user may need time to enable remote debugging or confirm the connection dialog
|
||||
timeout = config.BROWSER_LAUNCH_TIMEOUT
|
||||
utils.logger.info(
|
||||
f"[CDPBrowserManager] Waiting up to {timeout}s for browser CDP connection..."
|
||||
)
|
||||
connected = False
|
||||
for i in range(timeout):
|
||||
if await self._test_cdp_connection(self.debug_port):
|
||||
connected = True
|
||||
break
|
||||
if i % 5 == 0 and i > 0:
|
||||
utils.logger.info(
|
||||
f"[CDPBrowserManager] Still waiting for browser... ({i}s elapsed) "
|
||||
"Please enable remote debugging: chrome://inspect/#remote-debugging"
|
||||
)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
if not connected:
|
||||
raise RuntimeError(
|
||||
f"Cannot connect to existing browser on port {self.debug_port} "
|
||||
f"after waiting {timeout}s. Please ensure:\n"
|
||||
" 1. Your browser is running\n"
|
||||
" 2. Remote debugging is enabled (chrome://inspect/#remote-debugging)\n"
|
||||
f" 3. The debug port is {self.debug_port} (configure via CDP_DEBUG_PORT)"
|
||||
)
|
||||
|
||||
# Connect via CDP (reuse existing method)
|
||||
await self._connect_via_cdp(playwright)
|
||||
|
||||
# Create browser context (reuse existing method, will prefer existing context)
|
||||
browser_context = await self._create_browser_context(playwright_proxy, user_agent)
|
||||
self.browser_context = browser_context
|
||||
|
||||
utils.logger.info("[CDPBrowserManager] Successfully connected to existing browser")
|
||||
return browser_context
|
||||
|
||||
async def _get_browser_path(self) -> str:
|
||||
"""
|
||||
Get browser path
|
||||
@@ -254,12 +315,23 @@ class CDPBrowserManager:
|
||||
Connect to browser via CDP
|
||||
"""
|
||||
try:
|
||||
# Get correct WebSocket URL
|
||||
ws_url = await self._get_browser_websocket_url(self.debug_port)
|
||||
utils.logger.info(f"[CDPBrowserManager] Connecting to browser via CDP: {ws_url}")
|
||||
|
||||
# Use Playwright's connectOverCDP method to connect
|
||||
self.browser = await playwright.chromium.connect_over_cdp(ws_url)
|
||||
if config.CDP_CONNECT_EXISTING:
|
||||
# For existing browser (e.g. chrome://inspect/#remote-debugging),
|
||||
# Chrome exposes a WebSocket at /devtools/browser and may show a confirmation
|
||||
# dialog to the user. Use ws:// with a longer timeout to wait for user confirmation.
|
||||
ws_url = f"ws://localhost:{self.debug_port}/devtools/browser"
|
||||
utils.logger.info(f"[CDPBrowserManager] Connecting to existing browser via CDP: {ws_url}")
|
||||
utils.logger.info(
|
||||
"[CDPBrowserManager] Please check your browser for a confirmation dialog and accept it"
|
||||
)
|
||||
self.browser = await playwright.chromium.connect_over_cdp(
|
||||
ws_url, timeout=config.BROWSER_LAUNCH_TIMEOUT * 1000
|
||||
)
|
||||
else:
|
||||
# For launched browser, get WebSocket URL first
|
||||
ws_url = await self._get_browser_websocket_url(self.debug_port)
|
||||
utils.logger.info(f"[CDPBrowserManager] Connecting to browser via CDP: {ws_url}")
|
||||
self.browser = await playwright.chromium.connect_over_cdp(ws_url)
|
||||
|
||||
if self.browser.is_connected():
|
||||
utils.logger.info("[CDPBrowserManager] Successfully connected to browser")
|
||||
@@ -403,10 +475,14 @@ class CDPBrowserManager:
|
||||
finally:
|
||||
self.browser = None
|
||||
|
||||
# Close browser process
|
||||
# force=True means force close, ignoring AUTO_CLOSE_BROWSER config
|
||||
# Used for handling abnormal exit or manual cleanup
|
||||
if force or config.AUTO_CLOSE_BROWSER:
|
||||
# Close browser process (skip if connected to existing browser - we didn't launch it)
|
||||
if config.CDP_CONNECT_EXISTING:
|
||||
utils.logger.info(
|
||||
"[CDPBrowserManager] Connected to existing browser, skipping process cleanup"
|
||||
)
|
||||
elif force or config.AUTO_CLOSE_BROWSER:
|
||||
# force=True means force close, ignoring AUTO_CLOSE_BROWSER config
|
||||
# Used for handling abnormal exit or manual cleanup
|
||||
if self.launcher and self.launcher.browser_process:
|
||||
self.launcher.cleanup()
|
||||
else:
|
||||
|
||||
45
uv.lock
generated
45
uv.lock
generated
@@ -1,4 +1,5 @@
|
||||
version = 1
|
||||
revision = 1
|
||||
requires-python = ">=3.11"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12' and sys_platform == 'darwin'",
|
||||
@@ -863,6 +864,7 @@ dependencies = [
|
||||
{ name = "uvicorn" },
|
||||
{ name = "websockets" },
|
||||
{ name = "wordcloud" },
|
||||
{ name = "xhshow" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
@@ -900,6 +902,7 @@ requires-dist = [
|
||||
{ name = "uvicorn", specifier = "==0.29.0" },
|
||||
{ name = "websockets", specifier = ">=15.0.1" },
|
||||
{ name = "wordcloud", specifier = "==1.9.3" },
|
||||
{ name = "xhshow", specifier = ">=0.1.9" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1159,6 +1162,36 @@ wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycryptodome"
|
||||
version = "3.23.0"
|
||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276 }
|
||||
wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/5d/bdb09489b63cd34a976cc9e2a8d938114f7a53a74d3dd4f125ffa49dce82/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4", size = 2495152 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a7/ce/7840250ed4cc0039c433cd41715536f926d6e86ce84e904068eb3244b6a6/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae", size = 1639348 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/f0/991da24c55c1f688d6a3b5a11940567353f74590734ee4a64294834ae472/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477", size = 2184033 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/16/0e11882deddf00f68b68dd4e8e442ddc30641f31afeb2bc25588124ac8de/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7", size = 2270142 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/fc/4347fea23a3f95ffb931f383ff28b3f7b1fe868739182cb76718c0da86a1/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446", size = 2309384 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/d9/c5261780b69ce66d8cfab25d2797bd6e82ba0241804694cd48be41add5eb/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265", size = 2183237 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/5a/6f/3af2ffedd5cfa08c631f89452c6648c4d779e7772dfc388c77c920ca6bbf/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b", size = 2343898 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/9a/dc/9060d807039ee5de6e2f260f72f3d70ac213993a804f5e67e0a73a56dd2f/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d", size = 2269197 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/34/e6c8ca177cb29dcc4967fef73f5de445912f93bd0343c9c33c8e5bf8cde8/pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a", size = 1768600 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/1d/89756b8d7ff623ad0160f4539da571d1f594d21ee6d68be130a6eccb39a4/pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625", size = 1799740 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/5d/61/35a64f0feaea9fd07f0d91209e7be91726eb48c0f1bfc6720647194071e4/pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39", size = 1703685 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.5.2"
|
||||
@@ -1715,3 +1748,15 @@ wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/c0/bc14fd7fa96e5b544aac4e9e65b5dd6f753d72184da35e35eb0b24c4dde4/wordcloud-1.9.3-cp312-cp312-win32.whl", hash = "sha256:419acfe0b1d1227b9e3e14ec1bb6c40fd7fa652df4adf81f0ba3e00daca500b5", size = 291251 },
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/a0/b8fa5f2d7147a7675e2cab99108f7d8d524b67481f81f289cdb2b64ed1ab/wordcloud-1.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:2061a9978a6243107ce1a8a9fa24f421b03a0f7e620769b6f5075857e75aa615", size = 301393 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xhshow"
|
||||
version = "0.1.9"
|
||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||
dependencies = [
|
||||
{ name = "pycryptodome" },
|
||||
]
|
||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/9a/ed544bea5b6a3702fc232eb774817d7f03a678b5f2ac3d67d3c7698695e6/xhshow-0.1.9.tar.gz", hash = "sha256:5a17cb510e9ab3ca61ef8ce00bd4cae6cbce39bb7b5f8ef38a5a980011883245", size = 55126 }
|
||||
wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/7f/c0/2783314af317b7207422e94f74302dbe32c5c9a1641bcfe11c4c073b0b04/xhshow-0.1.9-py3-none-any.whl", hash = "sha256:c4b0d38f4746b8a3f9c08fcfe4628f81c887b27c76b7ba73fa61ff990d1839dc", size = 31289 },
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user