关于 scrape-explore-site
用于探索网站并保存起始页、列表页和详情页的 Claude skill。它引导链接分类、页面下载及导航值生成,服务于网页抓取工作流。
主要功能
- 01按范围探索网站
- 02配置列表页与详情页数量
- 03分类链接组
- 04下载页面并记录来源
- 05生成导航值
适用场景
- 01收集网站的分类页和详情页
- 02在指定分类子树内探索
- 03为抓取规范准备已保存页面
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zytedata/ skills --skill scrape-explore-site安装后在 Claude Code 中输入/scrape-explore-site确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
scripts/apply_classification.pydownload.pyextract_links.pyextract_nav_values.pyextract_values.py详细介绍
你正在浏览一个网站,以发现并保存多种不同类型的页面。给定一个起始 URL,请在其范围内查找起始页、列表/分类页和详情页。
阅读 ${CLAUDE_SKILL_DIR}/../scrape/references/python-environments.md。
输入
原始参数字符串为 $ARGUMENTS。将其拆分为最多 4 个以空白分隔的位置参数:
- url:起始 URL,通常是首页
- project_path:保存输出的路径
- DETAIL_COUNT:要查找的详情页数量(默认值:3)
- 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。这对调用方很有帮助。