sitemap.xml 是爬虫最省力的 URL 来源,但解析它的坑不少:命名空间、两种 gzip、五花八门的 lastmod、几万条 URL 的内存、以及不规范的 XML。这篇用一个本机服务器把这些情形逐一复现。
根元素标签:{http://www.sitemaps.org/schemas/sitemap/0.9}sitemapindex
不写命名空间 root.findall("sitemap/loc") → 0 个;写上命名空间 root.findall("{ns}sitemap/{ns}loc") → 3 个
用 lxml 的 xpath("//*[local-name()='loc']/text()")(不依赖命名空间)→ 3 个sitemap 的所有元素都在 http://www.sitemaps.org/schemas/sitemap/0.9 命名空间里。findall("sitemap/loc") 找不到任何东西,必须写 {ns}sitemap/{ns}loc,或者用 xpath 的 local-name() 绕过。
/sitemap2.xml.gz:Content-Type=application/gzip,Content-Encoding=None;requests 给出的 r.content 开头字节 1f8b(仍是 gzip,需要自己解压) /sitemap3.xml:Content-Type=application/xml,Content-Encoding=gzip;requests 给出的 r.content 开头字节 3c3f(已经是明文 XML,requests 解开了 HTTP 层的 gzip) 对 .xml.gz 自己 gzip.decompress 之后能解析:5 个 URL 不解压直接 ET.fromstring(r.content):ParseError: not well-formed (invalid token): line 1, column 0
.xml.gz 是文件本身压缩,requests 不会替你解压;Content-Encoding: gzip 是 HTTP 层压缩,requests 自动解开。判断依据是内容的开头字节(1f 8b 表示仍是 gzip),不是文件扩展名。
lastmod '2026-10-11' → 2026-10-11 00:00:00+00:00 lastmod '2026-10-11T08:30:00+08:00' → 2026-10-11 08:30:00+08:00 lastmod '2026-10-11T00:30:00Z' → 2026-10-11 00:30:00+00:00 lastmod '2026-10-11 08:30' → 2026-10-11 08:30:00+00:00 lastmod '11/10/2026' → None lastmod 'Sun, 11 Oct 2026 00:30:00 GMT' → None lastmod '' → None 3 个子 sitemap 共 15 条 URL;上次抓取时间 2026-10-05:lastmod 更新的 5 条,没有 lastmod 的 5 条(这种条目不能用来判断是否更新,只能当作"需要抓")
datetime.fromisoformat 能解析日期、带时区的时间和 Z 结尾的时间;11/10/2026 或 RFC 2822 日期解析不了。没有 lastmod 的条目(例子里 5/15)不能用来判断是否更新,增量抓取要把它们当作“需要抓”。
文件大小 6.8 MiB(未压缩),gzip 后 0.26 MiB ElementTree.fromstring:50000 条,312 ms,内存峰值 38.5 MiB ElementTree.iterparse(边读边 clear):50000 条,389 ms,内存峰值 4.0 MiB
5 万条 URL 的 6.8 MiB sitemap,fromstring 内存峰值 38.5 MiB,iterparse 边读边 clear 只要 4.0 MiB(多花 77 ms)。协议允许的上限我没有在这里核对(我印象里是 5 万条或 50 MB,以官方文档为准)。
未转义的 & :标准库 ElementTree 报错 ParseError: not well-formed (invalid token): line 1, column 115
lxml 的 recover=True 能读出 4 条:['http://shop.test/a?x=1=2', 'http://other.test/zzz', '/relative/path', 'http://shop.test/a?x=1y=2']
http://shop.test/a?x=1=2 → 同主机
http://other.test/zzz → 跨主机(与 sitemap 所在主机不同)
/relative/path → 相对路径,没有主机
http://shop.test/a?x=1y=2 → 同主机XML 里 & 没转义是常见错误。标准库直接报错;lxml 的 recover=True 能读,但把 x=1&y=2 读成 x=1=2,甚至把旁边写对了的 x=1&y=2 读成 x=1y=2。另外 sitemap 里可能有跨主机的 URL 和相对路径,要按所在站点校验后再抓。