mcpField
S
scrape-codegen

scrape-codegen

github.com/zytedata数据与数据库11174 次安装2 天前更新
代码仓库

关于 scrape-codegen

面向 Scrapy 项目的 Claude skill,可依据提取规范和已保存的 HTML 样本生成并测试 web-poet 页面对象。

主要功能

  1. 01生成页面对象框架
  2. 02分析已保存的 HTML 页面
  3. 03生成基于 schema 的提取代码
  4. 04转换 fixtures 并运行测试

适用场景

  1. 01根据提取规范创建页面对象
  2. 02生成列表页提取代码
  3. 03使用已保存的 fixtures 测试提取

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/zytedata/skills --skill scrape-codegen

安装后在 Claude Code 中输入/scrape-codegen确认已加载。

附带文件scripts/convert_fixtures.py

详细介绍

你正在根据提取规范生成一个 web-poet 页面对象。规范包含架构、已保存的 HTML 页面和预期值。它可以描述任何数据类型,例如产品详情、导航链接、文章内容等。Codegen 无需了解数据类型;它会根据架构生成提取数据的 PO。

规范由 /scrape-spec 生成,项目由 /scrape-ensure-project 创建。

阅读 ${CLAUDE_SKILL_DIR}/../scrape/references 中的 python-environments.md 和 docs-access.md。

输入

原始参数字符串为 $ARGUMENTS。将其拆分为最多 3 个以空格分隔的位置参数:

  1. spec_path:规范文件夹的路径,例如 .scrape/books-toscrape
  2. project_dir:Scrapy 项目的路径
  3. fields:可选,以逗号分隔的待生成字段名称(留空 = 所有字段)

流程

步骤 1:读取规范

读取 {spec_path}/spec.json,获取:

  • schema.properties — 字段定义
  • html_variant — 要使用的 HTML(raw 或 rendered)
  • url — 起始 URL(用于域名)
  • data_type — 要提取的数据类型(用于类命名);始终使用单数形式(例如 product、book)

根据以下约定,从 data_type 派生名称(不要使用复数形式):

  • ClassName = PascalCase + Page → product → ProductPage
  • ItemClass = PascalCase + Item → product → ProductItem
  • module_name = data_type 的 snake_case → product → product

如果提供了 fields,则只保留 schema.properties 中对应的字段。

列出 {spec_path}/pages/ 中与 {spec_path}/values/ 中的值相对应的页面目录。读取每个页面的预期值。

从 spec_path 派生 site_name(取父目录名称,例如从 .scrape/books-toscrape/products 中得到 books-toscrape)。 从 {project_dir} 检测项目名称。

列表类型检测。 如果 data_type 以 -list 结尾(例如 products-list):

  • 设置 is_list_type = true
  • 去掉 -list 后缀,得到 base_type(例如 products)
  • 派生 ItemClass:如果 base_type 以 s 结尾,则去掉末尾的 s 将其变为单数(例如 products → product),然后使用 TitleCase(例如 Product)。如果名称看起来已经是单数,则直接使用 TitleCase。
  • WrapperClass = {ItemClass}ListItems(例如 ProductListItems)
  • PageClass = {ItemClass}ListPage(例如 ProductListPage)
  • 值采用数组格式:{"url": ..., "values": [{...}, ...]}

否则,设置 is_list_type = false,并将 ClassName 派生为 TitleCase(data_type) + "Page"。

步骤 2:添加 item 和 page object 存根

对于非列表类型(is_list_type = false):

检查 {project_name}/items.py 中是否存在与 data_type 匹配的 item 类。 如果不存在,则根据架构编写一个(所有字段均为可选,使用 | None = None)。

在子 Agent 中添加 page object 存根,以便父技能之后继续执行:

Agent(description="add page object stub", prompt="/scrape-add-page-object {project_dir}/{project_name}/pages/{module_name}.py {ClassName} {domain} web_poet.WebPage {project_name}.items.{ItemClass}")

如果 html_variant 是 rendered,则使用 web_poet.BrowserPage。

对于列表类型(is_list_type = true):

items.py 需要包含两个类。检查它们是否已存在;缺少的类都要补上。

  1. 基础 item 类(ItemClass,例如 Product)——一个 @dataclass,字段类型与架构匹配。示例:

    @dataclass
    class Product:
        title: str
        price: str | None = None
    
  2. 包装类(WrapperClass,例如 ProductListItems)——一个 @dataclass,包含唯一的 items 字段,其类型为 list[ItemClass] | None = None。示例:

    @dataclass
    class ProductListItems:
        items: list[Product] | None = None
    

然后在子 Agent 中添加 page object 存根,并明确将 items 作为第 6 个参数传入,以便生成 @field 存根,即使所有包装类字段都有默认值:

Agent(description="add page object stub", prompt="/scrape-add-page-object {project_dir}/{project_name}/pages/{module_name}.py {PageClass} {domain} web_poet.WebPage {project_name}.items.{WrapperClass} items")
步骤 3:转换 fixture

从项目结构中查找 fixture 类路径(例如, {project_name}.pages.{module_name}.{ClassName})。

uv run ${CLAUDE_SKILL_DIR}/scripts/convert_fixtures.py \
    {spec_path} {project_dir} {fixture_class_path}
步骤 4:分析页面(并行)
mkdir -p .scrape/.work/{site_name}/codegen-analyze

每个页面启动一个 Agent,并提供对应的值,在一条消息中全部发送以并行执行。每个 Agent 都会运行 /scrape-codegen-analyze,并接收全部 4 个参数:

/scrape-codegen-analyze {spec_path}/pages/{page_id}/{html_variant}.html .scrape/.work/{site_name} {spec_path}/spec.json {spec_path}/values/{page_id}.json

跳过 HTML 文件不存在的页面。

对于列表类型的规格,页面名称以 list-* 开头,值中包含数组。 /scrape-codegen-analyze 技能会根据 meta.json(其中 page_type 为 list)自动检测列表页面,并生成基于容器的提取说明。

步骤 5:生成页面对象代码

所有分析 Agent 完成后,启动一个 Agent 运行 /scrape-codegen-generate,并接收全部 3 个参数:

/scrape-codegen-generate .scrape/.work/{site_name} {project_dir}/{project_name}/pages/{module_name}.py {spec_path}/spec.json
步骤 6:测试
cd {project_dir} && uv run pytest fixtures/ -x -v

报告结果。如果测试失败,请阅读错误信息,并考虑重新生成失败的字段。

步骤 7:报告
Generated page object at {project_dir}/{project_name}/pages/{module_name}.py:
  Class: {ClassName} (N fields)
  Fixtures: N test cases
  Tests: N/N passing

代码生成规则

遵循 ${CLAUDE_SKILL_DIR}/../scrape/references/web-poet.md 中的 web-poet 参考指南,并遵守以下规则:

  • 保持代码简洁且适用于不同领域,不要过度适配示例页面
  • 缺少数据时返回 None,不要返回空字符串、False 或 []
  • 使用保护子句,并在访问属性前检查是否为 None
  • 不要为字段方法添加文档字符串
  • 不要捕获通用的 Exception,只捕获特定异常
  • 尽量保证输出确定性,避免使用集合(如有需要,使用列表并去重)
  • 如果分析显示某个字段来自结构化数据(JSON-LD、microdata),请使用 extruct——元数据格式与前序阶段 extract_metadata.py 的输出一致,因此页面对象中可以使用相同的访问模式
  • 如果需要浏览器响应,请将 BrowserPage 用作基类

评论

支持 Markdown