爬虫

爬虫是定义如何抓取特定网站(或一组网站)的类,包括如何执行爬取(即跟踪链接)以及如何从其页面中提取结构化数据(即抓取项目)。换句话说,爬虫是您定义特定网站(或在某些情况下是一组网站)的爬取和解析页面的自定义行为的地方。

对于爬虫,抓取周期大致如下

  1. 首先,您生成初始请求以爬取第一个 URL,并指定一个回调函数,以便在从这些请求下载响应后调用该函数。

    要执行的第一个请求是通过迭代 start() 方法获得的,该方法默认会为 start_urls 爬虫属性中的每个 URL 生成一个 Request 对象,并将 parse 方法设置为 callback 函数以处理每个 Response

  2. 在回调函数中,您解析响应(网页)并返回 项目对象Request 对象,或这些对象的可迭代对象。这些 Request 也将包含一个回调(可能相同),然后由 Scrapy 下载,其响应再由指定的回调函数处理。

  3. 在回调函数中,您通常使用 选择器(但您也可以使用 BeautifulSoup、lxml 或任何您喜欢的机制)解析页面内容,并使用解析后的数据生成项目。

  4. 最后,从爬虫返回的项目通常会持久化到数据库(通过 项目管道)或使用 Feed 导出写入文件。

尽管这个周期或多或少适用于任何类型的爬虫,但 Scrapy 中捆绑了不同类型的默认爬虫以用于不同目的。我们将在本文中讨论这些类型。

scrapy.Spider

class scrapy.spiders.Spider
class scrapy.Spider(*args: Any, **kwargs: Any)[source]

任何爬虫都必须继承的基类。

它提供了一个默认的 start() 实现,该实现根据 start_urls 类属性发送请求,并为每个响应调用 parse() 方法。

name

一个字符串,定义此爬虫的名称。爬虫名称是 Scrapy 定位(并实例化)爬虫的方式,因此它必须是唯一的。但是,这不妨碍您实例化同一爬虫的多个实例。这是最重要的爬虫属性,并且是必需的。

如果爬虫抓取单个域名,通常的做法是将爬虫命名为该域名,无论是否包含 顶级域名(TLD)。例如,一个抓取 mywebsite.com 的爬虫通常会被命名为 mywebsite

allowed_domains

一个可选的字符串列表,包含此爬虫被允许抓取的域名。如果启用了 OffsiteMiddleware,则不属于此列表中指定域名(或其子域名)的 URL 请求将不会被跟踪。

假设您的目标 URL 是 https://www.example.com/1.html,那么请将 'example.com' 添加到列表中。

start_urls: list[str]

起始 URL。参见 start()

custom_settings

一个设置字典,运行此爬虫时将覆盖项目范围的配置。它必须定义为类属性,因为设置在实例化之前会进行更新。

有关可用内置设置的列表,请参阅:内置设置参考

crawler

此属性由 from_crawler() 类方法在类初始化后设置,并链接到此爬虫实例所绑定的 Crawler 对象。

Crawler 封装了项目中的许多组件,以实现其单点访问(例如扩展、中间件、信号管理器等)。有关它们的更多信息,请参阅 Crawler API

settings

运行此爬虫的配置。这是一个 Settings 实例,有关此主题的详细介绍,请参阅 设置 主题。

logger

使用爬虫的 名称 创建的 Python 日志器。您可以按照 从爬虫进行日志记录 中的说明,通过它发送日志消息。

state

一个字典,可用于在批次之间持久化一些爬虫状态。有关更多信息,请参阅 在批次之间保持持久状态

from_crawler(crawler, *args, **kwargs)[source]

这是 Scrapy 用来创建您的爬虫的类方法。

您可能不需要直接覆盖此方法,因为默认实现充当 __init__() 方法的代理,使用给定的位置参数 args 和命名参数 kwargs 调用它。

尽管如此,此方法在新实例中设置了 crawlersettings 属性,以便稍后在爬虫代码中访问它们。

2.11 版本变更: 现在可以在此方法中修改 crawler.settings 中的设置,如果您想根据参数修改它们,这会很方便。因此,这些设置不是最终值,因为它们可能会在稍后被例如 附加组件 修改。出于同样的原因,大多数 Crawler 属性在此阶段尚未初始化。

最终设置和已初始化的 Crawler 属性在 start() 方法、engine_started 信号的处理程序以及后续阶段中可用。

参数:
classmethod update_settings(settings)[source]

update_settings() 方法用于修改爬虫的设置,并在爬虫实例初始化期间调用。

它将一个 Settings 对象作为参数,可以添加或更新爬虫的配置值。此方法是一个类方法,这意味着它在 Spider 类上调用,并允许爬虫的所有实例共享相同的配置。

虽然可以在 custom_settings 中设置每个爬虫的设置,但使用 update_settings() 允许您根据其他设置、爬虫属性或其他因素动态添加、删除或更改设置,并使用除 'spider' 之外的设置优先级。此外,在子类中通过覆盖 update_settings() 可以轻松扩展它,而对 custom_settings 执行相同的操作可能会很困难。

例如,假设一个爬虫需要修改 FEEDS

import scrapy


class MySpider(scrapy.Spider):
    name = "myspider"
    custom_feed = {
        "/home/user/documents/items.json": {
            "format": "json",
            "indent": 4,
        }
    }

    @classmethod
    def update_settings(cls, settings):
        super().update_settings(settings)
        settings.setdefault("FEEDS", {}).update(cls.custom_feed)
async start() AsyncIterator[Any][source]

生成要发送的初始 Request 对象。

2.13 版本中新增。

例如

from scrapy import Request, Spider


class MySpider(Spider):
    name = "myspider"

    async def start(self):
        yield Request("https://toscrape.com/")

默认实现从 start_urls 读取 URL,并为每个 URL 生成一个启用 dont_filter 的请求。它在功能上等同于

async def start(self):
    for url in self.start_urls:
        yield Request(url, dont_filter=True)

您也可以生成 项目。例如

async def start(self):
    yield {"foo": "bar"}

要编写适用于低于 2.13 版本的 Scrapy 的爬虫,还需要定义一个返回可迭代对象的同步 start_requests() 方法。例如

def start_requests(self):
    yield Request("https://toscrape.com/")

另请参阅

起始请求

parse(response)[source]

这是 Scrapy 用于处理已下载响应的默认回调,当其请求未指定回调时使用。

parse 方法负责处理响应并返回抓取到的数据和/或更多要跟踪的 URL。其他 Request 回调函数与 Spider 类具有相同的要求。

此方法以及任何其他 Request 回调函数必须返回一个 Request 对象、一个 项目对象、一个包含 Request 对象和/或 项目对象 的可迭代对象,或者 None

参数:

response (Response) – 要解析的响应

log(message[, level, component])[source]

一个包装器,用于通过爬虫的 日志器 发送日志消息,保留以实现向后兼容性。有关更多信息,请参阅 从爬虫进行日志记录

closed(reason)

在爬虫关闭时调用。此方法为 spider_closed 信号提供了 signals.connect() 的快捷方式。

让我们看一个示例

import scrapy


class MySpider(scrapy.Spider):
    name = "example.com"
    allowed_domains = ["example.com"]
    start_urls = [
        "http://www.example.com/1.html",
        "http://www.example.com/2.html",
        "http://www.example.com/3.html",
    ]

    def parse(self, response):
        self.logger.info("A response from %s just arrived!", response.url)

从单个回调返回多个 Request 和项目

import scrapy


class MySpider(scrapy.Spider):
    name = "example.com"
    allowed_domains = ["example.com"]
    start_urls = [
        "http://www.example.com/1.html",
        "http://www.example.com/2.html",
        "http://www.example.com/3.html",
    ]

    def parse(self, response):
        for h3 in response.xpath("//h3").getall():
            yield {"title": h3}

        for href in response.xpath("//a/@href").getall():
            yield scrapy.Request(response.urljoin(href), self.parse)

您可以直接使用 start() 而不是 start_urls;为了使数据更有结构,您可以使用 Item 对象

import scrapy
from myproject.items import MyItem


class MySpider(scrapy.Spider):
    name = "example.com"
    allowed_domains = ["example.com"]

    async def start(self):
        yield scrapy.Request("http://www.example.com/1.html", self.parse)
        yield scrapy.Request("http://www.example.com/2.html", self.parse)
        yield scrapy.Request("http://www.example.com/3.html", self.parse)

    def parse(self, response):
        for h3 in response.xpath("//h3").getall():
            yield MyItem(title=h3)

        for href in response.xpath("//a/@href").getall():
            yield scrapy.Request(response.urljoin(href), self.parse)

爬虫参数

爬虫可以接收修改其行为的参数。爬虫参数的一些常见用途是定义起始 URL 或将爬取限制在网站的特定部分,但它们可以用于配置爬虫的任何功能。

爬虫参数通过 crawl 命令使用 -a 选项传递。例如

scrapy crawl myspider -a category=electronics

爬虫可以在其 __init__ 方法中访问参数

import scrapy


class MySpider(scrapy.Spider):
    name = "myspider"

    def __init__(self, category=None, *args, **kwargs):
        super(MySpider, self).__init__(*args, **kwargs)
        self.start_urls = [f"http://www.example.com/categories/{category}"]
        # ...

默认的 __init__ 方法将获取任何爬虫参数并将其作为属性复制到爬虫中。上面的示例也可以写成如下形式

import scrapy


class MySpider(scrapy.Spider):
    name = "myspider"

    async def start(self):
        yield scrapy.Request(f"http://www.example.com/categories/{self.category}")

如果您 从脚本运行 Scrapy,可以在调用 CrawlerProcess.crawlCrawlerRunner.crawl 时指定爬虫参数

process = CrawlerProcess()
process.crawl(MySpider, category="electronics")

请记住,爬虫参数仅是字符串。爬虫不会自行进行任何解析。如果您要从命令行设置 start_urls 属性,则必须使用 ast.literal_eval()json.loads() 等方法将其解析为列表,然后将其设置为属性。否则,您将导致迭代 start_urls 字符串(一个非常常见的 Python 陷阱),从而导致每个字符都被视为一个单独的 URL。

爬虫参数也可以通过 Scrapyd schedule.json API 传递。参见 Scrapyd 文档

scrapy-spider-metadata 参数

传递爬虫参数的另一种替代方法是使用库 scrapy-spider-metadata

这允许 Scrapy 爬虫将其参数定义、验证、文档化和预处理为 Pydantic 模型。

此示例展示了如何定义类型化参数,其中字符串参数会自动转换为整数

import scrapy
from pydantic import BaseModel
from scrapy_spider_metadata import Args


class MyParams(BaseModel):
    pages: int


class BookSpider(Args[MyParams], scrapy.Spider):
    name = "bookspider"
    start_urls = ["http://books.toscrape.com/catalogue"]

    async def start(self):
        for start_url in self.start_urls:
            for index in range(1, self.args.pages + 1):
                yield scrapy.Request(f"{start_url}/page-{index}.html")

    def parse(self, response):
        book_links = response.css("article.product_pod h3 a::attr(href)").getall()
        for book_link in book_links:
            yield response.follow(book_link, self.parse_book)

    def parse_book(self, response):
        yield {
            "title": response.css("h1::text").get(),
            "price": response.css("p.price_color::text").get(),
        }

此爬虫可以从命令行调用

scrapy crawl bookspider -a pages=2

起始请求

起始请求 是从爬虫的 start() 方法或 爬虫中间件process_start() 方法中生成的 Request 对象。

另请参阅

起始请求顺序

延迟起始请求迭代

您可以如下覆盖 start() 方法,以便在有已调度请求时暂停其迭代

async def start(self):
    async for item_or_request in super().start():
        if self.crawler.engine.needs_backout():
            await self.crawler.signals.wait_for(signals.scheduler_empty)
        yield item_or_request

这有助于在任何给定时间最大程度地减少调度器中的请求数量,从而最大程度地减少资源使用(内存或磁盘,具体取决于 JOBDIR)。

通用爬虫

Scrapy 附带了一些有用的通用爬虫,您可以从中继承您的爬虫。它们旨在为一些常见的抓取情况提供便利功能,例如根据特定规则跟踪网站上的所有链接、从 Sitemap 抓取,或解析 XML/CSV 提要。

对于以下爬虫中使用的示例,我们将假设您有一个项目,其中在 myproject.items 模块中声明了一个 TestItem

from dataclasses import dataclass


@dataclass
class TestItem:
    id: str | None = None
    name: str | None = None
    description: str | None = None

CrawlSpider

class scrapy.spiders.CrawlSpider[source]

这是用于爬取常规网站最常用的爬虫,因为它通过定义一组规则提供了方便的链接跟踪机制。它可能不是最适合您特定网站或项目的爬虫,但它足够通用,适用于多种情况,因此您可以从它开始,并根据需要进行覆盖以实现更多自定义功能,或者只是实现自己的爬虫。

除了从 Spider 继承的属性(您必须指定)之外,此类还支持一个新属性

rules

它是一个包含一个(或多个)Rule 对象的列表。每个 Rule 定义了爬取网站的特定行为。规则对象将在下面描述。如果有多个规则匹配同一个链接,则将根据它们在此属性中定义的顺序使用第一个规则。

此爬虫还公开了一个可覆盖的方法

parse_start_url(response, **kwargs)[source]

此方法为爬虫的 start_urls 属性中的 URL 生成的每个响应调用。它允许解析初始响应,并且必须返回一个 项目对象、一个 Request 对象,或者一个包含其中任何一个的可迭代对象。

爬取规则

class scrapy.spiders.Rule(link_extractor: LinkExtractor | None = None, callback: CallbackT | str | None = None, cb_kwargs: dict[str, Any] | None = None, follow: bool | None = None, process_links: ProcessLinksT | str | None = None, process_request: ProcessRequestT | str | None = None, errback: Callable[[Failure], Any] | str | None = None)[source]

link_extractor 是一个 链接提取器(Link Extractor) 对象,它定义了如何从每个爬取页面中提取链接。每个生成的链接将用于生成一个 Request 对象,该对象将在其 meta 字典中(在 link_text 键下)包含链接的文本。如果省略,将使用一个不带参数创建的默认链接提取器,从而提取所有链接。

callback 是一个可调用对象或一个字符串(在这种情况下,将使用爬虫对象中具有该名称的方法),用于为使用指定链接提取器提取的每个链接调用。此回调接收一个 Response 作为其第一个参数,并且必须返回一个单独的实例或一个 项目对象 和/或 Request 对象(或它们的任何子类)的可迭代对象。如上所述,接收到的 Response 对象将在其 meta 字典中(在 link_text 键下)包含生成 Request 的链接文本。

cb_kwargs 是一个字典,包含要传递给回调函数的关键字参数。

follow 是一个布尔值,指定是否应从此规则提取的每个响应中跟踪链接。如果 callback 为 None,则 follow 默认为 True,否则默认为 False

process_links 是一个可调用对象或一个字符串(在这种情况下,将使用爬虫对象中具有该名称的方法),它将为使用指定 link_extractor 从每个响应中提取的每个链接列表调用。这主要用于过滤目的。

process_request 是一个可调用对象(或一个字符串,在这种情况下,将使用爬虫对象中具有该名称的方法),它将为该规则提取的每个 Request 调用。此可调用对象应将该请求作为第一个参数,并将请求来源的 Response 作为第二个参数。它必须返回一个 Request 对象或 None(以过滤掉请求)。

errback 是一个可调用对象或一个字符串(在这种情况下,将使用爬虫对象中具有该名称的方法),用于在处理由规则生成的请求时引发任何异常时调用。它接收一个 Twisted Failure 实例作为第一个参数。

警告

由于其内部实现,在编写基于 CrawlSpider 的爬虫时,您必须为新请求显式设置回调;否则可能会发生意外行为。

CrawlSpider 示例

现在让我们看一个带有规则的 CrawlSpider 示例

from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor


class MySpider(CrawlSpider):
    name = "example.com"
    allowed_domains = ["example.com"]
    start_urls = ["http://www.example.com"]

    rules = (
        # Extract links matching 'category.php' (but not matching 'subsection.php')
        # and follow links from them (since no callback means follow=True by default).
        Rule(LinkExtractor(allow=(r"category\.php",), deny=(r"subsection\.php",))),
        # Extract links matching 'item.php' and parse them with the spider's method parse_item
        Rule(LinkExtractor(allow=(r"item\.php",)), callback="parse_item"),
    )

    def parse_item(self, response):
        self.logger.info("Hi, this is an item page! %s", response.url)
        item = {}
        item["id"] = response.xpath('//td[@id="item_id"]/text()').re(r"ID: (\d+)")
        item["name"] = response.xpath('//td[@id="item_name"]/text()').get()
        item["description"] = response.xpath(
            '//td[@id="item_description"]/text()'
        ).get()
        item["link_text"] = response.meta["link_text"]
        url = response.xpath('//td[@id="additional_data"]/@href').get()
        return response.follow(
            url, self.parse_additional_page, cb_kwargs=dict(item=item)
        )

    def parse_additional_page(self, response, item):
        item["additional_data"] = response.xpath(
            '//p[@id="additional_data"]/text()'
        ).get()
        return item

这个爬虫将开始爬取 example.com 的主页,收集类别链接和项目链接,并使用 parse_item 方法解析后者。对于每个项目响应,将使用 XPath 从 HTML 中提取一些数据,并用这些数据填充一个 Item

XMLFeedSpider

class scrapy.spiders.XMLFeedSpider[source]

XMLFeedSpider 旨在通过按特定节点名称迭代 XML 提要来解析它们。迭代器可以从 iternodesxmlhtml 中选择。出于性能原因,建议使用 iternodes 迭代器,因为 xmlhtml 迭代器会一次性生成整个 DOM 以进行解析。然而,在解析标记不佳的 XML 时,使用 html 作为迭代器可能会很有用。

要设置迭代器和标签名称,您必须定义以下类属性

iterator

一个字符串,定义要使用的迭代器。它可以是

  • 'iternodes' - 一个基于正则表达式的快速迭代器

  • 'html' - 一个使用 Selector 的迭代器。请记住,这使用 DOM 解析,并且必须将所有 DOM 加载到内存中,这对于大型提要来说可能是一个问题

  • 'xml' - 一个使用 Selector 的迭代器。请记住,这使用 DOM 解析,并且必须将所有 DOM 加载到内存中,这对于大型提要来说可能是一个问题

它默认为:'iternodes'

itertag

一个字符串,包含要迭代的节点(或元素)的名称。例如

itertag = 'product'
namespaces

一个 (prefix, uri) 元组列表,定义此爬虫将处理的文档中可用的命名空间。prefixuri 将用于使用 register_namespace() 方法自动注册命名空间。

然后,您可以在 itertag 属性中指定带有命名空间的节点。

示例:

class YourSpider(XMLFeedSpider):

    namespaces = [('n', 'http://www.sitemaps.org/schemas/sitemap/0.9')]
    itertag = 'n:url'
    # ...

除了这些新属性之外,此爬虫还具有以下可覆盖的方法

adapt_response(response)[source]

一个方法,它在响应从爬虫中间件到达后,爬虫开始解析之前立即接收响应。它可用于在解析响应主体之前修改它。此方法接收一个响应,并也返回一个响应(可以是相同或不同的响应)。

parse_node(response, selector)[source]

此方法为匹配提供的标签名称(itertag)的节点调用。它接收响应和每个节点的 Selector。覆盖此方法是强制性的。否则,您的爬虫将无法工作。此方法必须返回一个 项目对象、一个 Request 对象,或者一个包含其中任何一个的可迭代对象。

process_results(response, results)[source]

此方法为爬虫返回的每个结果(项目或请求)调用,旨在在将结果返回到框架核心之前执行任何所需的最后处理,例如设置项目 ID。它接收一个结果列表和产生这些结果的响应。它必须返回一个结果列表(项目或请求)。

警告

由于其内部实现,在编写基于 XMLFeedSpider 的爬虫时,您必须为新请求显式设置回调;否则可能会发生意外行为。

XMLFeedSpider 示例

这些爬虫使用起来非常简单,让我们看一个示例

from scrapy.spiders import XMLFeedSpider
from myproject.items import TestItem


class MySpider(XMLFeedSpider):
    name = "example.com"
    allowed_domains = ["example.com"]
    start_urls = ["http://www.example.com/feed.xml"]
    iterator = "iternodes"  # This is actually unnecessary, since it's the default value
    itertag = "item"

    def parse_node(self, response, node):
        self.logger.info(
            "Hi, this is a <%s> node!: %s", self.itertag, "".join(node.getall())
        )

        item = TestItem()
        item.id = node.xpath("@id").get()
        item.name = node.xpath("name").get()
        item.description = node.xpath("description").get()
        return item

基本上,我们上面所做的是创建一个爬虫,它从给定的 start_urls 下载提要,然后迭代其每个 item 标签,将它们打印出来,并将一些随机数据存储在一个 Item 中。

CSVFeedSpider

class scrapy.spiders.CSVFeedSpider[source]

此爬虫与 XMLFeedSpider 非常相似,不同之处在于它迭代的是行,而不是节点。每次迭代中调用的方法是 parse_row()

delimiter

一个字符串,包含 CSV 文件中每个字段的分隔符。默认为 ','(逗号)。

quotechar

一个字符串,包含 CSV 文件中每个字段的包围字符。默认为 '"'(引号)。

headers

CSV 文件中列名的列表。

parse_row(response, row)[source]

接收一个响应和一个字典(表示每一行),字典的键对应 CSV 文件中每个提供(或检测到)的标题。此爬虫还提供了覆盖 adapt_responseprocess_results 方法的机会,以用于预处理和后处理目的。

CSVFeedSpider 示例

让我们看一个与前一个类似的示例,但使用 CSVFeedSpider

from scrapy.spiders import CSVFeedSpider
from myproject.items import TestItem


class MySpider(CSVFeedSpider):
    name = "example.com"
    allowed_domains = ["example.com"]
    start_urls = ["http://www.example.com/feed.csv"]
    delimiter = ";"
    quotechar = "'"
    headers = ["id", "name", "description"]

    def parse_row(self, response, row):
        self.logger.info("Hi, this is a row!: %r", row)

        item = TestItem()
        item.id = row["id"]
        item.name = row["name"]
        item.description = row["description"]
        return item

SitemapSpider

class scrapy.spiders.SitemapSpider[source]

SitemapSpider 允许您通过使用 Sitemap 发现 URL 来抓取网站。

它支持嵌套 Sitemap 并从 robots.txt 中发现 Sitemap URL。

sitemap_urls

一个 URL 列表,指向您要抓取的 Sitemap 文件中的 URL。

您也可以指向 robots.txt 文件,它将被解析以从中提取 Sitemap URL。

sitemap_rules

一个元组列表 (regex, callback),其中

  • regex 是一个用于匹配从 Sitemap 中提取的 URL 的正则表达式。regex 可以是字符串或已编译的正则表达式对象。

  • callback 是用于处理匹配正则表达式的 URL 的回调函数。callback 可以是一个字符串(指示爬虫方法的名称)或一个可调用对象。

例如

sitemap_rules = [('/product/', 'parse_product')]

规则按顺序应用,只有第一个匹配的规则会被使用。

如果您省略此属性,Sitemap 中找到的所有 URL 都将使用 parse 回调函数进行处理。

sitemap_follow

一个 Sitemap 正则表达式列表,应遵循这些 Sitemap。这仅适用于使用指向其他 Sitemap 文件的 Sitemap 索引文件 的网站。

默认情况下,所有 Sitemap 都会被跟踪。

指定是否应跟踪一个 url 的备用链接。这些是同一网站中不同语言的链接,在相同的 url 块内传递。

例如

<url>
    <loc>http://example.com/</loc>
    <xhtml:link rel="alternate" hreflang="de" href="http://example.com/de"/>
</url>

设置了 sitemap_alternate_links 后,这将检索两个 URL。禁用 sitemap_alternate_links 后,将只检索 http://example.com/

默认情况下,sitemap_alternate_links 是禁用的。

sitemap_filter(entries)[source]

这是一个过滤函数,可以被覆盖以根据 Sitemap 条目的属性选择它们。

例如

<url>
    <loc>http://example.com/</loc>
    <lastmod>2005-01-01</lastmod>
</url>

我们可以定义一个 sitemap_filter 函数,按日期过滤 entries

from datetime import datetime
from scrapy.spiders import SitemapSpider


class FilteredSitemapSpider(SitemapSpider):
    name = "filtered_sitemap_spider"
    allowed_domains = ["example.com"]
    sitemap_urls = ["http://example.com/sitemap.xml"]

    def sitemap_filter(self, entries):
        for entry in entries:
            date_time = datetime.strptime(entry["lastmod"], "%Y-%m-%d")
            if date_time.year >= 2005:
                yield entry

这将只检索在 2005 年及以后修改的 entries

条目是从 Sitemap 文档中提取的字典对象。通常,键是标签名称,值是标签内的文本。

需要注意的是

  • 由于 loc 属性是必需的,没有此标签的条目将被丢弃

  • 备用链接存储在一个列表中,键为 alternate(参见 sitemap_alternate_links

  • 命名空间被移除,因此名为 {namespace}tagname 的 lxml 标签仅变为 tagname

如果您省略此方法,Sitemap 中找到的所有条目都将根据其他属性及其设置进行处理。

SitemapSpider 示例

最简单的示例:使用 parse 回调函数处理通过 Sitemap 发现的所有 URL

from scrapy.spiders import SitemapSpider


class MySpider(SitemapSpider):
    sitemap_urls = ["http://www.example.com/sitemap.xml"]

    def parse(self, response):
        pass  # ... scrape item here ...

使用特定回调函数处理一些 URL,使用不同回调函数处理其他 URL

from scrapy.spiders import SitemapSpider


class MySpider(SitemapSpider):
    sitemap_urls = ["http://www.example.com/sitemap.xml"]
    sitemap_rules = [
        ("/product/", "parse_product"),
        ("/category/", "parse_category"),
    ]

    def parse_product(self, response):
        pass  # ... scrape product ...

    def parse_category(self, response):
        pass  # ... scrape category ...

遵循 robots.txt 文件中定义的 Sitemap,并且只跟踪 URL 中包含 /sitemap_shop 的 Sitemap

from scrapy.spiders import SitemapSpider


class MySpider(SitemapSpider):
    sitemap_urls = ["http://www.example.com/robots.txt"]
    sitemap_rules = [
        ("/shop/", "parse_shop"),
    ]
    sitemap_follow = ["/sitemap_shops"]

    def parse_shop(self, response):
        pass  # ... scrape shop here ...

将 SitemapSpider 与其他 URL 源结合使用

from scrapy.spiders import SitemapSpider


class MySpider(SitemapSpider):
    sitemap_urls = ["http://www.example.com/robots.txt"]
    sitemap_rules = [
        ("/shop/", "parse_shop"),
    ]

    other_urls = ["http://www.example.com/about"]

    async def start(self):
        async for item_or_request in super().start():
            yield item_or_request
        for url in self.other_urls:
            yield Request(url, self.parse_other)

    def parse_shop(self, response):
        pass  # ... scrape shop here ...

    def parse_other(self, response):
        pass  # ... scrape other here ...