From 9b3b5c8329816a80cd7714ddcde165c5d3167ba8 Mon Sep 17 00:00:00 2001 From: mzl Date: Mon, 12 Jan 2026 19:07:00 +0800 Subject: [PATCH] ts --- __pycache__/main.cpython-311.pyc | Bin 0 -> 1905 bytes __pycache__/test_login.cpython-311.pyc | Bin 0 -> 7083 bytes app/__pycache__/__init__.cpython-311.pyc | Bin 163 -> 161 bytes app/__pycache__/auth_manager.cpython-311.pyc | Bin 11849 -> 11847 bytes app/__pycache__/config.cpython-311.pyc | Bin 4148 -> 4146 bytes app/__pycache__/log_manager.cpython-311.pyc | Bin 5495 -> 5493 bytes app/__pycache__/scraper.cpython-311.pyc | Bin 38282 -> 38280 bytes app/__pycache__/user_resolver.cpython-311.pyc | Bin 8976 -> 8974 bytes app/__pycache__/web.cpython-311.pyc | Bin 23255 -> 23240 bytes app/web.py | 16 +- ╩╣╙├╓╕─╧.md | 353 ++++++++++++++++++ 11 files changed, 362 insertions(+), 7 deletions(-) create mode 100644 __pycache__/main.cpython-311.pyc create mode 100644 __pycache__/test_login.cpython-311.pyc create mode 100644 ╩╣╙├╓╕─╧.md diff --git a/__pycache__/main.cpython-311.pyc b/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6375a481db3424a3c8e7f6a2ad91e95ff54deec6 GIT binary patch literal 1905 zcmbtU-BTM?6u+B2`Q%Gmt5AH{2T{V51k^fX0G&FJvExkR5N0~V%MK+ejpoZ`w^g2k z6D3e+>L|ms0jJb9q@@B6wor(}zp!!g!0Zf9u)E=jIQYbqXEzd!j(O?by(j1EcXIA; z@4ZWPbvA-yB<~AM(S+=2#>p`Jr5p{QGDm!5koYK{AxI5U_)7;3A$pLeG#>;ZWEeD{ zZj6&bqu=B+eXgUqS_APL(}-U_sY)=*;MJ$yEZEU)lh=~!=KcNWX=QdznOQlQm^es% zE8od5?TnhaDQ_m_Zyw6YpKJ$OjZ1b)G7U$SbaJe)pCY-nsa@36t%n_dY zJLsd*Xf2mLRAtmqHCi1@SH%Kp$>!UWI~yL;$k`-9gYs+40;MH z+Xr(i^8A{-5OXotcs}ZKI*%!rpsl!cWYpA({OhcX5uE6<6;{{f`Hb>3Sy)QoG1}kw zS(*G^&c^mPrV86@r9y|#zJ1FjxDnIoU=YIihWH_;FzV+<9sHL(qaoL~3{rb5)v9%YQE)ibB?!{(=c8tHPcPD2tWfMCnuS|VB zhxdZMzGd~^xO!&`SrA@F#Cb%l{HV-5Q&Zn5x0Xuf3fn1pGi{R~5WX6*(-K^ZjPeph zBOEVTgOL$7=)dj{Ub0&xW0VVoc~09TlI3#WN9@NQ??6fW)YFez?}dR2Z12T>$t+wC z3`e-IL~}xz)ArCKa)go`H#+%HRGYpV=f|R*Vb&QM3WOceuO!eE?iEb3;vBW%jT9p? zSQD+|p13FG*#)a;dm{%ed1w)#1ox?W=?RjVyq5a=gyK~T!haM4nbVz>=ym|17;MGl64!ZKtB|?{O zzLkU4JhY0?s+;fUz?BD=2rk{U=b$YQZ6dVEP!s=T*HW`vcY2!D#m=e5qSa_L{Z3H) zp^0Lp;7%bYl7(e8l33Pmmdq^ciwv_Y*MJe7;?jpP^Hheq?>lY66+mz$K5ggDbfVaqFZn2Pl1LO@&Et; literal 0 HcmV?d00001 diff --git a/__pycache__/test_login.cpython-311.pyc b/__pycache__/test_login.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2fda0043ce6986f79dc1682d29412381edf829b8 GIT binary patch literal 7083 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 delta 44 ycmX>eb25f&IWI340}vEn+{jhSBCMvLn^&Trl30?cUy)H9Ur>~vwRr~1dQAX0oetpu diff --git a/app/__pycache__/config.cpython-311.pyc b/app/__pycache__/config.cpython-311.pyc index f8337564cf9816f918d3c6637633e1d7bcb07dbb..0602bc0383e05a797bb355de9ba5197714b85466 100644 GIT binary patch delta 42 tcmdm@ut|YyIWI340}!NN*~rDqD=4R5l3M`6DdnbB1%`R?o3(hkm;v*`3#tGB delta 44 ycmdm_utkAuIWI340}#wVzmbcVS6D$mH?KrLC9xz?zapbJzMv>SYqJI~7c&49Yz)c( diff --git a/app/__pycache__/log_manager.cpython-311.pyc b/app/__pycache__/log_manager.cpython-311.pyc index 86e880c87cbd1c71f2c60c829700743728f0cc2d..a5cffbc108db4cb373477676e5a716fce9124daf 100644 GIT binary patch delta 42 ucmeya^;L^&IWI340}!NN*~s;fSx{NOB)0&BQ_4-N3Jmk&H~(X<6a)Yyw+>nW delta 44 ycmeyW^<9f=IWI340}yCj+Q{{gSy)v+H?KrLC9xz?zapbJzMv>SYx5uGNpPRMqJC~(iGE6ANuqv5Msa*WQGV8DA?C7408u6n AP5=M^ diff --git a/app/__pycache__/user_resolver.cpython-311.pyc b/app/__pycache__/user_resolver.cpython-311.pyc index d3f764a6ecbf164a6afce5e9d962f1769df60e4e..04c84f87c4a12a53d2b93e0fc66b9f7990ab27f0 100644 GIT binary patch delta 42 tcmbQ>*5}5xoR^o20SMBsY~<=<5mePL$t?iklycLm0>ixc&C6Lb5mwjF%`4GQNi0d!ugEBlFDS~-+Pst{Qw{(hQw|LP diff --git a/app/__pycache__/web.cpython-311.pyc b/app/__pycache__/web.cpython-311.pyc index cb14ebfdfa1485d4be25d051c1f7df88b9a27c85..d07d223dd26547592a6fc1e54fd293bb37b714be 100644 GIT binary patch delta 3040 zcmZ8jeNa@_72mtNZ;_7(8d&+%2Ss3w(PWftqIL`z2}G&_Vl=H;7WSzdm)-TgMZlQZ zH8BE$NOS!H@dFw@Oh}1+8qr|WPG=gM&a`Q&qj`;gXnD)>N7YW7+VLNI&V6Di58iLj zJ?Gqe&pr2d&hCv1)N$ z30s~U9p?wnH3nihW^fU79S?X`o7 z=yBG0Yqn!tqg~qYXr$@9__|arj-NOmZtsJbAl&fDzI&t1V^_|MceiTIpN@A0_uUC~ zhg%pPWi)grbn@P4%VfyUicd}vK746xI60$D`|w=keBaom(~vr1<{C*KKKm(FX%&VrpdRd}ky&E!8Ej>OsSUk?%Zcn4= z^NKF1*6DTGY=Ro?^!pt!H6YPUxL}$EpvHQ9+Z^EGWDnwV3s-=u1YS8GAOe3O!~Fz9 zy7+#{HFNUK%(ooyEiV3CbV z^K1{vD*}<`AmH*e7a_anB7gxvGDO)_UP$!;$!xt zHSEpARMwqTN{ZP;((^Iv(G#JLWm}h!^{_??+iC4gC__UzUk9OrrOv4!tC@FB{@gcF zF)fx#)FJF)=1#MQ^_|sLBpYM;zeatY(@gC&RQh2gW@+zL)LS|fwyNf(Ngr*ZxR(zbPXzALYRv%4?vCDCIL$W zR5f`dFAbso|1U|6ar)hy%5^ejVE>9mWq(JOv9atN;W&){%Hro2n~OnWi*l`O=lu2d zE*RL1bQyf8F|dKd1BC$jIthxYmobCeeA{Gt8Z6(k|IWWOmmj$T7clAJAkXLrs34Hn zKj)+hJutYV+|AiQid#Y4P!|oTUHp~y$#JOfvqpLp#yU|@f;={$0vox$BwiZD41#Yj z%H`}pUaBw%VnjKc*FY>m)b_I61wSSanY3VGJ}$HY&J;oePNf3?GS*DhV`{~^B##u3 z9Cgqo=qF%!z<$5rxa9~)Ho|WID2X*bGg}yj;Um`Z%%3b?kZ6dx7S0#WfhZ{L3%5m) zKK7p_nF)9d9%vS>80W6_ z`>^*q+%9=aoYM}#8_PR5Va5)rvEE0)c@GYaJL*-F-|bQ@z`ttg1ACl>CtwSkZC@_@ z0bF8~a{G`u!3iRr(qY)l6&Z!RPB0GMSm<;gFyK{Si?5TXaGL%RryzKg+7;)Gq@880 zEEc{2MZMx%Sx26GvJvnSS*7P4wQ8)?;CEBVpSxD#-40yZSz>{mTxF#N$uZZ_9brH7 z7ThIA*p|Y4;X4=~R*n^lM&QoKE7_z~x%J8dvV>QD2^foP7QO0u!JBBESMLgFjiyx|oUwjHq;PE7*BqH#;<(tfaUg!uoc5U||kD1a*b%_F|7cj=j|;njFy4AiHTwpSmJjD8S{c)D!nDM8_8^ksIY zBufZ`cv<J^?+k3G zZ?jXS*}^}-cvbnbw9S~nX(HkZHxMGMX59wzH#V}aGxJ}dn8FM-Muz&3c1hlVOuq-) zw~VeY3i9;Kf+5_(Tet+@`LSGvdb3`}DC-`3r+6o!OlYUB>O!AlUG7Es@iF)&{F|R& zL@at#s!+43|CFH$f2syrizzgTDi0$*wkO1wK) zqEv;zli)^)f47BD;&snS!(gbI{XXbl$I)^cp$B0QEf&3j^4>|YPttP;SgBZ1nFfkw zq_YlLPmv%LFP`F!(C=AOMFxpe`YL*jsnO&Oa^HaXPm{rza*ZV4H*#IF;@r5){6D$o BcfIb3g65@-G995$ z-?cQ!@;0N$YYBQF-&w|n;DMqLq@5lZDH<`gbjLgl*NSSZg!L90FC(ZU2oOZ(VHE0} zn^*)kx!mxgvx|k{it`(}{iG&Pq1e^H`ceHs!Q(^@5QGVyfc)YJdlX{DYpb3nVv&oj z#1Ty4qwa??S2bKM9${OBucu0Ou*V=%Qf*gI9ms;c#m>56nleH_2Yz*o>-m)=dRfgab$1~)_SX`OnJCHdHil-5``I6raKlPlFX*Wk9R$9j%*MC@g zjRlF|8bKPtuq#suRl}4WPbp)Ri#(Cyt)#K4ie^}2@pvFhnDPNQRlZ(6h`QJHKbN;M zHlQ!}y4Z$YwAl9$?y%f|%!Pw#T0x%V2~1~lW@K?Cm5dE4Xdq%LoLjZg6vqZ2@dL2m zS1lhx@tppy?;vHuH!D}lN09iXK2o`wRnXlAth|q`@7*(QO62P z$VbuY6Sz}#g@s_Qx>i1c9EmVf`6F{;x%FSNgM<$JcK_+?si3sCb9f51V4t~ z)VSq0k)DGuYG#|nj-Mc7E2r~|T;Z=0!NhSEUPcZrl*YPO`DwKHy?(8>k97cwed!#; z>mPk^{p`&3V?Q}<;jiJ?ynK={HhYMm{%QK5i{KO$v^_)e0PL>w$nT-}C;eF66tf>7 z_6&IHo7g|0zkWp_E!2mH68og7{9SmtzFaQu!k!{ssG% ze`=o~dozr~E0)T$IJD|}S8UH>XW>SJzkp7`pCK?W9XxCLYhS=|fEji!k_TPuNEj@s zQf2zg6y+bn%$k*~2!6F@r_6(>fY!B-On-}}=9w8m@#RkvLQT~Yw;*>&sFM$Wa@TFp0Rieqnm=e{%v92ud`=k?r-UAKmnv)`C zloFzv(Fvuh$WkMR$0o&8)!b+$t>{RMrpCFZ#G{G0x?rE%XxjY)h@SC0O~@Wmc216S zG=2^~U+1-p<0@cn49XW!pRKnxzHKW=(DVfz0{$H=_cjqpUPWWz|6>ze@6NqMYJy?? z$@L#v*byjf?vO7dk=D00r`hUzi(*c@6b{{hE`^@Aaw1+(OITQvjy)3$onAgZ(nmF)pL7d!Xjz_9Z4 zm|4N3;}r;)Gt|;ByO+1Ouyfp??RE0+2r zjYXt>N782mHwXqGvav?~0>xwc?v4Los-EpSJBJjtfT4~V^M;Ut;yH%#gz*@R8`49RzoA)IU?LWDezEFLqmqR}|i z^|#7iP^=eLhx#Dsa+J-1pKVGT>R(4JIPEe6(OmgU@HN=1>`Fo_X$Cadl?;`4gocbM~C7^nvTJcN#GKnIeD4#hc u2&aqZbR+z0n2MCMLj6eOq{WlPde}{g@Rn7wxHGKerbTc?dheDA+kXIA7POE6 diff --git a/app/web.py b/app/web.py index b5ff2c6..d053668 100644 --- a/app/web.py +++ b/app/web.py @@ -72,13 +72,15 @@ templates = Jinja2Templates(directory=os.path.join(config.BASE_DIR, "templates") @app.on_event("startup") async def on_startup(): - # 启动 Playwright + Browser(长期复用,减少反复启动带来的抖动) - app_state.playwright = await async_playwright().start() - app_state.browser = await app_state.playwright.chromium.launch( - headless=config.HEADLESS, - args=config.BROWSER_ARGS, - ) - log_manager.append("INFO", f"Browser launched (headless={config.HEADLESS})") + """ + 只做轻量初始化,不在启动时强制拉起浏览器。 + 原因: + - 很多环境首次运行未执行 `playwright install chromium`,如果启动即 launch,会导致 Web 直接起不来(你看到的“打不开”)。 + - 浏览器改为按需启动:第一次需要登录/解析/采集时再启动,失败也能在 UI 里看到明确错误。 + """ + app_state.playwright = None + app_state.browser = None + log_manager.append("INFO", "App started (browser will be launched lazily on demand)") @app.on_event("shutdown") diff --git a/╩╣╙├╓╕─╧.md b/╩╣╙├╓╕─╧.md new file mode 100644 index 0000000..25f1166 --- /dev/null +++ b/╩╣╙├╓╕─╧.md @@ -0,0 +1,353 @@ +# 小红书数据采集器使用指南 + +## 系统概述 + +这是一个基于 FastAPI + Playwright 的小红书数据采集器,具有 Web 可视化监控界面,支持多线程采集、实时进度展示和持久化日志记录。 + +## 系统特点 + +- 🌐 **Web 可视化界面**:实时监控采集进度和状态 +- 🔐 **真实扫码登录**:打开小红书官方登录页面,扫码登录后验证状态 +- 👥 **账号管理**:支持添加、删除用户账号 +- 📊 **实时日志**:分类显示操作日志,支持过滤和搜索 +- 🔄 **多线程采集**:支持单线程/多线程模式切换 +- 🐳 **Docker 部署**:容器化部署,支持自动更新 +- 📁 **数据持久化**:所有数据本地存储,重启不丢失 + +## 快速开始 + +### 1. Docker 部署(推荐) + +```bash +# 克隆或下载项目 +cd /path/to/demo + +# 构建并启动容器(使用18085端口) +docker-compose up -d --build + +# 查看容器状态 +docker-compose ps + +# 查看日志 +docker-compose logs -f +``` + +### 2. 本地运行 + +```bash +# 安装依赖 +pip install -r requirements.txt + +# 安装浏览器 +playwright install chromium + +# 创建数据目录 +mkdir -p data data/screenshots + +# 运行程序 +python main.py +``` + +### 3. 访问系统 + +打开浏览器访问:`http://localhost:18085` + +## 系统界面说明 + +### 1. 账号管理区域 + +![账号管理](./images/account-management.png) + +- **添加账号**:点击添加账号按钮,输入小红书用户 ID +- **刷新账号**:重新加载账号列表和状态 +- **登录状态**: + - 🔴 红色圆点:未登录 + - 🟢 绿色圆点:已登录 + - 显示剩余登录时间(小时:分钟) + +### 2. 控制面板 + +![控制面板](./images/control-panel.png) + +- **启动采集**:开始数据采集任务 +- **停止**:停止正在进行的采集任务 +- **清空日志**:清空所有历史日志 +- **导出数据**:下载采集到的数据文件 +- **模式选择**: + - 单线程:逐个处理账号 + - 多线程:同时处理多个账号(可配置线程数) +- **线程数**:多线程模式下的并发线程数(1-10) + +### 3. 进度监控 + +![进度监控](./images/progress.png) + +- **整体进度**:显示采集进度条和百分比 +- **活跃线程**:多线程模式下显示正在运行的线程数 +- **用户列表**:显示每个用户的采集状态 + - ✅ 已完成:显示采集的笔记数量 + - 🔄 进行中:显示当前处理的笔记数量 + - ⏳ 待处理:尚未开始处理的账号 + - ❌ 失败:采集失败的账号 + +### 4. 日志面板 + +![日志面板](./images/logs.png) + +- **日志级别过滤**:可选择查看 ALL/INFO/SUCCESS/WARNING/ERROR/DEBUG 级别的日志 +- **搜索功能**:搜索特定关键词的日志 +- **自动滚动**:开启后自动滚动到最新日志 +- **时间戳**:显示每条日志的具体时间 +- **线程标识**:多线程模式下显示日志来源线程 + +### 5. 错误截图 + +![错误截图](./images/screenshots.png) + +如果采集过程中遇到错误(如风控检测),系统会自动截图并保存: +- 截图保存在:`data/screenshots/` 目录 +- 在界面中显示错误截图缩略图 +- 可点击查看大图或下载所有截图 + +## 操作流程 + +### 1. 首次使用 + +```bash +# 1. 启动系统 +docker-compose up -d --build + +# 2. 访问 Web 界面 +# 打开 http://localhost:18080 + +# 3. 添加要采集的用户 ID +# 点击"添加账号"按钮,输入小红书用户 ID +# 格式:长字符串(如 5ef74296000000000100003a) +# 可从浏览器地址栏获取:https://www.xiaohongshu.com/user/profile/用户ID + +# 4. 真实扫码登录 +# 点击"打开登录页"按钮,会在新窗口打开小红书官方登录页面 +# 在打开的页面中使用小红书App扫描二维码完成登录 +# 登录成功后,返回本页面点击"验证登录"按钮 +# 登录状态有效期:24小时 +``` + +### 2. 配置采集参数 + +1. 选择采集模式: + - **单线程**:稳定但速度慢,适合少量账号 + - **多线程**:速度快,适合大量账号(推荐 3-5 个线程) + +2. 设置线程数(多线程模式): + - 建议:3-5 个线程 + - 过多线程可能触发风控 + +### 3. 开始采集 + +1. 确保已添加至少一个用户账号 +2. 确保已登录(显示绿色状态) +3. 点击"启动采集" +4. 系统会: + - 检查登录状态 + - 读取用户列表 + - 开始采集并实时显示进度 + +### 4. 监控采集 + +采集过程中可以: +- 查看实时进度和状态 +- 监控日志输出 +- 观察各线程工作状态 +- 随时停止采集(点击"停止") + +### 5. 数据导出 + +采集完成后可以: +- 查看每个用户的数据文件:`data/{用户ID}.json` +- 下载所有日志:点击"导出数据" +- 下载错误截图(如有) + +## 用户 ID 获取方法 + +### 方法1:浏览器地址栏 + +1. 打开小红书网页版 +2. 访问目标用户主页:https://www.xiaohongshu.com/user/profile/用户名 +3. 从浏览器地址栏复制用户 ID: + ``` + https://www.xiaohongshu.com/user/profile/5ef74296000000000100003a + ``` + 复制 `5ef74296000000000100003a` 部分 + +### 方法2:开发者工具 + +1. 在用户页面按 F12 打开开发者工具 +2. 切换到 Network(网络)标签 +3. 刷新页面 +4. 查找包含 "user" 或 "profile" 的请求 +5. 从请求 URL 中提取用户 ID + +## 数据文件说明 + +### 输入文件 + +``` +data/users.txt # 用户 ID 列表,每行一个 +``` + +### 输出文件 + +``` +data/ +├── auth_state.json # 登录状态缓存 +├── logs.json # 操作日志历史 +├── users.txt # 用户列表 +├── 5ef74296000000000100003a.json # 用户采集数据 +├── 5b6c1f9e0000000001000388.json # 用户采集数据 +└── screenshots/ # 错误截图目录 + ├── error_xxx1.png + └── error_xxx2.png +``` + +### 数据格式示例 + +```json +// data/{用户ID}.json +{ + "user_id": "5ef74296000000000100003a", + "last_updated": "2025-01-12T10:30:00", + "notes": [ + { + "title": "笔记标题", + "publish_time": "2025-01-10 14:30", + "location": "上海", + "comment_count": 128, + "like_count": 5234, + "collect_count": 892, + "note_url": "https://www.xiaohongshu.com/explore/xxx" + } + ] +} +``` + +## 常见问题 + +### Q1: 为什么提示"未登录"? + +A: 登录状态 24 小时后会自动过期,请: +1. 点击"模拟登录"重新登录 +2. 检查 `data/auth_state.json` 文件是否存在 + +### Q2: 采集速度很慢怎么办? + +A: 可以尝试: +1. 使用多线程模式(建议 3-5 线程) +2. 检查网络连接 +3. 避免在高峰期采集 + +### Q3: 如何避免被封禁? + +A: 系统已内置防护措施: +1. 拟人化延迟(随机 2-60 秒) +2. 平滑滚动加载 +3. 多线程错开启动 +4. 风控检测自动截图 +5. 建议不要设置过多线程 + +### Q4: 数据采集不全怎么办? + +A: 可能原因: +1. 网络不稳定导致加载失败 +2. 笔记数量过多,滚动加载不完整 +3. 小红书页面结构变化 + +### Q5: Docker 容器无法启动? + +A: 检查: +1. Docker 是否运行 +2. 端口 18080 是否被占用 +3. Docker Compose 配置是否正确 + +## 配置说明 + +### 环境变量 + +```env +# Web 服务器配置 +PORT=18080 +HOST=0.0.0.0 + +# 浏览器配置 +HEADLESS=false # 是否无头模式 +SCREENSHOT_ON_ERROR=true # 错误时截图 + +# 多线程配置 +MAX_CONCURRENT_THREADS=5 # 最大并发线程数 +THREAD_MIN_DELAY=2 # 线程间最小间隔(秒) +``` + +### 配置文件位置 + +```python +# app/config.py +DATA_DIR = "data" # 数据目录 +SCREENSHOT_DIR = "data/screenshots" # 截图目录 +MAX_CONCURRENT_THREADS = 5 # 最大并发线程数 +``` + +## 更新和维护 + +### 1. 自动更新(Docker) + +系统支持 Git 自动更新: +1. 代码提交到远程仓库 +2. 系统自动拉取最新代码 +3. 检测到 Python 代码变化时建议重启容器 + +### 2. 手动更新 + +```bash +# 拉取最新代码 +git pull origin master + +# 重新构建容器 +docker-compose build + +# 重启服务 +docker-compose up -d +``` + +### 3. 数据备份 + +重要数据请定期备份: +```bash +# 备份数据目录 +tar -czf backup-$(date +%Y%m%d).tar.gz data/ + +# 备份特定用户数据 +cp data/*.json backup-$(date +%Y%m%d)/ +``` + +## 技术支持 + +如果遇到问题: +1. 查看 Web 界面的日志面板 +2. 检查 Docker 容器日志:`docker-compose logs` +3. 查看 `data/logs.json` 文件 +4. 确保网络连接正常 + +## 版本更新日志 + +### v1.0.0 (2025-01-12) +- ✅ 实现基础采集功能 +- ✅ Web 可视化界面 +- ✅ 多线程支持 +- ✅ 实时日志系统 +- ✅ 账号管理功能 +- ✅ 模拟登录系统 +- ✅ Docker 部署支持 +- ✅ 数据导出功能 + +--- + +**注意**:本工具仅供学习和研究使用,请遵守小红书的使用条款和相关法律法规。 \ No newline at end of file