mcpField
S
scrape-explore-site

scrape-explore-site

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

关于 scrape-explore-site

用于探索网站并保存起始页、列表页和详情页的 Claude skill。它引导链接分类、页面下载及导航值生成,服务于网页抓取工作流。

主要功能

  1. 01按范围探索网站
  2. 02配置列表页与详情页数量
  3. 03分类链接组
  4. 04下载页面并记录来源
  5. 05生成导航值

适用场景

  1. 01收集网站的分类页和详情页
  2. 02在指定分类子树内探索
  3. 03为抓取规范准备已保存页面

安装

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

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

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

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

scripts/
apply_classification.py
download.py
extract_links.py
extract_nav_values.py
extract_values.py

详细介绍

你正在浏览一个网站,以发现并保存多种不同类型的页面。给定一个起始 URL,请在其范围内查找起始页、列表/分类页和详情页。

阅读 ${CLAUDE_SKILL_DIR}/../scrape/references/python-environments.md。

输入

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

  1. url:起始 URL,通常是首页
  2. project_path:保存输出的路径
  3. DETAIL_COUNT:要查找的详情页数量(默认值:3)
  4. LIST_COUNT:要保存的列表/分类页数量(默认值:2)

始终保存起始页。

范围

将起始 URL 视为范围根节点:

  • 网站根目录:广泛探索各个分类。
  • 分类/列表页:仅在该子树内探索。沿着分页和更深层子分类继续,但不要扩展到同级或父级分类。

示例:

/scrape-explore-site https://example.com .scrape/myspec
  → DETAIL_COUNT=3, LIST_COUNT=2

/scrape-explore-site https://example.com dataset/site-slug 8 4
  → DETAIL_COUNT=8, LIST_COUNT=4

工具

download.py — 捕获页面(HTTP + Playwright + 截图)
uv run ${CLAUDE_SKILL_DIR}/scripts/download.py --log-file {pages_dir}/download.log <<'EOF'
[...]
EOF

从标准输入读取下载任务的 JSON 数组:

[
  {"url": "...", "output_dir": "...", "page_type": "...",
   "discovered_from": {"page": "...", "url": "..."}}
]

为每个任务创建 OUTPUT_DIR/{raw.html, rendered.html, screenshot.png, meta.json}。多个任务会并行下载。日志文件会在多次运行中持续追加。

绝不要使用 WebFetch 下载页面——它会将完整 HTML 加载到上下文中。 绝不要读取 screenshot.png 文件——这些文件会保存供后续使用,不用于探索。使用链接组和 HTML 结构进行分类。

extract_links.py — 提取链接
uv run ${CLAUDE_SKILL_DIR}/scripts/extract_links.py PAGE.html [PAGE2.html ...] --base-url-from-meta --group --link-limit N

输出带编号的 JSON 组:

0: {"path": "article.product_pod > h3 > a", "count": 20, "links": [...]}
1: {"path": "ul.nav > li > a", "count": 50, "links": [...], "file": "list-1"}

你需要传入 --link-limit 值,该值应为 DETAIL_COUNT 和 LIST_COUNT 中较大的那个。这是为了限制输出大小。

将输出保存到文件以便分类。如果输入文件有多个,编号会全局递增,每组都包含一个 "file" 键。

绝不要保存此脚本的标准错误,只保存标准输出。

apply_classification.py — 分类并写入 links.json

确定每组对应的类型后,应用分类:

uv run ${CLAUDE_SKILL_DIR}/scripts/apply_classification.py GROUPS_FILE --classify "items=0 subCategories=1 nextPage=4" --pages-dir {pages_dir}

读取已保存的链接组文件,并将 links.json 写入每个来源页面的目录。无需使用 Write 工具——脚本会直接写入文件。

提取链接时选择 HTML 文件

优先使用 rendered.html(可处理由 JS 渲染的网站)。如果 rendered.html 不存在,则使用 raw.html。

链接分类

完成步骤 1(提取链接组)后,将每个带编号的组分类为以下类型之一:

  • items:指向详情页的链接。许多链接共享相同的路径模式,URL 中包含唯一 ID 或 slug。
  • subCategories:指向列表/分类页的链接,这些页面会缩小或细化当前页面的主题范围。每组只能指定一个标签,因此组内的每个 URL 都必须符合条件。应用此标签前,请在推理中注明该组的最后一个 URL——如果组内任何 URL 与当前页面无关,则将整个组设为 skip。
  • nextPage:分页链接。URL 或文本中包含页码,或包含“下一页”/“上一页”等文本。
  • skip:(从 --classify 中省略)导航、页脚、账户、页眉链接以及无关的分类链接。

综合使用以下三个信号:path(结构位置)、href(URL 模式)、text(锚文本)。

然后运行 apply_classification.py,并传入 --classify "items=0,3 subCategories=1 nextPage=2",以写入 JSON 文件。从 classify 字符串中省略 skip 组。

反向链接

对于每个已保存的页面(起始页除外),在任务中加入 discovered_from。这样会在下载时直接将反向链接写入 meta.json,无需单独读写。

输出结构

所有输出都保存在 {pages_dir} 下,其中 {pages_dir} 为 {project_path}/pages/:

start-1/          — start page
  raw.html  rendered.html  screenshot.png  meta.json  links.json
list-1/           — category/listing page (from start page)
list-2/           — sub-category page (from list-1, deeper navigation)
detail-1/         — item/article page
unknown-1/        — explored page that didn't fit list/detail (still kept)
...

每个页面目录都包含 raw.html、rendered.html、screenshot.png、meta.json,以及(如果已对该页面的链接进行分类)links.json。

处理被拦截的网站

检查 raw.html(HTTP)和 rendered.html(Playwright)——两者的表现可能不同。只有当两者都返回机器人检测内容(CAPTCHA、“verify you are human”、403、Cloudflare 挑战)时,才表示网站完全被拦截。如果 raw 被拦截,但 rendered 可用,则继续使用 rendered——这是可以的。

如果起始页面完全被拦截(raw 和 rendered 都被拦截),立即停止。报告拦截情况,并在摘要中返回 "blocked": true——不要尝试变通方法、重试、修改请求头或使用其他 URL。调用方会负责提供替代方案。

如果起始页面可以访问,但后续页面被拦截,则继续处理可用的页面。不要重试被拦截的 URL,也不要尝试绕过拦截。

检测到任何页面被拦截时,在该页面的 meta.json 中添加 "blocked": true(如果仅 HTTP 被拦截,则添加 "blocked": "raw")。

流程

1. 下载并分类起始页面

下载:

uv run ${CLAUDE_SKILL_DIR}/scripts/download.py --log-file {pages_dir}/download.log <<'EOF'
[{"url": "URL", "output_dir": "{pages_dir}/start-1", "page_type": "start"}]
EOF

提取链接并将分组保存到文件:

uv run ${CLAUDE_SKILL_DIR}/scripts/extract_links.py {pages_dir}/start-1/rendered.html --base-url-from-meta --group > {pages_dir}/start-1/groups.txt

读取分组,确定分类,然后应用:

uv run ${CLAUDE_SKILL_DIR}/scripts/apply_classification.py {pages_dir}/start-1/groups.txt --classify "items=N subCategories=N nextPage=N" --pages-dir {pages_dir}/start-1
2. 下载并分类列表页

从起始页面的 subCategories 链接中选取 LIST_COUNT 个 URL:

  • 如果起始页面是网站根页面,优先选择不同的类别。
  • 如果起始页面已经是类别页或列表页,则留在其子树中。优先选择后代页面;不要跳转到同级或父级类别。

在一次调用中下载所有候选页面:

uv run ${CLAUDE_SKILL_DIR}/scripts/download.py --log-file {pages_dir}/download.log <<'EOF'
[
  {"url": "LIST_URL_1", "output_dir": "{pages_dir}/list-1", "page_type": "list", "discovered_from": {"page": "start-1", "url": "START_URL"}},
  {"url": "LIST_URL_2", "output_dir": "{pages_dir}/list-2", "page_type": "list", "discovered_from": {"page": "start-1", "url": "START_URL"}}
]
EOF

在一次批量调用中提取所有列表页的链接并保存分组:

uv run ${CLAUDE_SKILL_DIR}/scripts/extract_links.py {pages_dir}/list-*/rendered.html --group --base-url-from-meta > {pages_dir}/list-groups.txt

读取编号分组(每组都有一个 "file" 键,例如 "list-1"),进行分类,然后应用:

uv run ${CLAUDE_SKILL_DIR}/scripts/apply_classification.py {pages_dir}/list-groups.txt --classify "items=N,N subCategories=N,N nextPage=N" --pages-dir {pages_dir}

分类后,检查每个页面的 links.json。如果列表页没有 items,则在其 meta.json 中添加 "warning": "no_items"——这很可能表示链接分类有误。

3. 跟进子子类别(深入导航)

检查是否有列表页的 subCategories 链接指向当前范围内更深层级的页面。如果有,并且仍需更多列表页才能达到 LIST_COUNT:

  • 选择 1-2 个不同的子子类别 URL
  • 下载并分类
  • 这些页面会成为额外的列表页,其 discovered_from 指向父列表页

这样可以深入而不扩大爬取范围。即使已达到 LIST_COUNT,只要有符合范围的子类别,也要跟进 1 个更深层级的页面。

4. 选择并下载详情页

收集起始页面和所有列表页中的全部 items 链接。选择 DETAIL_COUNT 个具有多样性的候选页面:

  • URL 中的 ID 或 slug 不同
  • 尽可能来自不同的类别或列表页
  • 不是同一商品的变体(例如不同颜色或尺寸)

记录每个候选页面的来源页面(用于反向链接)。

在一次调用中下载所有候选页面:

uv run ${CLAUDE_SKILL_DIR}/scripts/download.py --log-file {pages_dir}/download.log <<'EOF'
[
  {"url": "ITEM_URL_1", "output_dir": "{pages_dir}/detail-1", "page_type": "detail", "discovered_from": {"page": "list-1", "url": "LIST_URL_1"}},
  {"url": "ITEM_URL_2", "output_dir": "{pages_dir}/detail-2", "page_type": "detail", "discovered_from": {"page": "list-2", "url": "LIST_URL_2"}}
]
EOF
5. 清理

删除下载失败后留下的空目录。

将剩余页面重新编号,确保连续无间隔:detail-1、detail-2 等。

6. 生成导航值

链接分类完成后,根据 links.json 文件生成导航值:

uv run ${CLAUDE_SKILL_DIR}/scripts/extract_nav_values.py {project_path}

此操作会读取 {pages_dir}/ 中每个页面的 links.json 和 meta.json,并将导航值写入 {project_path}/values/。只有经过链接分类的页面才会生成值文件。

7. 返回摘要
Explored {domain}:
  start: 1 page
  list: N pages (target: LIST_COUNT)
  detail: N pages (target: DETAIL_COUNT)

Pages saved to {pages_dir}/

如果未达到目标数量,请说明原因。

返回起始页面的链接分类结果:条目数量、子类别数量,以及几个示例 URL。这对调用方很有帮助。

评论

支持 Markdown