爬虫

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

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

  1. 你首先生成初始请求来爬取第一个URL,并指定一个回调函数,该函数将在从这些请求下载的响应中被调用。

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

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

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

  4. 最后,从爬虫返回的项目通常会持久化到数据库中(在某些Item Pipeline中)或使用Feed exports写入文件。

尽管这个周期(或多或少)适用于任何类型的蜘蛛,但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请求。

假设你的目标网址是 https://www.example.com/1.html, 然后将 'example.com' 添加到列表中。

start_urls

当没有指定特定URL时,蜘蛛开始爬取的URL列表。因此,首先下载的页面将是此处列出的页面。随后的Request将依次从起始URL中包含的数据生成。

custom_settings

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

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

crawler

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

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

settings

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

logger

使用Spider的name创建的Python logger。你可以通过它发送日志消息,如从Spider记录日志中所述。

state

一个字典,您可以使用它在批次之间保持一些爬虫状态。 有关更多信息,请参见在批次之间保持持久状态

from_crawler(crawler, *args, **kwargs)

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

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

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

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

最终的设置和初始化的 Crawler 属性在 start_requests() 方法中可用,以及 engine_started 信号的处理程序和之后。

Parameters:
classmethod 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。其他Requests回调与Spider类有相同的要求。

此方法,以及任何其他请求回调,必须返回一个 Request 对象,一个 item object, 一个可迭代的 Request 对象和/或 item objects,或者 None

Parameters:

response (Response) – 要解析的响应

log(message[, level, component])

通过Spider的logger发送日志消息的包装器,为了向后兼容而保留。更多信息请参见从Spider记录日志

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或将爬网限制在站点的某些部分,但它们可以用于配置蜘蛛的任何功能。

Spider参数通过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

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

通用爬虫

Scrapy 提供了一些有用的通用蜘蛛,您可以使用它们来子类化您的蜘蛛。它们的目的是为一些常见的抓取情况提供便利的功能,比如根据某些规则跟踪网站上的所有链接,从Sitemaps抓取,或解析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[源代码]

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

除了从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)[源代码]

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

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

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[源代码]

XMLFeedSpider 旨在通过按特定节点名称迭代来解析 XML 源。迭代器可以从以下选项中选择:iternodesxmlhtml。出于性能考虑,建议使用 iternodes 迭代器,因为 xmlhtml 迭代器会一次性生成整个 DOM 以便解析。然而,当解析标记不良的 XML 时,使用 html 作为迭代器可能会很有用。

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

iterator

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

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

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

  • '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)[来源]

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

process_results(response, results)[源代码]

此方法针对蜘蛛返回的每个结果(项目或请求)调用,旨在执行返回结果到框架核心之前所需的任何最后处理,例如设置项目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下载一个feed,然后遍历每个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

站点地图爬虫

class scrapy.spiders.SitemapSpider[源代码]

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

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

sitemap_urls

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

你也可以指向一个robots.txt,它将被解析以从中提取站点地图URL。

sitemap_rules

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

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

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

例如:

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

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

重要的是要注意到:

  • 由于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文件中定义的站点地图,并且只跟踪包含/sitemap_shop的URL的站点地图:

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 ...