Scrapling 如何用 SitemapSpider 基于 sitemap 自动发现并爬取整个站点
当你拿到一个站点的 sitemap.xml,想把它列出的所有页面批量抓下来,但又不想为每一类 URL 手写 parse() 样板代码时,Scrapling 的 spider 系统提供了现成的 SitemapSpider 模板:你只需声明 sitemap_urls 和一组基于 LinkExtractor 的规则,它会自动解析 sitemap(包括 sitemap 索引),把每个 URL 分发给第一个匹配的回调处理。本文按「安装 → 编写 spider → 运行 → 验证结果」的顺序走通这条路径,并覆盖 sitemap 索引、robots.txt 入口和多语言 alternate URL 三个可选分支。
前提条件:Python 3.10 或更高版本。
安装:spider 功能需要 fetchers 依赖
Scrapling 的裸装命令只包含解析引擎,不包含 fetchers 和 spiders 所需的依赖,此时 import scrapling.spiders 会抛 ModuleNotFoundError:
pip install scrapling
使用 spider(包括 SitemapSpider)前必须安装 fetchers 依赖:
pip install "scrapling[fetchers]"
scrapling install # 下载浏览器及其系统依赖
scrapling install --force # 强制重装
scrapling install 会下载所有浏览器及其系统依赖和指纹操作依赖;如果只走 HTTP 请求,这一步也是 fetchers 安装流程的一部分。安装完成后,from scrapling.spiders import SitemapSpider 才能正常导入。
SitemapSpider 如何分发 sitemap 里的 URL
理解分发规则,才能解释为什么某些 URL 没有出现在结果里。SitemapSpider 继承自 Spider,内部机制(见 generic-templates.md 与 sitemap.py):
start_urls被换成sitemap_urls:未设置sitemap_urls时,start_requests()直接抛出RuntimeError("SitemapSpider needs sitemap_urls to be set.")。- 每个 sitemap URL 先由内部的
_parse_sitemap回调解析。遇到<sitemapindex>(sitemap 的 sitemap)时,自动递归下载每个子 sitemap;遇到<urlset>时提取每个<url>的<loc>。 - gzip 压缩的 sitemap(
.xml.gz或 gzip content-type)会被自动解压;XML 解析失败只记录一条 warning(如 "Failed to parse sitemap XML"),不会中断整个爬取。 - 拿到 URL 列表后,
SitemapSpider按顺序用每条规则的LinkExtractor.matches(url)逐个检查,第一个匹配的规则获胜,该 URL 以这条规则的 callback 发出请求。 - 有规则但没有任何规则匹配:该 URL 被丢弃。
rules()返回空列表:所有 URL 路由到 spider 的parse()方法;parse()默认实现会抛NotImplementedError,所以要么写规则,要么自己覆写parse()。
LinkExtractor 的 URL 过滤参数(来自 generic-templates.md 的参数表):
| 参数 | 默认值 | 说明 |
|---|---|---|
allow |
() |
保留的 URL 模式,空表示匹配全部 |
deny |
() |
丢弃的 URL 模式,永远优先于 allow |
allow_domains |
() |
保留的主机名,子域自动匹配 |
deny_domains |
() |
丢弃的主机名 |
deny_extensions |
IGNORED_EXTENSIONS |
丢弃的文件扩展名(pdf、zip、图片、视频等) |
注意 deny_extensions 有默认值:即使 allow 匹配,带 pdf、zip、图片等扩展名的 sitemap URL 也会被丢弃。
编写并运行一个 SitemapSpider
以下代码结构来自 generic-templates.md 的官方示例。使用前需要替换:sitemap_urls 换成目标站点的真实 sitemap 地址,rules() 里的 allow 正则和回调选择器按你的站点结构调整。
from scrapling.spiders import SitemapSpider, CrawlRule, LinkExtractor
class MySitemap(SitemapSpider):
name = "sm"
sitemap_urls = ["https://example.com/sitemap.xml"]
def rules(self):
return [
CrawlRule(LinkExtractor(allow=r"/posts/"), callback=self.parse_post),
CrawlRule(LinkExtractor(allow=r"/products/"), callback=self.parse_product),
]
async def parse_post(self, response):
yield {"title": response.css("h1::text").get()}
async def parse_product(self, response):
yield {"sku": response.css(".sku::text").get()}
result = MySitemap().start()
几个要点:
- 回调必须是 async generator(
async def+yield),与普通Spider一致。 CrawlRule除callback外还支持可选的priority覆盖和process_request(在请求发出前修改它)。start()内部处理全部异步机制,爬取过程会记录到终端,结束后返回CrawlResult对象。
验证爬取结果
start() 返回的 CrawlResult 是判断是否成功的依据(见 getting-started.md):
result = MySitemap().start()
# 爬完还是被暂停了?
print(f"Completed: {result.completed}")
# 访问抓到的条目
for item in result.items:
print(item)
# 查看统计
print(f"Scraped {result.stats.items_scraped} items")
print(f"Made {result.stats.requests_count} requests")
print(f"Failed: {result.stats.failed_requests_count}")
print(f"Took {result.stats.elapsed_seconds:.1f} seconds")
条目可以用内置方法直接导出,父目录不存在时会自动创建:
result.items.to_json("sm.json", indent=True) # JSON
result.items.to_jsonl("sm.jsonl") # 每行一个 JSON 对象
result.items.to_csv("sm.csv") # CSV
result.items.to_xml("sm.xml") # XML
判断爬取是否正常,看三件事:result.completed 是否为 True(False 且 result.paused 为 True 说明被 Ctrl+C 暂停,可重新运行恢复);stats.failed_requests_count 有多少失败请求;stats.items_scraped 是否接近你预期的 sitemap 条目数。如果发现 sitemap 里的 URL 数量明显多于实际请求数,通常是有 URL 没匹配任何规则被丢弃了,回去检查 rules() 的 allow 正则和 deny_extensions 默认过滤。
完整统计清单(状态码分布、被 robots.txt 拦截数、按域的字节数等)见 advanced.md 的 "Results & Statistics" 一节。
可选分支:三种 sitemap 入口与过滤
只爬部分子 sitemap(sitemap 索引)
当根 sitemap 是 <sitemapindex> 时,SitemapSpider 默认递归进入所有子 sitemap。用 sitemap_follow 指定一个 LinkExtractor,只进入匹配的子 sitemap(None 表示进入全部):
class MySitemap(SitemapSpider):
name = "sm"
sitemap_urls = ["https://example.com/sitemap.xml"]
sitemap_follow = LinkExtractor(allow=r"/posts-sitemap-\d+\.xml") # 只进文章 sitemap
用 robots.txt 作为入口
把 robots.txt 地址直接放进 sitemap_urls,SitemapSpider 会识别它,提取其中所有 Sitemap: 指令并逐个跟进:
class MySitemap(SitemapSpider):
name = "sm"
sitemap_urls = ["https://example.com/robots.txt"]
如果 robots.txt 里没有 Sitemap 指令,会记录一条 "No Sitemaps found in ..." 的 warning,爬取不会拿到任何 URL。
爬多语言版本页面
设置 sitemap_alternate_links = True,<xhtml:link rel="alternate" hreflang="..."> 中的地址也会被提取并同样走 rules() 分发:
class MySitemap(SitemapSpider):
name = "sm"
sitemap_urls = ["https://example.com/sitemap.xml"]
sitemap_alternate_links = True
限制与边界
- 未设置
sitemap_urls会直接抛RuntimeError,不存在其他默认入口。 - 有规则时,不匹配任何规则的 sitemap URL 被静默丢弃;sitemap 里的 PDF、图片等静态资源 URL 受
deny_extensions默认值影响同样会被丢弃。 - sitemap 本身损坏(gzip 解压失败或 XML 语法错误)只产生 warning 并跳过该 sitemap,需要到爬取日志里找 "Failed to decompress sitemap" / "Failed to parse sitemap XML" 来定位是哪个 sitemap 没被解析。
- 如果站点还有 robots.txt 抓取限制,可以像普通 spider 一样设置
robots_txt_obey = True,被 Disallow 的请求会计入stats.robots_disallowed_count。 - 大站点爬取被中断时,可通过构造参数
crawldir启用检查点机制(暂停/恢复、断点续爬),用法见 advanced.md 的 "Pause & Resume" 一节。
更多 spider 能力(并发控制 concurrent_requests/download_delay、AutoThrottle、流式输出 stream()、生命周期钩子)参考 advanced.md;如果不用模板、想在自己的 Spider.parse() 里直接取链接,LinkExtractor 也可以单独使用,示例同在 generic-templates.md。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
