This repository has been archived on 2026-08-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
kb-skills/.planning/phases/09-pipeline-integration/09-RESEARCH.md
T

15 KiB
Raw Blame History

Phase 9: Pipeline 整合 - Research

Researched: 2026-03-17 Domain: Pipeline Integration / Zhihu Crawler Integration Confidence: HIGH

Summary

Phase 9 需要将 Phase 7-8 实现的 ZhihuCrawler 和 ZhihuLogin 整合进现有 Pipeline 架构,替换原有的 ZhihuPlaywrightExtractor。核心工作是修改 ZhihuAdapter 的内部实现,同时保持公共接口不变,确保对 DownloadProcessor 无感知变化。

Primary recommendation: 采用"内部替换"策略 - 修改 ZhihuAdapter 内部调用 ZhihuCrawler,保持接口稳定,评论追加到正文,Cookie 过期自动触发扫码登录。

<user_constraints>

User Constraints (from CONTEXT.md)

Locked Decisions

整合方式

  • 策略: 修改现有 ZhihuAdapter 内部实现,切换到使用 ZhihuCrawler
  • 接口保持: 保持 ZhihuAdapter 的公共接口不变,对 DownloadProcessor 无感知变化
  • 实现变更: fetch_metadata() 和 fetch_content() 内部调用 ZhihuCrawler 而非 ZhihuPlaywrightExtractor

评论数据处理

  • 默认行为: 评论爬取默认开启
  • 存储方式: 评论追加到正文末尾(Markdown 格式)
  • 数量上限: 20 条热门评论(使用 hot 排序,API 默认限制)
  • 嵌套处理: 不展开回复的回复,只爬第一层评论(继承自 Phase 8)
  • 策略: 自动触发扫码登录
  • 流程: 检测到 Cookie 失效时,调用 ZhihuLogin 触发扫码登录,用户扫码后继续执行
  • 用户体验: 用户会看到二维码提示,扫码后流程自动继续

失败降级策略

  • 策略: 直接报错,不降级
  • 不降级: 新爬虫失败时不尝试旧版 ZhihuPlaywrightExtractor
  • 错误处理: 抛出异常,由上层 Pipeline 统一处理错误

Claude's Discretion

  • 评论追加到正文的具体 Markdown 格式(标题、分隔线等)
  • 二维码显示的具体实现方式(终端/图片文件)
  • 错误提示信息的具体文案

Deferred Ideas (OUT OF SCOPE)

None — discussion stayed within phase scope

</user_constraints>

<phase_requirements>

Phase Requirements

ID Description Research Support
PIPE-01 替换现有 ZhihuProcessor,使用新的爬虫实现 ZhihuAdapter 内部调用 ZhihuCrawler,保持接口不变
PIPE-02 保持与现有 Pipeline 架构的兼容性 PlatformAdapter 接口不变,DownloadProcessor 无需修改
PIPE-03 支持断点续传和幂等执行 WorkflowStateManager + is_step_completed 检查机制
</phase_requirements>

Standard Stack

Core

Library Version Purpose Why Standard
pydantic v2 数据验证和模型定义 项目已使用,CrawlResult/AnswerData/ArticleData 等模型
playwright sync_api 浏览器自动化 ZhihuCrawler 已使用,支持代理和 Cookie
tenacity ^8.0 重试机制 ZhihuCrawler 使用 Retrying 对象
asyncio stdlib 异步执行 PlatformAdapter 接口使用 async/await

Supporting

Library Version Purpose When to Use
kb_workflow.crawler.ZhihuCrawler internal 问答/专栏/评论爬取 fetch_metadata/fetch_content 调用
kb_workflow.auth.ZhihuLogin internal QR 码登录和 Cookie 管理 Cookie 过期时触发
kb_workflow.pipeline.errors.AuthError internal 认证错误处理 Cookie 失效时抛出

Key Classes

Class Location Purpose
ZhihuCrawler kb_workflow/crawler/zhihu_crawler.py 主爬虫,crawl_answer/crawl_article/crawl_comments
ZhihuLogin kb_workflow/auth/zhihu_login.py login_by_qrcode(),Cookie 持久化
ZhihuAdapter kb_workflow/pipeline/platforms/zhihu.py 待修改 - PlatformAdapter 实现
DownloadProcessor kb_workflow/pipeline/processors/download.py 调用 Adapter,无需修改

Architecture Patterns

Current ZhihuAdapter Implementation

# 当前实现:使用 ZhihuPlaywrightExtractor (DOM 解析)
class ZhihuAdapter(PlatformAdapter):
    def _get_extractor(self):
        return ZhihuPlaywrightExtractor(headless=self.headless, cookie=self.cookie)

    async def fetch_metadata(self, content_id, content_type, source_url=None):
        result = await asyncio.to_thread(self._extract_with_playwright, url)
        # 返回 metadata dict

    async def fetch_content(self, content_id, content_type, source_url=None):
        metadata = await self.fetch_metadata(...)
        return self._format_with_frontmatter(metadata, content_markdown, content_type)

Target Implementation Pattern

# 目标实现:使用 ZhihuCrawler (API 拦截)
class ZhihuAdapter(PlatformAdapter):
    def __init__(self, ...):
        self._crawler = None

    def _get_crawler(self) -> ZhihuCrawler:
        if self._crawler is None:
            self._crawler = ZhihuCrawler(headless=self.headless, timeout=self.timeout)
        return self._crawler

    async def fetch_metadata(self, content_id, content_type, source_url=None):
        crawler = self._get_crawler()
        if content_type == "answer":
            result = await asyncio.to_thread(crawler.crawl_answer, url, include_comments=True)
        elif content_type == "zhuanlan":
            result = await asyncio.to_thread(crawler.crawl_article, url)
        # 转换 CrawlResult -> metadata dict

    async def fetch_content(self, content_id, content_type, source_url=None):
        # 调用 fetch_metadata,格式化为 Markdown(含评论)
# Cookie 过期处理流程
async def fetch_metadata(self, content_id, content_type, source_url=None):
    try:
        result = self._crawl_with_retry(url)
        if result.error and self._is_cookie_expired(result):
            # 触发扫码登录
            login_result = self._trigger_qrcode_login()
            if login_result.success:
                # 刷新配置并重试
                get_settings.cache_clear()
                result = self._crawl_with_retry(url)
        return self._convert_to_metadata(result)
    except Exception as e:
        raise AuthError(step_name="zhihu_metadata", ...)

Comment Append Format (Claude's Discretion)

## 正文内容
[原回答/文章内容]

---

## 热门评论
1. **@用户A**: 评论内容...
2. **@用户B**: 评论内容...
3. **@用户C**: 评论内容...

Anti-Patterns to Avoid

  • 不要修改 PlatformAdapter 接口: DownloadProcessor 依赖接口稳定
  • 不要在 ZhihuCrawler 中添加 Pipeline 逻辑: 保持爬虫职责单一
  • 不要同步阻塞调用爬虫: 使用 asyncio.to_thread 包装
  • 不要忽略 Cookie 过期: 必须触发扫码登录流程

Don't Hand-Roll

Problem Don't Build Use Instead Why
HTML to Markdown 自定义转换器 MarkdownConverter (已存在) zhihu_extractor.py 已有
Cookie 解析 手动解析 cookie 字符串 PlaywrightBrowser._add_cookies() 已实现,处理格式正确
重试逻辑 @retry 装饰器 tenacity Retrying 对象 支持可配置超时,Phase 8 已用
API 签名 逆向 x-zse-96 浏览器自动处理 route.fetch() 自动包含签名

Key insight: ZhihuCrawler 已完整实现问答/专栏/评论爬取,只需要正确调用和结果转换。

Common Pitfalls

Pitfall 1: CrawlResult 到 Metadata Dict 转换不完整

What goes wrong: ZhihuCrawler 返回 CrawlResult(AnswerData/ArticleData),但 DownloadProcessor 期望特定格式的 metadata dict Why it happens: 数据模型不完全匹配 How to avoid: 实现 _convert_crawl_result_to_metadata() 转换函数,确保所有字段正确映射 Warning signs: DownloadProcessor 提取 title/author 时返回空字符串

Pitfall 2: 评论未正确追加到正文

What goes wrong: fetch_content() 返回的内容不包含评论 Why it happens: include_comments=True 未正确传递或评论格式化逻辑遗漏 How to avoid: fetch_content() 必须调用 fetch_metadata(include_comments=True),格式化时追加评论 Warning signs: 输出 Markdown 文件无评论部分

What goes wrong: 误判 Cookie 有效/无效,导致不必要的登录或无限重试 Why it happens: 仅依赖 HTTP 状态码,未检查响应内容 How to avoid: 检查 API 响应中的 error 字段和 401/403 状态码 Warning signs: 登录循环或认证错误未触发

Pitfall 4: asyncio.to_thread 调用错误

What goes wrong: ZhihuCrawler 方法调用参数传递不正确 Why it happens: to_thread 需要函数引用和参数分开传递 How to avoid: await asyncio.to_thread(crawler.crawl_answer, url, include_comments=True) Warning signs: TypeError: 'CrawlResult' object is not callable

Code Examples

ZhihuCrawler Usage (from existing code)

# Source: kb_workflow/crawler/zhihu_crawler.py
crawler = ZhihuCrawler(headless=True, timeout=30)

# 爬取回答(含评论)
result = crawler.crawl_answer(
    url="https://www.zhihu.com/question/123/answer/456",
    include_comments=True
)
# result = CrawlResult(success=True, content_type="answer", data=AnswerData, comments=[...])

# 爬取专栏文章
result = crawler.crawl_article(url="https://zhuanlan.zhihu.com/p/123")
# result = CrawlResult(success=True, content_type="article", data=ArticleData)

ZhihuLogin QR Code Flow (from existing code)

# Source: kb_workflow/auth/zhihu_login.py
login = ZhihuLogin(intermediate_path=str(settings.intermediate_path), headless=False)
result = login.login_by_qrcode()

if result.success:
    # 保存 Cookie 到配置文件
    cookie_path = Path.home() / ".kb_env"
    # 更新 KB_ZHIHU_COOKIE 环境变量
    # 刷新 settings 缓存
    get_settings.cache_clear()

Metadata Conversion Pattern

def _convert_answer_to_metadata(self, result: CrawlResult) -> Dict[str, Any]:
    """Convert CrawlResult[AnswerData] to metadata dict."""
    answer: AnswerData = result.data
    return {
        "type": "answer",
        "title": answer.question_title,
        "author": answer.metadata.author.name if answer.metadata.author else "",
        "content": answer.content,
        "content_markdown": self._html_to_markdown(answer.content),
        "publish_time": self._format_timestamp(answer.metadata.created_time),
        "url": f"https://www.zhihu.com/question/{answer.question_id}/answer/{answer.id}",
        "question_id": str(answer.question_id),
        "answer_id": str(answer.id),
        "vote_up": answer.metadata.voteup_count,
        "comment_count": answer.metadata.comment_count,
    }

Comment Formatting Pattern

def _format_comments_section(self, comments: List[CommentData]) -> str:
    """Format comments as Markdown section."""
    if not comments:
        return ""

    lines = ["\n\n---\n\n## 热门评论\n"]
    for i, comment in enumerate(comments[:20], 1):  # 最多 20 条
        author = f"@{comment.author.name}" if comment.author else "匿名"
        lines.append(f"{i}. **{author}**: {comment.content}")

    return "\n".join(lines)

State of the Art

Old Approach Current Approach When Changed Impact
ZhihuPlaywrightExtractor (DOM 解析) ZhihuCrawler (API 拦截) Phase 8 更可靠的数据获取,支持评论
手动 Cookie 管理 ZhihuLogin QR 码登录 Phase 7 自动化登录流程
无评论支持 评论爬取 + 追加到正文 Phase 8 更完整的内容存档

Deprecated/outdated:

  • ZhihuPlaywrightExtractor 直接调用: 将被 ZhihuCrawler 替代
  • 手动设置 KB_ZHIHU_COOKIE: 通过 login 命令自动管理

Open Questions

  1. 评论 Markdown 格式细节

    • What we know: 用户决策评论追加到正文,分隔线 + 热门评论标题
    • What's unclear: 具体的分隔线样式、评论编号格式
    • Recommendation: 使用 --- 分隔线,1. **@用户**: 内容 格式
  2. Cookie 过期检测的精确条件

    • What we know: API 返回 401 或特定错误信息
    • What's unclear: 具体的错误码和响应结构
    • Recommendation: 检查 response.status == 401 或 JSON 中的 error 字段
  3. ZhihuLogin 在 headless 模式下的二维码展示

    • What we know: QR 码保存到文件,路径返回给用户
    • What's unclear: 是否需要终端 ASCII 输出
    • Recommendation: 默认保存文件并打印路径,可选终端输出

Validation Architecture

Test Framework

Property Value
Framework pytest
Config file pytest.ini
Quick run command pytest tests/test_platforms/test_zhihu.py -x -v
Full suite command pytest tests/ -v

Phase Requirements → Test Map

Req ID Behavior Test Type Automated Command File Exists?
PIPE-01 ZhihuAdapter 使用 ZhihuCrawler unit pytest tests/test_platforms/test_zhihu.py -x ✅ 需修改
PIPE-02 DownloadProcessor 兼容性 integration pytest tests/test_processors/test_download.py -x ✅ 已存在
PIPE-03 断点续传/幂等执行 unit pytest tests/test_pipeline/test_idempotency.py -x ✅ 已存在

Sampling Rate

  • Per task commit: pytest tests/test_platforms/test_zhihu.py -x -v
  • Per wave merge: pytest tests/test_platforms/ tests/test_processors/test_download.py -v
  • Phase gate: Full suite green before /gsd:verify-work

Wave 0 Gaps

  • tests/test_platforms/test_zhihu.py — 更新测试使用 ZhihuCrawler mock
  • 新增测试:Cookie 过期自动触发登录流程
  • 新增测试:评论追加到正文格式验证
  • 新增测试:CrawlResult 到 metadata 转换正确性

(现有测试基础设施完善,主要是更新和新增 ZhihuAdapter 相关测试)

Sources

Primary (HIGH confidence)

  • kb_workflow/crawler/zhihu_crawler.py - ZhihuCrawler 实现
  • kb_workflow/crawler/models.py - CrawlResult, AnswerData, ArticleData, CommentData
  • kb_workflow/auth/zhihu_login.py - ZhihuLogin QR 码登录
  • kb_workflow/pipeline/platforms/zhihu.py - ZhihuAdapter (待修改)
  • kb_workflow/pipeline/processors/download.py - DownloadProcessor (无需修改)

Secondary (MEDIUM confidence)

  • tests/test_platforms/test_zhihu.py - 现有 ZhihuAdapter 测试
  • tests/test_crawler/ - ZhihuCrawler 测试

Tertiary (LOW confidence)

  • None - 所有信息均来自项目代码

Metadata

Confidence breakdown:

  • Standard stack: HIGH - 项目已有完整实现,只需整合
  • Architecture: HIGH - PlatformAdapter 接口稳定,模式清晰
  • Pitfalls: HIGH - 基于现有代码分析,问题可预见

Research date: 2026-03-17 Valid until: 30 days - 项目内部代码,稳定性高