蜘蛛

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

对于蜘蛛,抓取周期大致如下

  1. 您首先生成要爬取的初始 URL 的请求,并指定一个回调函数,该函数将在下载这些请求的响应时调用。

    要执行的第一个请求是通过调用 start_requests() 方法获得的,该方法(默认情况下)为 start_urls 中指定的 URL 生成 Request,并将 parse 方法作为请求的回调函数。

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

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

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

即使此循环适用于任何类型的蜘蛛(或多或少),但 Scrapy 中也捆绑了不同类型的默认蜘蛛以用于不同的目的。我们将在本文中讨论这些类型。

scrapy.Spider

class scrapy.spiders.Spider
class scrapy.Spider

这是最简单的蜘蛛,也是每个其他蜘蛛都必须继承的蜘蛛(包括与 Scrapy 捆绑在一起的蜘蛛,以及您自己编写的蜘蛛)。它不提供任何特殊功能。它只是提供了一个默认的 start_requests() 实现,该实现从 start_urls 蜘蛛属性发送请求,并为每个生成的响应调用蜘蛛的方法 parse

name

一个字符串,定义此蜘蛛的名称。蜘蛛名称是 Scrapy 如何定位(并实例化)蜘蛛的方式,因此它必须是唯一的。但是,没有任何内容阻止您实例化多个相同蜘蛛的实例。这是最重要的蜘蛛属性,并且是必需的。

如果蜘蛛抓取单个域,则一种常见做法是根据域命名蜘蛛,是否包含 TLD。因此,例如,抓取 mywebsite.com 的蜘蛛通常称为 mywebsite

allowed_domains

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

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

start_urls

蜘蛛将从这里开始爬取的 URL 列表,当未指定特定 URL 时。因此,下载的第一批页面将是此处列出的页面。后续的 Request 将从 start URL 中包含的数据中依次生成。

custom_settings

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

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

crawler

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

爬虫封装了项目中的许多组件,以便于单一入口访问(例如扩展、中间件、信号管理器等)。请参阅 爬虫 API 以了解更多信息。

settings

运行此蜘蛛的配置。这是一个 Settings 实例,请参阅 设置 主题以详细了解此主题。

logger

使用蜘蛛的 name 创建的 Python 日志记录器。您可以使用它通过它发送日志消息,如 从蜘蛛中记录日志 中所述。

state

一个字典,您可以使用它在批次之间保留一些蜘蛛状态。请参阅 在批次之间保持持久状态 以了解更多信息。

from_crawler(crawler, *args, **kwargs)

这是 Scrapy 用于创建蜘蛛的类方法。

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

但是,此方法在新的实例中设置了 crawlersettings 属性,以便以后可以在蜘蛛代码中访问它们。

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

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

参数:
类方法 update_settings(settings)

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)
start_requests()

此方法必须返回一个可迭代对象,其中包含要爬取的第一个请求和/或此爬虫的项目对象。当爬虫打开以进行抓取时,Scrapy 会调用它。Scrapy 只调用它一次,因此可以安全地将start_requests()实现为生成器。

默认实现为start_urls中的每个URL生成Request(url, dont_filter=True)

如果要更改用于开始抓取域的请求,则应覆盖此方法。例如,如果需要通过POST请求登录才能开始,则可以执行以下操作

import scrapy


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

    def start_requests(self):
        return [
            scrapy.FormRequest(
                "http://www.example.com/login",
                formdata={"user": "john", "pass": "secret"},
                callback=self.logged_in,
            )
        ]

    def logged_in(self, response):
        # here you would extract links to follow and return Requests for
        # each of them, with another callback
        pass
parse(response)

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

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

此方法以及任何其他请求回调都必须返回一个Request对象、一个项目对象、一个Request对象和/或项目对象的可迭代对象,或None

参数:

response (Response) – 要解析的响应

log(message[, level, component])

通过爬虫的logger发送日志消息的包装器,出于向后兼容性考虑而保留。有关更多信息,请参见从爬虫记录日志

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)

从单个回调返回多个请求和项目

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_requests(),而不是start_urls;为了使数据具有更好的结构,您可以使用Item对象

import scrapy
from myproject.items import MyItem


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

    def start_requests(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"

    def start_requests(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。

一个有效的用例是设置HttpAuthMiddleware使用的HTTP身份验证凭据或UserAgentMiddleware使用的用户代理

scrapy crawl myspider -a http_user=myuser -a http_pass=mypassword -a user_agent=mybot

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

通用爬虫

Scrapy附带了一些有用的通用爬虫,您可以使用它们作为爬虫的父类。它们的目标是为一些常见的抓取场景提供便利的功能,例如根据某些规则遵循站点上的所有链接,从站点地图抓取或解析XML/CSV提要。

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

import scrapy


class TestItem(scrapy.Item):
    id = scrapy.Field()
    name = scrapy.Field()
    description = scrapy.Field()

CrawlSpider

class scrapy.spiders.CrawlSpider[source]

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

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

rules

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

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

parse_start_url(response, **kwargs)[source]

此方法针对爬虫的 start_urls 属性中 URL 生成的每个响应调用。它允许解析初始响应,并且必须返回一个 item 对象、一个 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 是一个 链接提取器 对象,它定义了如何从每个爬取的页面中提取链接。每个生成的链接都将用于生成一个 Request 对象,该对象将在其 meta 字典中(在 link_text 键下)包含链接的文本。如果省略,则将使用不带参数创建的默认链接提取器,从而导致提取所有链接。

callback 是一个可调用对象或字符串(在这种情况下,将使用具有该名称的爬虫对象中的方法),用于为使用指定的链接提取器提取的每个链接调用。此回调接收 Response 作为其第一个参数,并且必须返回单个实例或 item 对象 和/或 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 的爬虫时,您必须显式设置新请求的回调;否则可能会发生意外行为。

版本 2.0 中的新功能: errback 参数。

CrawlSpider 示例

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

import scrapy
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 = scrapy.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 Feed。迭代器可以选择:iternodesxmlhtml。出于性能考虑,建议使用 iternodes 迭代器,因为 xmlhtml 迭代器会一次性生成整个 DOM 以进行解析。但是,当解析标记有错误的 XML 时,使用 html 作为迭代器可能很有用。

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

iterator

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

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

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

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

默认为:'iternodes'

itertag

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

itertag = 'product'
namespaces

(prefix, uri) 元组列表,定义该文档中可用的命名空间,这些命名空间将使用此 Spider 进行处理。prefixuri 将用于使用 register_namespace() 方法自动注册命名空间。

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

示例

class YourSpider(XMLFeedSpider):

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

除了这些新属性外,此 Spider 还具有以下可重写方法

adapt_response(response)[source]

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

parse_node(response, selector)[source]

此方法针对与提供的标签名称 (itertag) 匹配的节点调用。接收响应和每个节点的 Selector。重写此方法是强制性的。否则,您的 Spider 将无法工作。此方法必须返回一个 item 对象、一个 Request 对象或包含其中任何一个的可迭代对象。

process_results(response, results)[source]

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

警告

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

XMLFeedSpider 示例

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

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

基本上,我们在上面做的是创建一个 Spider,它从给定的 start_urls 下载 Feed,然后遍历其每个 item 标签,打印它们,并在 Item 中存储一些随机数据。

CSVFeedSpider

class scrapy.spiders.CSVFeedSpider[source]

此 Spider 与 XMLFeedSpider 非常相似,只是它迭代行而不是节点。每次迭代调用的方法是 parse_row()

delimiter

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

quotechar

CSV 文件中每个字段的封闭字符的字符串。默认为 '"'(引号)。

headers

CSV 文件中列名的列表。

parse_row(response, row)[source]

接收响应和一个字典(表示每一行),其中每个键对应于 CSV 文件的每个提供的(或检测到的)标题。此 Spider 还提供了重写 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 允许您通过使用 站点地图 发现 URL 来抓取站点。

它支持嵌套站点地图并从 robots.txt 中发现站点地图 URL。

sitemap_urls

指向您要抓取其 URL 的站点地图的 URL 列表。

您还可以指向 robots.txt,它将被解析以从中提取站点地图 URL。

sitemap_rules

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

  • regex 是用于匹配从站点地图中提取的 URL 的正则表达式。regex 可以是 str 或已编译的正则表达式对象。

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

例如

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

规则按顺序应用,并且只使用第一个匹配的规则。

如果省略此属性,则站点地图中找到的所有 URL 将使用 parse 回调进行处理。

sitemap_follow

应遵循的站点地图的正则表达式列表。这仅适用于使用 站点地图索引文件 指向其他站点地图文件的站点。

默认情况下,所有站点地图都将被遵循。

指定是否应遵循一个 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]

这是一个过滤器函数,可以重写它以根据站点地图条目的属性选择它们。

例如

<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

Entries 是从站点地图文档中提取的字典对象。通常,键是标签名称,值是标签内的文本。

需要注意的是

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

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

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

如果您省略此方法,则将处理站点地图中找到的所有条目,并观察其他属性及其设置。

SitemapSpider 示例

最简单的示例:使用 parse 回调处理通过站点地图发现的所有 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 文件中定义的站点地图,并且仅遵循 URL 包含 /sitemap_shop 的站点地图

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"]

    def start_requests(self):
        requests = list(super(MySpider, self).start_requests())
        requests += [scrapy.Request(x, self.parse_other) for x in self.other_urls]
        return requests

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

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