scrapy 2.3 數(shù)據(jù)抓取實(shí)例

2021-06-02 11:18 更新

既然您知道了如何從頁(yè)面中提取數(shù)據(jù),那么讓我們看看如何從頁(yè)面中跟蹤鏈接。

第一件事是提取到我們要跟蹤的頁(yè)面的鏈接。檢查我們的頁(yè)面,我們可以看到有一個(gè)鏈接指向下一個(gè)帶有以下標(biāo)記的頁(yè)面:

<ul class="pager">
    <li class="next">
        <a href="/page/2/">Next <span aria-hidden="true">&rarr;</span></a>
    </li>
</ul>

我們可以嘗試在外殼中提?。?/p>

>>> response.css('li.next a').get()
'<a href="/page/2/">Next <span aria-hidden="true"></span></a>'

這將獲取anchor元素,但我們需要該屬性 ?href? . 為此,Scrapy支持CSS擴(kuò)展,允許您選擇屬性內(nèi)容,如下所示:

>>> response.css('li.next a::attr(href)').get()
'/page/2/'

還有一個(gè) ?attrib? 可用屬性(請(qǐng)參見 選擇元素屬性 更多信息):

>>> response.css('li.next a').attrib['href']
'/page/2/'

現(xiàn)在讓我們看看我們的spider被修改為遞歸地跟蹤下一頁(yè)的鏈接,從中提取數(shù)據(jù):

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').get(),
                'author': quote.css('small.author::text').get(),
                'tags': quote.css('div.tags a.tag::text').getall(),
            }

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

現(xiàn)在,在提取數(shù)據(jù)之后, ?parse()? 方法查找到下一頁(yè)的鏈接,并使用 ?urljoin()? 方法(因?yàn)殒溄涌梢允窍鄬?duì)的),并生成對(duì)下一頁(yè)的新請(qǐng)求,將自身注冊(cè)為回調(diào),以處理下一頁(yè)的數(shù)據(jù)提取,并保持爬行在所有頁(yè)中進(jìn)行。

這里您看到的是scrapy的以下鏈接機(jī)制:當(dāng)您在回調(diào)方法中生成一個(gè)請(qǐng)求時(shí),scrapy將計(jì)劃發(fā)送該請(qǐng)求,并注冊(cè)一個(gè)回調(diào)方法,以便在該請(qǐng)求完成時(shí)執(zhí)行。

使用它,您可以構(gòu)建復(fù)雜的爬蟲程序,這些爬蟲程序根據(jù)您定義的規(guī)則跟蹤鏈接,并根據(jù)所訪問的頁(yè)面提取不同類型的數(shù)據(jù)。

在我們的示例中,它創(chuàng)建了一種循環(huán),跟蹤到下一頁(yè)的所有鏈接,直到找不到一個(gè)為止——這對(duì)于爬行博客、論壇和其他帶有分頁(yè)的站點(diǎn)很方便。

創(chuàng)建請(qǐng)求的快捷方式

作為創(chuàng)建請(qǐng)求對(duì)象的快捷方式,您可以使用 ?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').get(),
                'author': quote.css('span small::text').get(),
                'tags': quote.css('div.tags a.tag::text').getall(),
            }

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

不像Scrapy.Request, ?response.follow? 直接支持相對(duì)URL-無需調(diào)用URLJOIN。注意 ?response.follow? 只返回一個(gè)請(qǐng)求實(shí)例;您仍然需要生成這個(gè)請(qǐng)求。

也可以將選擇器傳遞給 ?response.follow? 而不是字符串;此選擇器應(yīng)提取必要的屬性:

for href in response.css('ul.pager a::attr(href)'):
    yield response.follow(href, callback=self.parse)

為了 ?<a>? 元素有一個(gè)快捷方式: ?response.follow? 自動(dòng)使用其href屬性。因此代碼可以進(jìn)一步縮短:

for a in response.css('ul.pager a'):
    yield response.follow(a, callback=self.parse)

要從iterable創(chuàng)建多個(gè)請(qǐng)求,可以使用 ?response.follow_all? 取而代之的是:

anchors = response.css('ul.pager a')
yield from response.follow_all(anchors, callback=self.parse)

或者,進(jìn)一步縮短:

yield from response.follow_all(css='ul.pager a', callback=self.parse)

更多示例和模式

下面是另一個(gè)spider,它演示回調(diào)和以下鏈接,這次是為了抓取作者信息:

import scrapy


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

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

    def parse(self, response):
        author_page_links = response.css('.author + a')
        yield from response.follow_all(author_page_links, self.parse_author)

        pagination_links = response.css('li.next a')
        yield from response.follow_all(pagination_links, self.parse)

    def parse_author(self, response):
        def extract_with_css(query):
            return response.css(query).get(default='').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'),
        }

這個(gè)蜘蛛將從主頁(yè)開始,它將跟蹤所有指向作者頁(yè)面的鏈接,調(diào)用 ?parse_author? 它們的回調(diào),以及與 ?parse? 像我們以前看到的那樣回?fù)堋?/p>

這里,我們把回電傳遞給 ?response.follow_all? 作為使代碼更短的位置參數(shù);它也適用于 ?Request? .

這個(gè) ?parse_author? 回調(diào)定義了一個(gè)助手函數(shù),用于從CSS查詢中提取和清理數(shù)據(jù),并用作者數(shù)據(jù)生成python dict。

這個(gè)蜘蛛展示的另一個(gè)有趣的事情是,即使同一作者引用了很多話,我們也不需要擔(dān)心多次訪問同一作者頁(yè)面。默認(rèn)情況下,scrappy過濾掉對(duì)已經(jīng)訪問過的URL的重復(fù)請(qǐng)求,避免了由于編程錯(cuò)誤而太多地訪問服務(wù)器的問題。這可以通過設(shè)置進(jìn)行配置 ?DUPEFILTER_CLASS? .

希望到目前為止,您已經(jīng)很好地了解了如何使用scrappy跟蹤鏈接和回調(diào)的機(jī)制。

作為另一個(gè)利用以下鏈接機(jī)制的蜘蛛示例,請(qǐng)查看 ?CrawlSpider? 類,該類用于實(shí)現(xiàn)一個(gè)小規(guī)則引擎,您可以使用該引擎在上面編寫爬蟲程序。

另外,一個(gè)常見的模式是使用:ref:`trick將其他數(shù)據(jù)傳遞給回調(diào)<topics-request-response-ref-request-callback-arguments>`來構(gòu)建包含來自多個(gè)頁(yè)面的數(shù)據(jù)的項(xiàng)目。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)