# 视频号(weixin_video)同步作品逻辑说明 ## 一、整体流程 ``` 作品管理「同步作品」 / 任务队列 sync_works → WorkService.syncWorks(userId, accountId?, platform?) → 按账号筛选后,对每个账号调用 HeadlessBrowserService.fetchAccountInfo(platform, cookies, ...) → 若平台在 supportedPlatforms 内(含 weixin_video),优先用 Python 拉作品 → fetchWorksViaPython('weixin_video', cookies, onProgress) → 请求 Python 服务 POST /works,platform 映射为 'weixin' → Python platforms/weixin.py WeixinPublisher.get_works(cookies, page, page_size) → 返回 works + total + has_more,Node 据此分页循环 → 拿到全量 works 后,再调 fetchAccountInfoWithPlaywright 补账号信息(头像、粉丝等) → WorkService 用返回的 works 与 DB 做比对:新增/更新/删除本地作品 ``` ## 二、Node 端(HeadlessBrowserService) - **平台映射**:`weixin_video` → 请求 Python 时用 `platform: 'weixin'`(`pythonPlatform = platform === 'weixin_video' ? 'weixin' : platform`)。 - **分页方式**:视频号**不用**游标分页,用**页码**: - `useCursorPagination = platform === 'xiaohongshu' || platform === 'douyin'` → 视频号为 false。 - 因此 `pageParam = pageIndex`(0, 1, 2, ...),每次请求带 `page`、`page_size`。 - **每页条数**:非小红书/非抖音统一 `pageSize = 50`。 - **停止条件**:`!result.has_more || pageWorks.length === 0 || newCount === 0` 时结束循环。 ## 三、Python 端(platforms/weixin.py) - **入口**:`get_works(cookies, page=0, page_size=20)`,由 `run_get_works` 调用(app.py 的 /works 路由传 page、page_size)。 - **当前实现要点**: 1. 打开固定页面:`https://channels.weixin.qq.com/platform/post/list`,等待 `div.post-feed-item`。 2. 用 `self.page.locator('div.post-feed-item')` 取当前 DOM 下所有作品项,`item_count = await post_items.count()`。 3. 只遍历前 `min(item_count, page_size)` 条,解析封面、标题、时间、播放/点赞/评论/分享/收藏等。 4. **未使用参数 `page`**:没有根据 page 做滚动、点击「加载更多」或请求下一页接口,每次请求都是同一页 DOM。 5. 返回值: - `total = len(works)`(当前批数量,非平台总作品数); - `has_more = item_count > page_size`(仅表示当前屏 DOM 条数是否大于 page_size)。 ## 四、存在的问题 1. **分页未实现** - Node 会按 `has_more` 继续请求 page=1, 2, …,但 Python 每次都是同一页、同一批 DOM,第二页起通常会返回重复数据,Node 端 `newCount === 0` 后停止。 - 实际效果:**只能拿到首屏/首批作品**(约几十条),列表若为虚拟滚动,首屏 DOM 条数有限,总数会更少。 2. **work_id 不稳定** - 使用 `work_id = f"weixin_{i}_{hash(title)}_{hash(publish_time)}"`,同一作品在不同批次或重试中可能 i 不同,且 hash 可能碰撞;若页面有唯一 ID(如接口或 data 属性),应用真实 ID 更稳妥。 3. **total / has_more 含义与 Node 预期不一致** - Node 用 `declaredTotal`、`has_more` 决定是否继续请求;Python 的 `total` 只是本批条数,`has_more` 只反映当前屏是否多于 page_size,不能代表「平台是否还有更多作品」。 4. **调试代码未清理** - 存在 `print("1111111111111111")` 等调试输出,以及大段 DOM 打印,建议移除或改为可配置日志。 ## 五、改进方向建议 1. **实现真实分页** - 若视频号创作者后台有「加载更多」或滚动加载:在 `get_works` 里根据 `page` 做多次滚动或点击,再采集当前屏新增的 `div.post-feed-item`,并去重。 - 若有列表接口(如类似抖音 work_list):改为请求接口并解析 cursor/offset 分页,返回 `next_page` 供 Node 按游标请求(需同步改 Node 对 weixin_video 的分页策略)。 2. **使用稳定作品 ID** - 从 DOM 或接口中取作品唯一 ID(若有),作为 `work_id`,便于去重与和本地库一致对应。 3. **清理调试并规范日志** - 去掉无意义 print,DOM 打印改为 debug 级别或环境变量控制。 --- 文档基于当前代码整理,若 weixin.py 或 HeadlessBrowserService 有改动,以实际代码为准。