butubb
44cbe8e2aa
fix(monitor): 已登录时点「同步登录态为 Cookie」要干等 30 秒
...
Deploy VitePress site to Pages / build (push) Waiting to run
Deploy VitePress site to Pages / Deploy (push) Blocked by required conditions
顺序反了:原先先开页面、读二维码,读完才发现「已经登录、没有二维码」。而读二维码内部
会 wait_for_selector 等满 30 秒才放弃 —— 用户点一下按钮要干等半分钟,还白开一个标签页。
实测日志里就是 `Page.wait_for_selector: Timeout 30000ms exceeded`。
改成先问登录状态(那是一次接口调用,很快),已登录就直接返回,根本不碰页面。
新增测试守住这个顺序:已登录时 context.new_page 不得被调用。
2026-10-09 13:49:11 +08:00
butubb
de9ff58371
feat(monitor): 扫码同时存一份 Cookie,并把登录判定换成权威判据
...
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
用户问「现在是不是扫码就能自动获取 cookie」—— 不是,而且这正是那个面板显得没用的根源:
它只干了半件事,扫码**只写浏览器 profile,完全不提取 cookie**(qrlogin.py 里连一行
取 cookie 的代码都没有)。于是:
- CDP 开着时任务能用(复用 profile),但 Cookie 面板始终显示「未配置」
- CDP 一关,任务立刻断,因为库里那份 cookie 从来没被填过
现在扫码把两件事一起做了:写 profile(CDP 用)+ 存一份到库(Cookie 注入用)。
两种机制同时填上,开关怎么切都不断。cookie 只在内存里从 qrlogin 传到路由,不进响应体。
同时修掉一个同类 bug:监控侧的登录判定还在用页面里的 window.__INITIAL_STATE__,
而那是**页面加载那一刻的快照** —— 浏览器本来就登录着时它是对的,但扫码是加载之后
才登录的,快照不会翻转,表现为「扫了码却一直停在二维码上」。运营模块踩过同一个坑,
当时只修了那一处。现在两边统一为:拿 cookie 问后台接口「我是谁」。顺带不再需要页面导航,
检测变轻了。
前端:已登录时按钮原先被我藏起来了,面板于是变成一块只能看、不能操作的区域 ——
用户的原话是「没用」。现在两种状态都给按钮,含义不同:未登录=取二维码,
已登录=把当前登录态同步成 Cookie。
测试:tests/test_qrlogin.py 重写(stub 从页面探针换成后台接口),新增「成功会话必须
交出 cookie」「只能取一次」两例。
2026-10-09 13:46:48 +08:00
butubb
bef0a4fbde
fix(creator): 扫码成功却一直停在二维码上 + 运营改为左右布局
...
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
【扫码不完成】根因是判据本身。原先读页面里的 window.__INITIAL_STATE__,而那是
页面加载那一刻的快照:监控那边的同一探针能用,是因为那台浏览器的页面加载时就已经
登录了;而扫码是加载之后才登录的 —— SPA 内部确实登进去了,但初始快照不会翻转,
于是检测永远等不到。
改成拿 cookie 直接问创作者后台 /api/galaxy/user/info 我是谁。实测这个判据很干净:
游客也会拿到 a1(所以签名算得出来),但接口直接回 401 无登录信息;只有真正登录了
才返回 user_id。所以「有 a1」什么都证明不了,后台认了才算。
顺带按 5 秒节流 —— 前端每 2 秒问一次,没必要每次都打后台接口。
【弹窗不关】成功后不自动关闭,停在二维码上会让人以为没成功。现在显示账号卡片与原话
提示,1.6 秒后自动关闭并提供一个「完成」按钮。
【已完成结果会残留】take_cookie 取走 cookie 就拆会话,而在飞的轮询会看到 _current 为空
回报 idle,把已显示的成功能擦掉。现在把结果记在模块里重复返回,关闭弹窗时清掉 ——
否则下次打开会立刻显示上次的成功。
【布局】按用户要求改成左右两栏(左账号列表、右数据面板),与监控统一,取消二级菜单。
未选过时默认选中第一个,右栏不会一开始就是空的。
测试:tests/test_creator_login.py 新增 7 例,含「游客会话永不完成」「接口不打满每次轮询」
「临时上下文用完必须关掉」。
2026-10-07 16:40:24 +08:00
butubb
c2b310c7bf
feat(creator): 新增「运营」模块 —— 多账号扫码登录与创作者后台数据
...
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
侧边栏在「监控」右边加了「运营」:账号列表 → 点进二级详情看该账号的数据。
【为什么是独立模块而不是监控的子视图】两者形状不同:监控是公开数据(点赞/收藏/评论/分享)的每轮快照+差分;运营是创作者后台按日期给出的曝光/观看/完播率/涨粉。凭据不同、采集方式也不同 —— 那边要浏览器登录态,这边是纯请求。硬塞进同一个模型会同时污染两边。
【扫码登录的关键差异】监控的扫码把登录态写进浏览器默认 profile(爬虫要复用)。运营要的是 cookie 字符串(纯请求够用),所以每次登录开一个**临时上下文**,扫完取出 cookie 就丢弃 —— 登第二个账号不会把第一个顶掉,也不影响监控那个登录态,十个账号互不干扰。
【决策依据】tools/probe_creator_api.py 的 Phase 0 实测:签名可自造(XYW_:MD5 → base64 → AES-128-CBC,与 xhshow 内置实现常量逐字节一致);主站 cookie 即可认证创作者后台;接口与参数已与真实页面对齐。
后端:
- api/creator/models.py: creator_account / creator_note_stat。**复用 MonitorBase**,这样 create_all 与上一轮改成元数据驱动的 _ensure_columns 会自动覆盖新表
- api/creator/signing.py: XYW_ 签名,带三条实测结论(url= 前缀、appId=ugc、401 与 406 的区别)
- api/creator/client.py: 纯 httpx 客户端。字段名尚未亲眼验证过,所以写成多别名匹配;解析不出来存 None 而非 0
- api/creator/service.py: 账号 CRUD 与同步。cookie 绝不进入对外结构,只给 has_cookie
- api/creator/login.py: 临时上下文的扫码登录
- api/routers/creator.py: 8 条路由,全部带鉴权
前端:
- 侧边栏「运营」+ OperationView(账号列表 → 二级详情)+ AddAccountDialog
- 权限状态显眼呈现:pending 时照抄后台原话「已为您申请数据权限,次日可查看」,并说明此时同步返回 0 条是正常的,不是采集失败
测试:tests/test_creator_client.py 新增 48 例,含「cookie 不得出现在对外结构里」这条不变量,以及权限未生效时空壳响应的处理。
2026-10-07 16:30:45 +08:00
butubb
6eff6fcc83
fix: 扫码登录状态可独立查询 + 趋势图改回自适应纵轴
...
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
【登录反馈】实测这台浏览器 loggedIn=true,其实早就登录成功了;看不到反馈是判据和设计的问题:
1. 判据不可靠。原先靠 web_session 的值变化判断——对照组显示:一个全新的空 profile 首次访问小红书就会被发一个 web_session,所以「有这个 cookie」什么都证明不了。可信信号是页面自己的 __INITIAL_STATE__.user.loggedIn,但它是 Vue 响应式引用,必须 .value 解包(这就是前面探针读到 [object Object] 和 None 的原因)。
2. 状态绑死在临时会话上。扫码会话是内存状态,进程一重启就没(部署、崩溃都算),面板于是悄悄退回初始态——一次成功扫码看起来像什么都没发生。
改法不是让会话活得久,而是把「登没登录」变成随时可查、与会话无关:
- 新增 GET /api/monitor/login/state,直接问浏览器要答案,带 5 秒缓存;force=true 先重载页面再读,用于状态陈旧
- qrlogin 改为常驻 Playwright 客户端 + 复用同一个标签页,并在重启后认领浏览器里已存在的 xhs 标签页,避免堆孤儿页
- 把「读不到状态」与「未登录」分开——前者显示具体错误,不再悄悄显示成未登录
- 面板顶部常驻显示登录态与昵称,带「重新检测」按钮;扫码成功后自动翻转
【趋势图】上一轮改过头了。dataviz 规范里没有「折线图必须从 0 起」这条——基线相关的条文全是讲柱状图的(柱状图用长度编码数值,不从 0 起比例就是错的;折线图用位置编码,轴只需如实框住数据)。改回自适应,保留上一轮修好的左侧刻度栏让范围始终可见;步长收敛到 1/2/5×10ⁿ,全平序列撑开一档避免除零。
2026-10-07 15:42:58 +08:00
butubb
e608b51210
test: 修正列渲染断言——SQLAlchemy 会对保留字加反引号
...
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
monitor_run.trigger 是 MySQL 保留字,渲染出来是 `trigger`。这比之前手写的列清单更正确:
清单里的裸 trigger 会直接语法错误。断言改为剥掉引号后比对列名。
2026-10-07 15:37:08 +08:00
butubb
d937ff5fe6
fix(db): _ensure_columns 改为按模型元数据推导,并补上漏加的调度字段
...
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
上一个提交加了 4 个调度字段,却没在 _ADDED_COLUMNS 里登记,后果是生产环境:
- 任务列表接口报 Unknown column,UI 打不开任务列表
- 调度器每 20 秒 tick 一次炸一次,定时任务完全不会触发
最阴险的是启动完全正常——能连库、能起来,只是随后每条查询都失败。
- _ensure_columns 不再遍历手写清单,改为遍历 MonitorBase.metadata.sorted_tables,
从根上消掉「加了字段忘了登记」这类漏
- 新增 _column_ddl:用 CreateColumn 渲染类型与可空性,并给 NOT NULL 列补 DEFAULT。
模型的 default= 是 ORM 侧行为、不会进 DDL,而给已有数据的表加 NOT NULL 列必须有值,
否则能否成功取决于服务端 sql_mode
- 主键列跳过:MySQL 不允许 AUTO_INCREMENT 与 DEFAULT 共存
新增 tests/test_monitor_column_migration.py 守住:每个 NOT NULL 列都必须能生成带
DEFAULT 的合法 ALTER。
2026-10-07 15:35:40 +08:00
butubb
8f4e5586e9
feat(schedule): 任务支持「每天定时 / 每周定时」,用选择器而不是手写 cron
...
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
- schedule.py: 新增调度计算模块(纯函数,便于单测)。三种模式:interval(每 N 分钟)/ daily(选钟点)/ weekly(选星期 + 钟点)
- 钟点模式是「固定时刻」而非「固定延迟」——从日历重算,所以某轮跑晚了不会把之后每一轮都拖晚。interval 保持原语义:从上一轮开始计时
- 抖动只加给 interval。给「每天 9:00」也加抖动就成了 9:00–9:01 随机触发,操作者选的时间被悄悄改掉,只会像 bug
- 模型 / schema / service: 新增 schedule_mode / schedule_hours / schedule_days / schedule_minute。时钟字段存逗号分隔文本——几个小整数、永远整体读写,单开一张表只会换来 join。interval_minutes 保留且仍是默认值,已有任务不受影响
- service: 改动任何调度字段都按合并后的状态重算 next_run_at。重新启用也算改动,否则停用一个月再打开会带着一个月前的 next_run_at,一保存就立即触发
- 前端: 运行方式三选一 + 小时/星期胶囊多选 + 分钟下拉,并实时预览结果句子。任务卡片改显示后端拼好的 schedule_label,避免列表和编辑器对同一计划给出两种说法
- 校验: 钟点模式至少选一个时间,按周至少选一个星期
tests/test_schedule.py 新增 24 个用例,含「恰好等于当前时刻的档位归属下一天」这个会让调度器自循环的边界。
2026-10-07 15:33:17 +08:00
butubb
37ca1b1cd6
feat: CDP 接管开关 + 扫码登录面板 + Docker 部署
...
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
- runner: enable_cdp_mode 从硬编码 False 改为系统设置 cdp_enabled。服务器部署下爬虫接管已开启远程调试的 Chrome(默认 9222),复用其 profile 登录态;本机桌面默认仍为关,行为不变
- qrlogin: 新增 CDP 扫码登录。Chrome 在服务器上跑于 Xvfb,show_qrcode 依赖的 PIL 桌面看图程序不存在,二维码无处可显示;改为经 CDP 从页面取出二维码交给 WebUI 渲染。刻意复用 browser.contexts[0](新建 context 是无痕 profile,扫了也白扫),且绝不调用 browser.close()(会连带关掉操作者自己的 Chrome)
- webui: 设置页新增扫码面板,替换原本跳到采集页看终端二维码的入口
- Dockerfile / .dockerignore / docker-compose.yml: 服务器部署。host 网络是必需而非图省事——容器里 127.0.0.1:9222 必须落到宿主机回环
- UPSTREAM.md: 补充 gitcode 镜像,用于 GitHub 大包传输必断时补历史
2026-10-07 10:41:11 +08:00
butubb
4e60524f37
feat: 监控面板 / 登录鉴权 / 多平台切换 / MySQL
...
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
在上游 MediaCrawler 之上新增一层:
- 监控层 api/monitor/ —— 多博主/多笔记的定时采集、指标快照差分、报表、
企业微信通知。每轮采集写入独立目录,差分才成立。
- WebUI 登录鉴权 api/auth.py —— PBKDF2 口令 + 服务端会话,/api 全接口防护。
WebSocket 单独加依赖:BaseHTTPMiddleware 对 ws 作用域直接放行,覆盖不到。
- 全局平台切换 + 能力矩阵 —— 如实区分「爬虫模块支持」与「监控层已接线」,
未接通的平台直接拒绝建任务,而不是静默跑空。
- 监控库改用 MySQL 5.7(可回退 SQLite 供测试):逐表强制 utf8mb4
(服务端与库默认都是 latin1),启动校验所连 schema 以防写错库,
连接池 recycle + pre_ping 应对 MySQL 的 8 小时空闲断连。
修复上游缺陷:
- xhs/core.py: 主页抓取失败会跳掉整个博主,导致一条作品都抓不到,
而那份资料只喂给一个空函数。改为尽力而为,失败不中断。
- xhs/login.py: cookie 登录只注入 web_session,冷启动签名会失败。
新增 INJECT_ALL_COOKIES 开关(默认关闭,原有行为不变)。
- requirements.txt: 补上 websockets。它在上游 pyproject.toml 里有声明、
这里漏了,导致 uvicorn 没有 WebSocket 能力,实时日志流从未工作。
改动过的上游文件清单及合并方式见 UPSTREAM.md。
测试:492 passed(另有 1 个既有的 Windows/gbk 上游测试失败,与本改动无关)
2026-10-07 09:58:40 +08:00
程序员阿江(Relakkes)
380b426000
fix(dy): 补上 ArgusSecurityPlugin 要求的 x-tt-argus 请求头
...
抖音在边缘网关新挂了 ArgusSecurityPlugin,对 aweme/detail、aweme/post 这批接口做
业务前置校验:缺少 x-tt-argus 时直接 403,响应体为
"Blocked by ArgusSecurityPlugin Uifid Not Found";只补 uifid 参数但仍没有这个头
则是 "... Signature Not Found"——后者很容易被误判成 a_bogus / verifyFp 的问题。
网关当前不校验该头取值,传固定字符串即可(实测 "1" 就够)。
参考 https://github.com/Johnserf-Seed/f2/issues/443
注意这是权宜之计:网关哪天升级到真校验该值,会重新出现 Signature Not Found,
届时需要改为页面内注入 JS 让抖音自带 SDK 补齐 Argus 头。
验证:真实 cookie 下 get_video_by_id 恢复可用。
- client.py: 默认请求头补 x-tt-argus,cookie 有 UIFID 时带 uifid 头
(两种都没有则不发,避免被当成「有但为空」)
- 新增 tests/test_douyin_argus_header.py(不发网络请求)
2026-09-19 13:25:59 +08:00
程序员阿江(Relakkes)
c7e6c9fdc0
feat(ks): 支持分享短链 /f/<token> 形式的视频输入
...
快手分享短链 https://www.kuaishou.com/f/X9Idt15MQb9L2cv 路径里是 share_token
而不是视频 ID,它只做 302 跳转,真实地址在 Location 里。原先的解析器只认
/short-video/<id> 和纯 ID,遇到短链会抛 ValueError 被 continue 掉——只有一行
ERROR 日志,看起来像"爬了但没数据"。
- help.py: 新增 /f/<token> 分支,返回 url_type="short"(token 不是视频 ID,
必须跟随重定向),并补上第三种形式的文档
- client.py: 新增 resolve_short_url,GET 时 follow_redirects=False,读
301/302/303/307/308 的 Location
- core.py: url_type == "short" 时先解析短链再解析一次,失败则跳过该条
- 新增 tests/test_kuaishou_url_parse.py(6 条,不发网络请求)
验证:两条真实短链分别解析到 3xyziwesje8e9jg / 3xbbkdxtxqm8sae,详情均成功;
带 query 的标准视频页不会被误判成短链。
2026-09-18 17:23:20 +08:00
程序员阿江(Relakkes)
1e1ae64cb4
fix(ks): 视频不可用时 photo 为 null 导致整轮爬取崩溃
...
快手对已删除/私密/不存在的视频,返回的 visionVideoDetail 里 photo/author 是
null——key 存在、值是 null。而 detail.get("photo", {}) 只在 key 缺失时给默认值,
key 存在且为 null 时拿到的仍是 None,紧接着的 photo.get(...) 抛 AttributeError。
该异常不在 get_video_info_task 的 except 列表里(只 catch DataFetchError /
KeyError),会穿过 asyncio.gather 直接把整轮爬取带崩。
- core.py: 用 (x or {}) 兜住 null;photo 为空时打 WARNING 并跳过该视频,
不再返回半残的 detail 让下游存空记录、再去下载媒体
- 新增 tests/test_kuaishou_unavailable_video.py(不发网络请求)
验证:photo=null / photo 缺失 / visionVideoDetail 缺失 均返回 None;
真实 cookie 端到端确认不可用视频被跳过、正常视频照常返回详情。
2026-09-18 17:18:01 +08:00
程序员阿江(Relakkes)
cf513e70a1
fix(dy): 补上 detail 接口的 uifid/verifyFp 风控参数
...
抖音 detail 接口的 Argus 风控要求 uifid / verifyFp / fp 三个参数,缺一则直接
403:响应体为 "Blocked by ArgusSecurityPlugin Uifid Not Found",补上 uifid 但
verifyFp 不对时换成 "... Signature Not Found"。原实现只传 aweme_id,于是
get_aweme_detail 全线失败,详情和媒体都拿不到。
- 三个参数取自浏览器 cookie:uifid 用 UIFID(缺失时退到 UIFID_TEMP),
verifyFp/fp 用 s_v_web_id。必须用 cookie 里的 s_v_web_id——实测 uifid 搭配
自生成的 verifyFp 会被判 Signature Not Found,两者需要同源
- 新增 tests/test_douyin_aweme_detail_params.py(不发网络请求)
验证:抓包比对真实浏览器发出的 detail 请求,参数逐项一致;真实爬取中详情与
74MB 视频均下载成功。
2026-09-18 17:00:11 +08:00
程序员阿江(Relakkes)
eec25bb0a0
fix(xhs): 适配上游 EF* 分档,修复视频只下到封面
...
小红书把 DASH 分档名从按编码命名(h264/h265/av1)改成了按内部档位命名
(EF4/EF5/EF6/EF7),同时 video.consumer 里的 origin_video_key 也没了。
extract_video_urls 写死 stream["h264"],两条路都取不到地址,于是静默返回
空列表:笔记照常入库,但 video_url 为空、媒体只下到封面,全程不报错。
- media.py: 遍历 stream 下所有列表型分档,不再写死分档名;同一分档内含多个
分辨率(720P→4K),按 (height, avg_bitrate) 降序,最高档作主地址,其余作为
备用地址交给下载器回退;origin_video_key 分支保留
- 测试 fixture 换成真实响应结构,旧的 h264 结构另存一份确保兼容性不被改坏
验证:真实响应下旧实现取到 0 条、新实现 5 条;真实下载得到 4K 视频
(54079413 字节 / hevc 3840x2160 / 281.87s),旧结构与图文笔记行为不变。
2026-09-18 16:10:18 +08:00
程序员阿江(Relakkes)
c03ab60aac
fix(bili): 登录态判定改为校验 cookie 有效性,避免静默降级到 480P
...
check_login_state 原先只判断 SESSDATA 是否存在。过期会话的 SESSDATA 会一直
留在浏览器里,于是 pong 报"账号未登录"后进入的登录流程会被立刻判成成功:
既不等待扫码,又把这份死 cookie 灌给 API client。
后果是隐蔽的:详情和评论两个接口不要求登录,照常爬到;而 playurl 依据
Cookie 决定清晰度,未登录状态会静默封顶在 480P,不报任何错。
- login.py: 抽出 is_login_cookie_refreshed(),要求 SESSDATA 被扫码换发成新值
才算登录成功;进入登录流程前记录旧值,残留死 cookie 时输出告警
- core.py: 登录流程结束后补一次 pong 校验,仍失败则明确报错退出,
不再带着未登录状态跑完全程
- 新增 tests/test_bilibili_login_state.py 覆盖上述分支
2026-09-18 15:48:06 +08:00
程序员阿江(Relakkes)
0ca7b29cf0
feat(media): 重构媒体下载,支持 xhs/dy/ks/bili/wb 五平台
...
旧实现只覆盖 4 个平台,且把整个文件读进内存、无重试与完整性校验,
代码按平台复制粘贴了 4 份。本次用统一下载器替换:
- 新增 media_downloader/:流式写入、Range 续传、指数退避重试、大小校验、
路径穿越防护;B 站 DASH 音视频分轨下载后交由 ffmpeg 无损合流
- 新增 media_platform/<平台>/media.py:从平台原始响应提取媒体地址,
与下载器解耦;快手首次接入下载能力
- 开关:config.ENABLE_GET_MEDIA 与 --get_media,并打通 API/WebUI;
同时修正旧配置项 ENABLE_GET_MEIDAS 的拼写
- 落盘按帖子聚合:{SAVE_DATA_PATH 或 data}/{platform}/media/{内容ID}/
- B 站装好 ffmpeg 时走 DASH 最高画质,否则降级 mp4 直链(产物 video-durl.mp4,
避免低清文件阻塞后续的高清路径)
- 删除 4 个 *_store_media.py、AbstractStoreImage/Video 及各 client 的媒体 GET 方法
媒体下载失败只记录日志,不中断爬取主流程。
2026-09-17 22:54:22 +08:00
程序员阿江(Relakkes)
d594c20c13
fix(xhs,bilibili): 修复访问受限异常击穿与评论采集边界问题
...
xhs: PR #958 把 IPBlockError / PlatformAccessError 加入 request() 的
retry_if_not_exception_type 后,tenacity 会直接重抛原异常而不再包装成
RetryError,core 层的 except 分支接不住,单条笔记被限流会让整批
asyncio.gather 抛出,同批已抓取但未入库的数据全部丢失。
- get_note_detail_async_task / get_creators_and_notes 捕获访问受限异常,
记录明确日志后跳过当前条目,恢复原有的"跳过并继续"语义
- 移除 request() 中已不可达的 IP_ERROR_CODE 分支
bilibili: 修复 get_video_all_comments 的两处翻页边界问题
- is_first_page 改为独立标志,接口返回 next=0 且 is_end=False 时
不再把后续页误判为首页而重复注入置顶评论
- result 无条件累加,否则开启楼中楼抓取时循环守卫永不推进,
max_count 完全失效并可能死循环;截断提前到抓取楼中楼之前,
避免为已被丢弃的评论抓子评论
2026-08-11 18:10:16 +08:00
程序员阿江-Relakkes
3c25521bbb
Merge pull request #958 from ottercoconut/agent/fix-xhs-raw-response-errors
...
fix(xhs): 在返回原始 HTML 前识别访问限制
2026-08-11 18:00:22 +08:00
ottercoconut
508abc504f
补全小红书响应与重试回归测试
2026-08-11 17:52:54 +08:00
ottercoconut
6937b12738
修复小红书原始响应错误分类与重复重试
2026-08-11 16:45:49 +08:00
muzimu
17c6d386b8
fix(bilibili): 修复缺失置顶评论
2026-08-08 18:01:26 +08:00
程序员阿江(Relakkes)
a06273ea6c
fix: 将平台业务ID统一改为String并自动建表
...
- 抖音、B站、快手、微博的帖子/视频/评论ID从BigInteger改为String,
避免PostgreSQL下字符串写入BIGINT报错及未来ID溢出风险
- B站dynamic_id改为String,修复API返回id_str被强转int导致的精度丢失
- 知乎提取器对content_id/question_id显式str()转换
- main.py启动数据库保存模式时自动建表,无需手动--init_db
- 同步更新相关老化测试
2026-07-01 23:03:43 +08:00
程序员阿江(Relakkes)
9f4f8bf768
refactor: 教学版移除全平台用户个人信息采集与持久化
...
- 用户 ID 转为匿名 creator_hash,昵称中间脱敏,IP/头像/主页/签名/性别不再采集
- 覆盖 xhs/weibo/bilibili/douyin/kuaishou/tieba/zhihu 7 个平台
- 删除 7 张 creator 档案 ORM 表,15 张内容/评论表新增 creator_hash 列
- B 站禁用粉丝/关注/联系人列表抓取
- 新增 tools/user_hash.py 与 4 个平台的 mock+SQLite 端到端测试
测试: pytest tests/test_no_user_info.py tests/test_weibo_no_user_info.py tests/test_douyin_no_user_info.py tests/test_kuaishou_no_user_info.py (21 passed)
2026-07-01 13:09:55 +08:00
程序员阿江(Relakkes)
c9a111be73
fix: 修复已有浏览器 CDP 连接
2026-06-18 17:22:38 +08:00
程序员阿江(Relakkes)
8e93438fe5
Keep PR 900 overrides bounded and opt-in
...
The PR adds API limit overrides and static proxy support, but the review found that the default proxy provider changed to an invalid static placeholder and the new API fields accepted unbounded values. This keeps the existing proxy default intact, makes static proxy explicit via config or CLI, validates API limit ranges, and adds focused regression coverage for both paths.
Constraint: PR branch must remain contributor-branch compatible and avoid adding dependencies
Rejected: Keep static as the default provider | breaks existing --enable_ip_proxy defaults with an invalid placeholder URL
Rejected: Accept arbitrary integer limits | lets API callers request negative or excessive crawl sizes
Confidence: high
Scope-risk: narrow
Directive: Do not change proxy provider defaults when adding new providers; new providers should be opt-in and covered by provider-specific tests
Tested: uv run pytest tests/test_api_limits.py tests/test_static_proxy_provider.py
Tested: uv run pytest tests
Tested: uv run pytest test/test_utils.py
Tested: uv run python -m compileall api cmd_arg config proxy tests
Tested: git diff --cached --check
Not-tested: Live crawler run against external platforms or real proxy vendor endpoints
2026-05-29 21:27:52 +08:00
钟保罗
ec432eb63e
feat: 启动任务接口添加帖子/视频数量与评论数量覆盖支持
2026-05-19 20:57:07 +08:00
程序员阿江(Relakkes)
f328ee35b5
fix: restore Tieba crawling after PC page rewrite
...
Tieba search, detail, comments, creator, and forum-list pages now rely on the current signed PC JSON APIs instead of brittle HTML selectors. The CLI also maps Tieba detail and creator arguments into the platform-specific config so command-line runs exercise the intended mode.
Constraint: Tieba PC pages no longer expose stable HTML structures for search, creator, and forum-list extraction
Constraint: Current PC APIs require browser cookies, tbs, and the web client signing convention
Rejected: Keep expanding HTML selectors | search and creator pages returned large documents with empty parsed results after the redesign
Confidence: high
Scope-risk: moderate
Directive: Do not replace these API paths with page HTML parsing without re-verifying the current Tieba network requests
Tested: uv run pytest tests/test_tieba_client_pagination.py tests/test_cmd_arg_tieba.py tests/test_tieba_extractor.py -q
Tested: uv run python -m py_compile cmd_arg/arg.py media_platform/tieba/help.py media_platform/tieba/client.py media_platform/tieba/core.py tests/test_cmd_arg_tieba.py tests/test_tieba_client_pagination.py tests/test_tieba_extractor.py
Tested: uv run main.py --platform tieba --type search --keywords 编程兼职 --get_comment false
Tested: uv run main.py --platform tieba --type detail --specified_id 9835114923 --get_comment true --max_comments_count_singlenotes 3
Tested: uv run main.py --platform tieba --type creator --creator_id https://tieba.baidu.com/home/main?id=tb.1.6ad0cd4a.7ZcjVYWa7UpHttCld2OppA --get_comment false
Not-tested: Second-level Tieba comment API migration; this path still uses the existing /p/comment HTML parser
Not-tested: Full pytest suite has one pre-existing unrelated XHS Excel store assertion failure
2026-04-30 18:20:46 +08:00
程序员阿江(Relakkes)
0282e626c9
feat: 新增 JSONL 存储格式支持,默认存储格式改为 jsonl
...
JSONL(JSON Lines)每行一个 JSON 对象,采用 append 模式写入,
无需读取已有数据,大数据量下性能远优于 JSON 格式。
- 新增 AsyncFileWriter.write_to_jsonl() 核心方法
- 7 个平台新增 JsonlStoreImplement 类并注册到工厂
- 配置默认值从 json 改为 jsonl,CLI/API 枚举同步更新
- db_session.py 守卫条件加入 jsonl,避免误触 ValueError
- 词云生成支持读取 JSONL 文件,优先 jsonl 回退 json
- 原有 json 选项完全保留,向后兼容
- 更新相关文档和测试
2026-03-03 23:31:07 +08:00
程序员阿江(Relakkes) and Claude Opus 4.5
157ddfb21b
i18n: translate all Chinese comments, docstrings, and logger messages to English
...
Comprehensive translation of Chinese text to English across the entire codebase:
- api/: FastAPI server documentation and logger messages
- cache/: Cache abstraction layer comments and docstrings
- database/: Database models and MongoDB store documentation
- media_platform/: All platform crawlers (Bilibili, Douyin, Kuaishou, Tieba, Weibo, Xiaohongshu, Zhihu)
- model/: Data model documentation
- proxy/: Proxy pool and provider documentation
- store/: Data storage layer comments
- tools/: Utility functions and browser automation
- test/: Test file documentation
Preserved: Chinese disclaimer header (lines 10-18) for legal compliance
🤖 Generated with [Claude Code](https://claude.com/claude-code )
Co-Authored-By: Claude Opus 4.5 <[email protected] >
2025-12-26 23:27:19 +08:00
程序员阿江(Relakkes)
6e858c1a00
feat: excel store with other platform
2025-11-28 15:12:36 +08:00
hsparks.codes
324f09cf9f
fix: Update tests to handle openpyxl color format and ContextVar
...
- Fix header color assertion to check only RGB values (not alpha channel)
- Remove ContextVar mock as it cannot be patched in Python 3.11+
- All 17 tests now passing successfully
2025-11-28 05:04:00 +01:00
hsparks.codes
46ef86ddef
feat: Add Excel export functionality and unit tests
...
Features:
- Excel export with formatted multi-sheet workbooks (Contents, Comments, Creators)
- Professional styling: blue headers, auto-width columns, borders, text wrapping
- Smart export: empty sheets automatically removed
- Support for all platforms (xhs, dy, ks, bili, wb, tieba, zhihu)
Testing:
- Added pytest framework with asyncio support
- Unit tests for Excel store functionality
- Unit tests for store factory pattern
- Shared fixtures for test data
- Test coverage for edge cases
Documentation:
- Comprehensive Excel export guide (docs/excel_export_guide.md)
- Updated README.md and README_en.md with Excel examples
- Updated config comments to include excel option
Dependencies:
- Added openpyxl>=3.1.2 for Excel support
- Added pytest>=7.4.0 and pytest-asyncio>=0.21.0 for testing
This contribution adds immediate value for users who need data analysis
capabilities and establishes a testing foundation for future development.
2025-11-28 04:44:12 +01:00