关于 scrape-codegen-generate
Claude skill 将各页面的提取分析整合为 Python web-poet 页面对象,并依据 schema 和项目要求生成代码。
主要功能
- 01整合多个页面的分析
- 02生成符合 schema 的字段
- 03支持列表页提取
- 04处理缺失值
适用场景
- 01根据页面分析生成页面对象
- 02创建列表页提取代码
- 03合并不同的字段提取模式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zytedata/ skills --skill scrape-codegen-generate安装后在 Claude Code 中输入/scrape-codegen-generate确认已加载。
详细介绍
你正在生成 web-poet 页面对象代码。你会收到各页面的提取分析(来自 Stage 1),这些分析描述了如何从指定域名的页面中提取各个字段。你的任务是综合这些分析,生成一个适用于整个域名的页面对象类。
输入
原始参数字符串为 $ARGUMENTS。将其按空格拆分为 3 个位置参数:
- work_path:包含 Stage 1 分析文件的目录,例如
.scrape/.work/spec - output_path:生成的页面对象的保存路径,例如
.scrape/spec/page_object.py - 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
注明共识较难达成或提取过程可能不稳定的字段。