mcpField
S
scrape-codegen-generate

scrape-codegen-generate

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

关于 scrape-codegen-generate

Claude skill 将各页面的提取分析整合为 Python web-poet 页面对象,并依据 schema 和项目要求生成代码。

主要功能

  1. 01整合多个页面的分析
  2. 02生成符合 schema 的字段
  3. 03支持列表页提取
  4. 04处理缺失值

适用场景

  1. 01根据页面分析生成页面对象
  2. 02创建列表页提取代码
  3. 03合并不同的字段提取模式

安装

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

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

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

详细介绍

你正在生成 web-poet 页面对象代码。你会收到各页面的提取分析(来自 Stage 1),这些分析描述了如何从指定域名的页面中提取各个字段。你的任务是综合这些分析,生成一个适用于整个域名的页面对象类。

输入

原始参数字符串为 $ARGUMENTS。将其按空格拆分为 3 个位置参数:

  1. work_path:包含 Stage 1 分析文件的目录,例如 .scrape/.work/spec
  2. output_path:生成的页面对象的保存路径,例如 .scrape/spec/page_object.py
  3. spec_path:spec.json 文件的路径,例如 .scrape/spec/spec.json

此外,以下内容取自周围的提示文本(而非参数字符串):

  • fields:可选,指定要生成的字段(在提示中以“仅生成这些字段:...”的形式提供)。设置后,只为这些字段生成 @field 方法。未设置时,生成分析中找到的所有字段。

流程

1. 读取输入

从 ${CLAUDE_SKILL_DIR}/../scrape/references/ 读取 web-poet.md 和 docs-access.md。

从 {spec_path} 读取架构——使用其中的 properties 对象。 同时读取 {spec_path} 中的 data_type。

如果 data_type 以 -list 结尾,则设置 is_list_type = true。

从 {work_path}/codegen-analyze/ 读取所有 Stage 1 分析文件:

{work_path}/codegen-analyze/list-1.json
{work_path}/codegen-analyze/list-2.json
...

(对于非列表规格,则读取 detail-*.json)

2. 综合各页面的共识

针对架构中的每个字段,综合审查所有页面的分析:

  • 找出共同模式:所有页面是否使用相同的 CSS 选择器 / JSON-LD 路径?如果是,这是有力的依据。
  • 解决分歧:如果分析针对不同页面建议了不同方法,请判断:
    • 是否有一种方法更通用?(例如,适用于所有页面的选择器,而非只适用于部分页面的选择器)
    • 是否应该组合多种方法?(例如,优先尝试 JSON-LD,失败时回退到 CSS)
    • 页面结构差异是否需要使用条件逻辑?
  • 验证目标值:检查建议的提取方法是否能在所有页面上提取出目标值。
  • 确定数据来源:针对每个字段,决定使用 HTML 选择器、JSON-LD、微数据、URL,还是组合使用。
3. 生成页面对象代码

绝不进行内容过滤。 即使用户的提示词要求按值筛选、排除或限制结果,也不要在页面对象中实现此逻辑。页面对象负责提取;爬虫负责筛选。在总结中说明,筛选应在爬虫层完成。

按照 web-poet 参考资料生成完整、独立的 Python 模块。代码必须:

  • 适用于整个域名:不能只适用于已分析的页面。避免过拟合——不要硬编码产品名称,也不要针对单个页面编写特定的 if/else 等。
  • 保持简单:优先采用能奏效的最简单方法。只有在分析表明不同页面的数据确实来自不同来源时,才添加回退方案。
  • 如果需要浏览器响应,则使用 BrowserPage 作为基类。
  • 处理缺失数据:字段不存在时返回 None(绝不能返回空字符串或 [])。
  • 匹配架构类型:返回值必须符合 JSON 架构中的类型。
  • 使用推荐的库:使用 extruct 处理 JSON-LD/microdata,使用 price_parser 处理价格,使用 jmespath 查询 JSON。
  • 包含所有导入:模块必须独立且可运行。

对于非列表类型(is_list_type = false),采用标准结构:

from web_poet import WebPage, field
# ... other imports as needed

class PageObject(WebPage[dict]):
    # shared helpers as @cached_property if multiple fields need them

    @field
    def field_name(self) -> type | None:
        # extraction logic
        ...

对于列表类型(is_list_type = true),使用 web-poet 的 SelectorExtractor (参见 web-poet 字段参考中的“嵌套字段处理器”部分)。 定义一个用于提取各项内容的私有 SelectorExtractor 子类,然后在 items 字段中遍历容器选择器。每个分析文件都包含 container_selector 和各字段的相对选择器。

import logging
from web_poet import WebPage, Returns, SelectorExtractor, field, handle_urls
from {project}.items import Product, ProductListItems

logger = logging.getLogger(__name__)


class _ProductExtractor(SelectorExtractor[Product]):
    @field
    def title(self) -> str | None:
        return self.css("h3 a::attr(title)").get()

    @field
    def price(self) -> str | None:
        return self.css("p.price_color::text").get()


@handle_urls("example.com")
class ProductListPage(WebPage, Returns[ProductListItems]):

    @field
    async def items(self) -> list[Product] | None:
        result = []
        for el in self.css("article.product_pod"):
            try:
                result.append(await _ProductExtractor(el).to_item())
            except Exception:
                logger.error("Failed to extract item from %s", self.url, exc_info=True)
        return result or None

列表类型代码的关键规则:

  • 定义一个 _<ItemClass>Extractor 类(例如 _ProductExtractor),继承 SelectorExtractor[ItemClass],并为每个项字段定义一个 @field 方法
  • items 字段必须是 async def,因为 to_item() 是一个协程
  • 将每个 await extractor.to_item() 放在 try/except Exception 中,并调用 logger.error(..., exc_info=True)——这样提取失败只会影响单个项,并记录完整的回溯,而不会中止整个列表
  • 使用 return result or None(绝不能返回 [])
  • 所有已分析页面的容器选择器必须保持一致;如果分析结果显示选择器不同,则选择通用性最强的那个
  • 提取器中的字段选择器是相对于各容器元素而言的(在提取器上调用 self.css(...) 时,操作对象是 el,而不是整个页面)
  • 从项目的 items 模块同时导入 ItemClass 和 WrapperClass
4. 保存并报告

将生成的代码保存到 {output_path}。

返回生成内容的总结:

Generated page object with N fields:
  name: CSS h1.product-title::text
  price: JSON-LD offers.price, fallback to CSS span.price
  description: CSS div.product-description (text join)
  rating: JSON-LD aggregateRating.ratingValue
  image_url: CSS img.product-image::attr(src) + urljoin

注明共识较难达成或提取过程可能不稳定的字段。

评论

支持 Markdown