XML站点地图是网站用来告知搜索引擎可抓取页面地址的标准化文件,通常遵循Sitemap协议,内容由规范的XML标签组成,使用Scrapy解析这类文件可以快速批量获取目标站点的页面链接,减少逐个页面发现的耗时。

XML站点地图的基本结构
标准的XML站点地图结构如下,核心内容放在<urlset>标签内,每个<url>节点对应一个页面的相关信息,其中<loc>标签存放页面的具体访问地址,是我们解析的核心目标。
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://ipipp.com/page1.html</loc>
<lastmod>2023-10-01</lastmod>
<changefreq>daily</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://ipipp.com/page2.html</loc>
<lastmod>2023-10-02</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
</urlset>
Scrapy解析普通XML站点地图的实现
我们可以直接在Scrapy的爬虫中发送请求获取站点地图内容,然后使用内置的Selector工具解析XML内容,提取所有<loc>标签内的链接。
创建基础爬虫文件
首先创建一个Scrapy爬虫项目,然后编写如下爬虫代码,实现站点地图的请求和解析。
import scrapy
class SitemapSpider(scrapy.Spider):
name = "sitemap_spider"
# 目标站点的XML站点地图地址
start_urls = ["https://ipipp.com/sitemap.xml"]
def parse(self, response):
# 使用xpath提取所有loc标签的内容,即页面链接
page_urls = response.xpath("//loc/text()").getall()
for url in page_urls:
yield {
"page_url": url
}
# 可以直接跟进请求解析页面内容
yield scrapy.Request(
url=url,
callback=self.parse_page
)
def parse_page(self, response):
# 解析单个页面的逻辑,这里示例提取页面标题
title = response.xpath("//title/text()").get()
yield {
"url": response.url,
"title": title
}
代码逻辑说明
- start_urls设置站点地图的访问地址,Scrapy会自动发送请求获取内容
- parse方法中通过xpath选择器匹配所有<loc>标签的文本内容,得到所有页面链接
- 可以先把链接yield出来保存,也可以直接发送新的请求跟进解析每个页面的内容
- parse_page方法用来处理单个页面的解析逻辑,可根据实际需求调整
处理压缩格式的站点地图
很多站点为了节省带宽,会提供gzip压缩后的站点地图文件,后缀通常为sitemap.xml.gz,Scrapy默认会自动处理gzip压缩的响应内容,不需要额外配置解压逻辑。
只需要把start_urls中的地址换成压缩文件的地址即可,比如:
start_urls = ["https://ipipp.com/sitemap.xml.gz"]
Scrapy的响应处理机制会自动识别gzip压缩内容并解压,后续的解析逻辑和普通XML站点地图完全一致。
解析分页站点地图
部分大型站点的内容较多,会把站点地图拆分成多个文件,主站点地图中会列出所有子站点地图的地址,子站点地图中才是具体的页面链接,这时候需要递归解析。
主站点地图的结构通常如下:
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://ipipp.com/sitemap_1.xml</loc>
<lastmod>2023-10-01</lastmod>
</sitemap>
<sitemap>
<loc>https://ipipp.com/sitemap_2.xml</loc>
<lastmod>2023-10-01</lastmod>
</sitemap>
</sitemapindex>
对应的解析逻辑需要调整,先提取子站点地图的地址,再逐个请求子站点地图提取页面链接:
import scrapy
class MultiSitemapSpider(scrapy.Spider):
name = "multi_sitemap_spider"
start_urls = ["https://ipipp.com/sitemap_index.xml"]
def parse(self, response):
# 提取所有子站点地图的地址
sitemap_urls = response.xpath("//sitemap/loc/text()").getall()
for sitemap_url in sitemap_urls:
yield scrapy.Request(
url=sitemap_url,
callback=self.parse_sitemap
)
def parse_sitemap(self, response):
# 解析子站点地图中的页面链接
page_urls = response.xpath("//url/loc/text()").getall()
for url in page_urls:
yield {
"page_url": url
}
注意事项
- 部分站点的站点地图地址可能不是默认的/sitemap.xml,需要查看站点的robots.txt文件获取正确的站点地图地址,通常robots.txt中会标注Sitemap: 开头的条目
- 解析时要注意站点地图的编码格式,如果出现乱码可以手动指定response的编码,比如response.encoding = "utf-8"
- 请求站点地图时要遵守站点的爬虫协议,控制请求频率,避免给目标站点造成过大压力
- 如果站点地图内容较大,解析时可以按需提取需要的链接,不需要全部处理