Scrapy教程

在本教程中,我们假设您的系统上已经安装了Scrapy。如果不是这种情况,请参阅安装指南。

我们将要剔除quotes.toscrape.com,这是一个列出着名作家的报价的网站。

本教程将引导您完成以下任务:

  1. 创建一个新的Scrapy项目
  2. 编写一个蜘蛛来抓取站点并提取数据
  3. 使用命令行导出刮取的数据
  4. 改变蜘蛛递归地跟随链接
  5. 使用蜘蛛参数

Scrapy是用Python编写的。如果你是新来的语言,你可能想要从这个语言的意图开始,以充分利用Scrapy。

如果您已经熟悉其他语言,并希望快速学习Python,我们建议您阅读Dive Into Python 3.或者,您也可以按照Python教程进行阅读。

如果你刚开始编程,并希望从Python开始,你可能会发现在线书“学习Python的难题”很有用。您还可以查看非程序员的Python资源列表。

创建一个项目

在开始刮擦之前,您将必须设置一个新的Scrapy项目。 输入您要存储代码的目录,然后运行:

scrapy startproject tutorial

这将创建一个包含以下内容的教程目录:

tutorial/
    scrapy.cfg            # deploy configuration file

    tutorial/             # project's Python module, you'll import your code from here
        __init__.py

        items.py          # project items definition file

        pipelines.py      # project pipelines file

        settings.py       # project settings file

        spiders/          # a directory where you'll later put your spiders
            __init__.py

我们的第一只蜘蛛

蜘蛛是您定义的课程,Scrapy用于从网站(或一组网站)中刮取信息。他们必须子类scrapy.Spider并定义初始请求,可选地如何遵循页面中的链接,以及如何解析下载的页面内容来提取数据。

这是我们第一个蜘蛛的代码。将其保存在项目中的/ spiders目录下的名为quotes_spider.py的文件中:

进口刮板

QuotesSpider类(scrapy.Spider):

name =“quotes”



def start\_requests(self):

    urls = \[

        'http://quotes.toscrape.com/page/1/',

        'http://quotes.toscrape.com/page/2/',

    \]

    对于url中的url:

        yield scrapy.Request(url = url,callback = self.parse)



def解析(self,response):

    page = response.url.split(“/”)\[ - 2\]

    filename ='quotes-%s.html'%页

    with open(filename,'wb')as f:

        f.write(response.body)

    self.log('保存的文件%s'%filename)

你可以看到,我们的Spider子类scrapy.Spider并定义了一些属性和方法:

名称:识别蜘蛛。它在项目中必须是唯一的,也就是说,您不能为不同的Spiders设置相同的名称。

start_requests():必须返回一个可迭代的请求(您可以返回一个请求列表或写一个生成器函数),Spider将开始爬行。随后的请求将从这些初始请求连续生成。

parse():一种将被调用来处理为每个请求下载的响应的方法。响应参数是TextResponse的一个实例,它保存页面内容,并且还有其他有用的方法来处理它。

parse()方法通常解析响应,将裁剪的数据提取为dicts,并且还可以查找新的URL来跟踪并从中创建新的请求(Request)。

如何运行我们的蜘蛛

要使我们的蜘蛛工作,请转到项目的顶级目录并运行:

刮擦爬行报价

此命令运行spider,我们刚刚添加了名称引号,这将发送一些针对quotes.toscrape.com域的请求。你将得到类似于此的输出:

...(为简洁省略)

2016-12-16 21:24:05 [scrapy.core.engine]信息:蜘蛛打开

2016-12-16 21:24:05 [scrapy.extensions.logstats]信息:抓取0页(0页/分钟),刮0项(0项/分)

2016-12-16 21:24:05 [scrapy.extensions.telnet] DEBUG:Telnet控制台收听127.0.0.1:6023

2016-12-16 21:24:05 [scrapy.core.engine] DEBUG:Crawled(404)<GET http://quotes.toscrape.com/robots.txt>(referer:None)

2016-12-16 21:24:05 [scrapy.core.engine] DEBUG:Crawled(200)<GET http://quotes.toscrape.com/page/1/>(referer:None)

2016-12-16 21:24:05 [scrapy.core.engine] DEBUG:Crawled(200)<GET http://quotes.toscrape.com/page/2/>(referer:None)

2016-12-16 21:24:05 [引用] DEBUG:保存的文件引用-1.html

2016-12-16 21:24:05 [quote] DEBUG:保存的文件引用 - 2.html

2016-12-16 21:24:05 [scrapy.core.engine] INFO:关闭蜘蛛(完成)

...

现在,检查当前目录下的文件。您应该注意到,已经创建了两个新文件:quotes-1.html和quotes-2.html,其中包含各个URL的内容,就像我们的解析方法指示一样。

注意

如果您想知道为什么我们还没有解析HTML,请继续,我们将尽快介绍。

什么发生在引擎盖下?

Scrapy安排scrapy.Request对象由Spider的start_requests方法返回。在收到每个响应后,它会实例化响应对象并调用与请求(在本例中为解析方法)相关联的回调方法,将响应作为参数传递。

start_requests方法的快捷方式

而不是实现从URL生成scrapy.Request对象的start_requests()方法,您可以使用URL列表定义一个start_urls类属性。该列表将被默认的start_requests()实现用于为您的蜘蛛创建初始请求:

进口刮板

QuotesSpider类(scrapy.Spider):

name =“quotes”

start\_urls = \[

    'http://quotes.toscrape.com/page/1/',

    'http://quotes.toscrape.com/page/2/',

\]



def解析(self,response):

    page = response.url.split(“/”)\[ - 2\]

    filename ='quotes-%s.html'%页

    with open(filename,'wb')as f:

        f.write(response.body)

将调用parse()方法来处理这些URL的每个请求,即使我们没有明确告诉Scrapy这样做。这是因为parse()是Scrapy的默认回调方法,该方法被调用用于没有明确分配的回调的请求。

提取数据

学习如何使用Scrapy提取数据的最佳方式是尝试使用Shell Scrapy shell的选择器。跑:

刮板“http://quotes.toscrape.com/page/1/'

注意

记住在从命令行运行Scrapy shell时始终将url引入引号,否则包含参数(即。&character)的url将不起作用。

在Windows上,使用双引号:

刮板“http://quotes.toscrape.com/page/1/”

你会看到像:

[...在这里刮擦日志...]

2016-09-19 12:09:27 [scrapy.core.engine] DEBUG:Crawled(200)<GET http://quotes.toscrape.com/page/1/>(referer:None)

可用的刮擦物体:

刮擦模块(包含刮擦,请求,刮擦等)

[s] crawler <scrapy.crawler.Crawler object at 0x7fa91d888c90>

[s]项目{}

[s]请求<GET http://quotes.toscrape.com/page/1/&gt;

[s]回复<200 http://quotes.toscrape.com/page/1/&gt;

[s]设置<scrapy.settings.Settings对象在0x7fa91d888c10>

[s] spider <DefaultSpider'default'at 0x7fa91c8af990>

有用的捷径:

[s] shelp()Shell帮助(打印此帮助)

获取(req_or_url)获取请求(或URL)并更新本地对象

[s] view(response)在浏览器中查看响应

>>>

使用shell,您可以尝试使用响应对象使用CSS选择元素:

>>> response.css('title')

[<Selector xpath ='descendant-or-self :: title'data ='<title>行情to scrape </ title>'>]

运行response.css('title')的结果是一个名为SelectorList的列表样对象,它表示围绕XML / HTML元素的Selector对象的列表,允许您运行进一步的查询来细分选择或提取数据。

要从上面的标题中提取文本,您可以执行以下操作:

>>> response.css('title :: text')。extract()

['行情刮]

这里有两件事要注意:一个是我们已经在CSS查询中添加了::文本,这意味着我们只想直接在<title>元素内部选择文本元素。如果我们不指定:: text,我们将获得完整的title元素,包括其标签:

>>> response.css('title')。extract()

['<title>要查看的行情</ title>']

另一件事是调用.extract()的结果是一个列表,因为我们正在处理SelectorList的一个实例。当你知道你只是想要第一个结果,在这种情况下,你可以做:

>>> response.css('title :: text')。extract_first()

“行情刮”

或者你可以写:

>>> response.css('title :: text')[0] .extract()

“行情刮”

但是,使用.extract_first()避免了一个IndexError,如果没有找到匹配选择的元素,则返回None。

这里有一个教训:对于大多数刮削代码,您希望它能够因为页面上找不到的错误而具有弹性,因此即使某些部件无法被刮除,您至少可以获取一些数据。

除了extract()和extract_first()方法之外,还可以使用re()方法来提取使用正则表达式:

>>> response.css('title :: text')。re(r'Quotes。*')

['行情刮]

>>> response.css('title :: text')。re(r'Q w +')

[ '行情']

>>> response.css('title :: text')。re(r'( w +)to( w +)')

['行情','刮']

为了找到要使用的正确的CSS选择器,您可能会使用view(response)在Web浏览器中从shell打开响应页面。您可以使用浏览器开发工具或扩展名(如Firebug)(有关使用Firebug进行刮取和使用Firefox进行刮取的部分)。

选择器小工具也是一个很好的工具,可以快速找到可选的元素的CSS选择器,它可以在许多浏览器中运行。

XPath:简要介绍

除了CSS,Scrapy选择器还支持使用XPath表达式:

>>> response.xpath('// title')

[<Selector xpath ='// title'data ='<title>行情to scrape </ title>'>]

>>> response.xpath('// title / text()')。extract_first()

“行情刮”

XPath表达式非常强大,是Scrapy选择器的基础。实际上,CSS选择器被转换为XPath。您可以看到,如果仔细阅读shell中的选择器对象的文本表示。

虽然也许不像CSS选择器那么受欢迎,但XPath表达式提供更多的功能,因为除了导航结构之外,它还可以查看内容。使用XPath,您可以选择以下内容:选择包含文本“下一页”的链接。这使得XPath非常适合刮取任务,我们鼓励您学习XPath,即使您已经知道如何构建CSS选择器,这将使刮刮更容易。

我们不会在这里覆盖XPath,但您可以在这里阅读更多关于在刮刮选择器中使用XPath的信息。要了解XPath的更多信息,我们建议本教程通过示例了解XPath,本教程将学习“如何在XPath中思考”。

提取报价和作者

现在你知道一些关于选择和提取的东西,我们来完成我们的蜘蛛,编写代码以从网页中提取引号。

http://quotes.toscrape.com中的每个报价都由HTML元素表示,如下所示:

<div class="quote">
    <span class="text">“The world as we have created it is a process of our
    thinking. It cannot be changed without changing our thinking.”</span>
    <span>
        by <small class="author">Albert Einstein</small>
        <a href="/author/Albert-Einstein">(about)</a>
    </span>
    <div class="tags">
        Tags:
        <a class="tag" href="/tag/change/page/1/">change</a>
        <a class="tag" href="/tag/deep-thoughts/page/1/">deep-thoughts</a>
        <a class="tag" href="/tag/thinking/page/1/">thinking</a>
        <a class="tag" href="/tag/world/page/1/">world</a>
    </div>
</div>

我们来打开刮壳,玩一下,找出如何提取我们想要的数据:

$ scrapy shell'http://quotes.toscrape.com'

我们得到一个列表的选择器的报价HTML元素:

>>> response.css(“div.quote”)

通过上面的查询返回的每个选择器都允许我们对其子元素运行进一步的查询。让我们将第一个选择器分配给一个变量,以便我们可以直接在特定的引用上运行我们的CSS选择器:

>>> quote = response.css(“div.quote”)[0]

现在,我们使用刚刚创建的引用对象,从该引用中提取标题,作者和标签:

>>> title = quote.css(“span.text :: text”)。extract_first()

>>>标题

“”我们创造的世界是我们思想的一个过程。如果不改变我们的思想,就不能改变“。”

>>> author = quote.css(“small.author::text”)。extract_first()

>>>作者

'艾尔伯特爱因斯坦'

鉴于标签是字符串列表,我们可以使用.extract()方法来获取所有这些:

>>> tags = quote.css(“div.tags a.tag :: text”)。extract()

>>>标签

[“变”,“深思”,“思想”,“世界”]

已经弄清楚如何提取每一个位,我们现在可以遍历所有引号元素,并把它们放在一个Python字典中:

>>>在response.css(“div.quote”)中的引用:

... text = quote.css(“span.text :: text”)。extract_first()

... author = quote.css(“small.author::text”)。extract_first()

...标签= quote.css(“div.tags a.tag :: text”)。extract()

... print(dict(text = text,author = author,tags = tags))

'''''''''''''''''','author':'阿尔伯特·爱因斯坦','文字':'“我们创造的世界是一个过程我们的想法不改变我们的想法就不可能改变。“'}

{'tags':['能力','选择'],'作者':'J.K.罗琳','文字':'“这是我们的选择,哈利,显示出我们真正的东西,远远超过我们的能力。

...更多的这些,为了简洁省略

>>>

提取我们的蜘蛛数据

我们回到我们的蜘蛛到目前为止,它并没有提取任何数据,只将整个HTML页面保存到本地文件。我们将上面的提取逻辑整合到我们的蜘蛛中。

Scrape蜘蛛通常生成包含从页面提取的数据的许多字典。为此,我们在回调中使用yield Python关键字,如下所示:

进口刮板

QuotesSpider类(scrapy.Spider):

name =“quotes”

start\_urls = \[

    'http://quotes.toscrape.com/page/1/',

    'http://quotes.toscrape.com/page/2/',

\]



def解析(self,response):

    在response.css('div.quote')中引用:

        产量 {

            'text':quote.css('span.text :: text')。extract\_first(),

            'author':quote.css('small.author::text')。extract\_first(),

            'tags':quote.css('div.tags a.tag :: text')。extract(),

        }

如果你运行这个蜘蛛,它会输出提取的数据与日志:

2016-09-19 18:57:19 [scrapy.core.scraper] DEBUG:Scrape from <200 http://quotes.toscrape.com/page/1/&gt;

{'tags':['life','love'],'author':'AndréGide','text':'“最好不要因为你不喜欢被爱而被憎恶。 “'}

2016-09-19 18:57:19 [scrapy.core.scraper] DEBUG:Scrape from <200 http://quotes.toscrape.com/page/1/&gt;

{'tags':['爱迪生','失败','鼓舞'','释义'],'作者':'托马斯·爱迪生','文字':“我没有失败,我刚刚发现万种方式不行。“”}

存储刮削数据

通过使用Feed导出,使用以下命令,最简单的方法来存储刮除的数据:

刮擦爬行报价-o quotes.json

这将生成一个quotes.json文件,其中包含以JSON序列化的所有已查找的项目。

由于历史原因,Scrapy附加到给定的文件,而不是覆盖其内容。如果您在第二次运行此命令之前不删除该文件,那么最终会出现一个破坏的JSON文件。

您还可以使用其他格式,如JSON行:

scrapy crawl quotes -o quotes.jl

JSON Lines格式很有用,因为它像流一样,您可以轻松地将新记录附加到它。当您运行两次时,它不具有相同的JSON问题。另外,由于每条记录都是单独的行,所以您可以处理大文件,而无需将内存中的所有内容都放在一起,还有JQ等工具可以帮助您在命令行中执行此操作。

在小项目(如本教程中的一个)中,这应该是足够的。但是,如果要使用已刮取的项目执行更复杂的操作,则可以编写项目管道。在项目创建过程中,在教程/ pipelines.py中为您创建了项目管道的占位符文件。虽然您只需要存储已刮取的项目,但不需要实施任何项目管道。

>>> for quote in response.css("div.quote"):
...     text = quote.css("span.text::text").extract_first()
...     author = quote.css("small.author::text").extract_first()
...     tags = quote.css("div.tags a.tag::text").extract()
...     print(dict(text=text, author=author, tags=tags))
{'tags': ['change', 'deep-thoughts', 'thinking', 'world'], 'author': 'Albert Einstein', 'text': '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'}
{'tags': ['abilities', 'choices'], 'author': 'J.K. Rowling', 'text': '“It is our choices, Harry, that show what we truly are, far more than our abilities.”'}
    ... a few more of these, omitted for brevity
>>>

提取我们的蜘蛛数据

我们回到我们的蜘蛛 到目前为止,它并没有提取任何数据,只将整个HTML页面保存到本地文件。 我们将上面的提取逻辑整合到我们的蜘蛛中。

Scrape蜘蛛通常生成包含从页面提取的数据的许多字典。 为此,我们在回调中使用yield Python关键字,如下所示:

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = [
        'http://quotes.toscrape.com/page/1/',
        'http://quotes.toscrape.com/page/2/',
    ]

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('small.author::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }

如果你运行这个蜘蛛,它会输出提取的数据与日志:

2016-09-19 18:57:19 [scrapy.core.scraper] DEBUG: Scraped from <200 http://quotes.toscrape.com/page/1/>
{'tags': ['life', 'love'], 'author': 'André Gide', 'text': '“It is better to be hated for what you are than to be loved for what you are not.”'}
2016-09-19 18:57:19 [scrapy.core.scraper] DEBUG: Scraped from <200 http://quotes.toscrape.com/page/1/>
{'tags': ['edison', 'failure', 'inspirational', 'paraphrased'], 'author': 'Thomas A. Edison', 'text': "“I have not failed. I've just found 10,000 ways that won't work.”"}

存储刮削数据

通过使用Feed导出,使用以下命令,最简单的方法来存储刮除的数据:

刮擦爬行报价-o quotes.json

这将生成一个quotes.json文件,其中包含以JSON序列化的所有已查找的项目。

由于历史原因,Scrapy附加到给定的文件,而不是覆盖其内容。如果您在第二次运行此命令之前不删除该文件,那么最终会出现一个破坏的JSON文件。

您还可以使用其他格式,如JSON行:

scrapy crawl quotes -o quotes.jl

JSON Lines格式很有用,因为它像流一样,您可以轻松地将新记录附加到它。当您运行两次时,它不具有相同的JSON问题。另外,由于每条记录都是单独的行,所以您可以处理大文件,而无需将内存中的所有内容都放在一起,还有JQ等工具可以帮助您在命令行中执行此操作。

在小项目(如本教程中的一个)中,这应该是足够的。但是,如果要使用已刮取的项目执行更复杂的操作,则可以编写项目管道。在项目创建过程中,在教程/ pipelines.py中为您创建了项目管道的占位符文件。虽然您只需要存储已刮取的项目,但不需要实施任何项目管道。

以下链接

我们来说,而不是从http://quotes.toscrape.com的前两个页面中删除这些内容,您可以在网站的所有页面中找到引号。

现在您已经知道如何从页面中提取数据,我们来看看如何跟踪它们的链接。

首先是提取我们想要跟踪的页面的链接。 检查我们的页面,我们可以看到有一个链接到下一个页面与以下标记:

<ul class =“pager”>

 &lt;li class =“next”&gt;

     &lt;a href="/page/2/"&gt;下一页&lt;span aria-hidden =“true”&gt;&rarr; &lt;/ span&gt; &lt;/a&gt;

&lt;/ LI&gt;

</ UL>

我们可以尝试在shell中提取它:

>>> response.css('li.next a')。extract_first()

'<a href="/page/2/"> Next <span aria-hidden =“true”>→</ span> </a>'

这得到了锚点元素,但是我们需要属性href。 为此,Scrapy支持CSS扩展,您可以选择属性内容,如下所示:

>>> response.css('li.next a :: attr(href)')。extract_first()

'/页/2/'

让我们看看现在我们的蜘蛛修改为递归地跟随链接到下一页,从中提取数据:

您还可以使用其他格式,如JSON行:

scrapy crawl quotes -o quotes.jl

JSON Lines格式很有用,因为它像流一样,您可以轻松地将新记录附加到它。当您运行两次时,它不具有相同的JSON问题。另外,由于每条记录都是单独的行,所以您可以处理大文件,而无需将内存中的所有内容都放在一起,还有JQ等工具可以帮助您在命令行中执行此操作。

在小项目(如本教程中的一个)中,这应该是足够的。但是,如果要使用已刮取的项目执行更复杂的操作,则可以编写项目管道。在项目创建过程中,在教程/ pipelines.py中为您创建了项目管道的占位符文件。虽然您只需要存储已刮取的项目,但不需要实施任何项目管道。

以下链接

我们来说,而不是从http://quotes.toscrape.com的前两个页面中删除这些内容,您可以在网站的所有页面中找到引号。

现在您已经知道如何从页面中提取数据,我们来看看如何跟踪它们的链接。

首先是提取我们想要跟踪的页面的链接。检查我们的页面,我们可以看到有一个链接到下一个页面与以下标记:

<ul class =“pager”>

&lt;li class =“next”&gt;

    &lt;a href="/page/2/"&gt;下一页&lt;span aria-hidden =“true”&gt;&rarr; &lt;/ span&gt; &lt;/a&gt;

&lt;/ LI&gt;

</ UL>

我们可以尝试在shell中提取它:

>>> response.css('li.next a')。extract_first()

'<a href="/page/2/"> Next <span aria-hidden =“true”>→</ span> </a>'

这得到了锚点元素,但是我们需要属性href。为此,Scrapy支持CSS扩展,您可以选择属性内容,如下所示:

>>> response.css('li.next a :: attr(href)')。extract_first()

'/页/ 2 /'

让我们看看现在我们的蜘蛛修改为递归地跟随链接到下一页,从中提取数据:

进口刮板

QuotesSpider类(scrapy.Spider):

name =“quotes”

start\_urls = \[

    'http://quotes.toscrape.com/page/1/',

\]



def解析(self,response):

    在response.css('div.quote')中引用:

        产量 {

            'text':quote.css('span.text :: text')。extract\_first(),

            'author':quote.css('small.author::text')。extract\_first(),

            'tags':quote.css('div.tags a.tag :: text')。extract(),

        }



    next\_page = response.css('li.next a :: attr(href)')。extract\_first()

    如果next\_page不是None:

        next\_page = response.urljoin(next\_page)

        yield scrapy.Request(next\_page,callback = self.parse)

现在,在提取数据之后,parse()方法查找到下一页的链接,使用urljoin()方法构建一个完整的绝对URL(因为链接可以是相对的),并产生一个新的请求到下一页,注册为回调以处理下一页的数据提取,并保持爬行遍历所有页面。

您在这里看到的是Scrapy的以下链接机制:当您以回调方式生成请求时,Scrapy将安排该请求发送,并注册一个回调方法,以在该请求完成时执行。

使用它,您可以根据您定义的规则构建复杂的跟踪链接,并根据访问页面提取不同类型的数据。

在我们的示例中,它创建一个循环,跟随到所有到下一页的链接,直到它找不到一个方便的抓取博客,论坛和其他站点分页。

创建请求的快捷方式

作为创建请求对象的快捷方式,您可以使用response.follow:

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = [
        'http://quotes.toscrape.com/page/1/',
    ]

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('small.author::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }

        next_page = response.css('li.next a::attr(href)').extract_first()
        if next_page is not None:
            next_page = response.urljoin(next_page)
            yield scrapy.Request(next_page, callback=self.parse)

现在,在提取数据之后,parse()方法查找到下一页的链接,使用urljoin()方法构建一个完整的绝对URL(因为链接可以是相对的),并产生一个新的请求到下一页,注册为回调以处理下一页的数据提取,并保持爬行遍历所有页面。

您在这里看到的是Scrapy的以下链接机制:当您以回调方式生成请求时,Scrapy将安排该请求发送,并注册一个回调方法,以在该请求完成时执行。

使用它,您可以根据您定义的规则构建复杂的跟踪链接,并根据访问页面提取不同类型的数据。

在我们的示例中,它创建一个循环,跟随到所有到下一页的链接,直到它找不到一个方便的抓取博客,论坛和其他站点分页。

创建请求的快捷方式

作为创建请求对象的快捷方式,您可以使用response.follow:

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = [
        'http://quotes.toscrape.com/page/1/',
    ]

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('span small::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }

        next_page = response.css('li.next a::attr(href)').extract_first()
        if next_page is not None:
            yield response.follow(next_page, callback=self.parse)

不像scrapy.Request,response.follow直接支持相对URL - 不需要调用urljoin。 请注意,response.follow只返回一个Request实例; 您仍然必须提供此请求。

您也可以将选择器传递给response.follow而不是字符串; 该选择器应该提取必要的属性:

对于href in response.css('li.next a :: attr(href)'):

 yield response.follow(href,callback = self.parse)

对于<a>元素,有一个快捷方式:response.follow自动使用它们的href属性。 所以代码可以进一步缩短:

for a in response.css('li.next a'):

 yield response.follow(a,callback = self.parse)

注意

response.follow(response.css('li.next a'))无效,因为response.css返回一个类似列表的对象,其中包含所有结果的选择器,而不是单个选择器。 一个for循环就像上面的例子,或者response.follow(response.css('li.next a')[0])很好。

更多的例子和模式

这是另一个蜘蛛,它说明了回调和以下链接,这次是为了刮取作者信息:

import scrapy


class AuthorSpider(scrapy.Spider):
    name = 'author'

    start_urls = ['http://quotes.toscrape.com/']

    def parse(self, response):
        # follow links to author pages
        for href in response.css('.author + a::attr(href)'):
            yield response.follow(href, self.parse_author)

        # follow pagination links
        for href in response.css('li.next a::attr(href)'):
            yield response.follow(href, self.parse)

    def parse_author(self, response):
        def extract_with_css(query):
            return response.css(query).extract_first().strip()

        yield {
            'name': extract_with_css('h3.author-title::text'),
            'birthdate': extract_with_css('.author-born-date::text'),
            'bio': extract_with_css('.author-description::text'),
        }

这个蜘蛛将从主页面开始,它将跟随所有到作者页面的链接,每个页面都调用parse_author回调,以及我们之前看到的parse回调的分页链接。

这里我们将回调函数传递给response.follow作为位置参数,使代码更短;它也适用于scrapy.Request。

parse_author回调定义了一个帮助函数来提取和清除来自CSS查询的数据,并产生具有作者数据的Python dict。

这个蜘蛛演示的另一个有趣的事情是,即使同一作者有许多引号,我们也不用担心多次访问同一作者页面。默认情况下,Scrapy会将重复的请求过滤出已访问的URL,避免了由于编程错误导致服务器太多的问题。这可以通过设置DUPEFILTER_CLASS进行配置。

希望现在您对Scrapy的使用以下链接和回调的机制有很好的了解。

作为利用以下链接机制的另一个示例蜘蛛,请查看CrawlSpider类,以实现一个通用蜘蛛,实现一个小规则引擎,您可以使用它来在其上写入爬网程序。

另外,一个常见的模式是用多个页面的数据构建一个项目,使用一个技巧将附加数据传递给回调。

使用蜘蛛论证

您可以通过在运行它们时使用-a选项为您的蜘蛛提供命令行参数:

scrapy crawl quotes -o quotes-humor.json -a tag=humor

默认情况下,这些参数传递给Spider的__init__方法并成为spider属性。

在此示例中,为tag参数提供的值将通过self.tag提供。 您可以使用它来使您的蜘蛛仅使用特定标记提取引号,并根据参数构建URL:

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"

    def start_requests(self):
        url = 'http://quotes.toscrape.com/'
        tag = getattr(self, 'tag', None)
        if tag is not None:
            url = url + 'tag/' + tag
        yield scrapy.Request(url, self.parse)

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('small.author::text').extract_first(),
            }

        next_page = response.css('li.next a::attr(href)').extract_first()
        if next_page is not None:
            yield response.follow(next_page, self.parse)

如果您将tag = humor参数传递给此蜘蛛,您会注意到它只会访问幽默标签中的URL,例如http://quotes.toscrape.com/tag/humor。

您可以在这里了解更多关于处理蜘蛛参数的信息。

下一步

本教程仅涵盖了Scrapy的基础知识,但还有很多其他功能未在此提及。 检查还有什么? Scrapy中的部分一目了然,可以快速了解最重要的内容。

您可以从“基本概念”部分继续了解有关命令行工具,蜘蛛,选择器以及本教程未涵盖的其他内容的更多信息,例如对刮削数据进行建模。 如果您喜欢使用示例项目,请查看“示例”部分。

results matching ""

    No results matching ""