1 緒論

在Scrapy模塊中有4個現成的spider類,分別是:

  1. Spider
  2. CrawlSpider
  3. XMLFeedSpider
  4. CSVFeedSpider

Spider是最簡單的爬蟲也是最基礎的爬蟲類,其他所有的爬蟲類包括自定義的爬蟲類必須繼承它。這一節主要講Scrapy寫爬蟲最核心的內容,並從CrawlSpider類展開並開始學習如何構建最簡單的爬蟲程序。

在我寫的第一篇文章中,我說一個爬蟲無非是做下面幾樣事情:

  1. 請求(requests)目標站點的網頁(文本);
  2. 利用正則表達式、Beautiful SoupLXML、CSS提取數據
  3. 制定爬取規則(如「下一頁」等)、爬取方法(非同步爬取、設置代理等);
  4. 存儲數據。

至於如何存儲數據我們可以暫時不用關心,因為在Scrapy的命令行中可以使用參數直接將數據存儲到文件,可以參見[Command line tool] - 命令行工具。

從我以往的經驗中總結,用Scrapy寫爬蟲再只要理清下面兩點就能寫成一個爬蟲:

  1. 知道要爬哪些頁面,爬完這個頁面我還要再爬哪些頁面,入口在哪;
  2. 如何從這些頁面中提取數據

第2點我們在上3次的文章做了細緻的介紹了,這裡就不多說,在你學習完本節內容後再多多學習提取數據的方法即可:

[Easy XPath] - 開始使用XPath

[RegEx] - 正則表達式

[Selectors in Scrapy] - 數據匹配的方法

所以,本節將帶你從實例中理清寫爬蟲的思路及方法。

2 需求分析

假設現在你對如何用Scrapy寫爬蟲暫時還不清楚,手頭上只有接到的任務,即你要爬取哪個網站的哪些數據。所以第一步要做的就是需求分析,理清爬取順序,不要管其他。

任務:

爬取 ->網站 熱門標籤下的所有quotes及其作者

來看下這個頁面:

scrapy view http://quotes.toscrape.com/

如圖右側紅色方框即為熱門標籤,我們隨機點一個標籤進去看看:

http://quotes.toscrape.com/tag/love/

我們定義上圖為標籤主頁,上圖中紅色方框表示我們需要提取的數據項。在數據的爬取中我們要最大限度地保證數據的完整性,換句話說:獲取網站上存在的所有目標數據。除了標籤主頁的數據,那剩餘的數據在哪?入口在哪裡?

往下我們發現Next按鈕,同時打開調試器看看它的地址:

http://quotes.toscrape.com/tag/love/page/2/

點擊第二頁,再找不到Next按鈕了,也就是love標籤下的數據只存在兩頁面上:

http://quotes.toscrape.com/tag/love/
http://quotes.toscrape.com/tag/love/page/2/

總結兩步:

① 從主頁開始獲取所有右側熱門標籤對應的地址:

http://quotes.toscrape.com/tag/love/
http://quotes.toscrape.com/tag/inspirational/
……

② 訪問每一個標籤主頁,在每個標籤主頁中點擊「Next」獲取剩餘數據,直到找不到Next按鈕

最後補充一點:

love標籤主頁上的數據,分析上來講就是第一頁,那是不是說它等價於:

http://quotes.toscrape.com/tag/love/page/1/

我們發現這個鏈接在標籤主頁上有:

因此每個標籤下的數據都可以按照如下順序獲取:

http://quotes.toscrape.com/tag/love/page/1/
http://quotes.toscrape.com/tag/love/page/2/
……

這裡就涉及到第一重點

下面兩個網站指向的是同一頁面:

http://quotes.toscrape.com/tag/love/
http://quotes.toscrape.com/tag/love/page/1/

如果我們按照上面的page頁數來爬數據,那我們的數據就是重複,這個問題我們需要在下面進一步解決。

3 CrawlSpider類用法詳解

先一通氣將完它特有的屬性和方法,然後再從僅完成上面任務給出爬蟲代碼、為CrawlSpider類中每個參數用法寫例子

① parse_start_url(response)

用於處理start_urls的response,它的用處是:如果需要模擬登錄等操作可以重寫該方法。

Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)

Rule用於:

  1. 提取指定格式的鏈接(link_extractor);
  2. 過濾提取的鏈接(process_links);
  3. 對指定頁面指定相應的處理方法(process_request);
  4. 指定頁面的處理方法(callback);
  5. 為不同的提取鏈接的方法指定跟進的規則(follow);
  6. 回調函數傳參(cb_kwargs)。

避免使用 parse 作為回調函數(callback)

在PyCharm下按如下目錄創建文件:

env:虛擬環境
simple:爬蟲文件夾
Quotes_CrawlSpider.py:爬蟲
run.py:用於啟動爬蟲,方便調試

run.py代碼如下:

from scrapy import cmdline
cmdline.execute("scrapy runspider Quotes_CrawlSpider.py -o quotes.json".split())

解釋:

相當與從命令行啟動爬蟲文件, -o quotes.json 將爬蟲yield出來的item存到json文件。

3.1 完成上面爬蟲任務所需的爬蟲代碼

Quotes_CrawlSpider.py代碼如下:

# -*- coding: utf-8 -*-
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor

class MySpider(CrawlSpider):
name = toscrape.com
allowed_domains = [toscrape.com]
start_urls = [http://quotes.toscrape.com/]

rules = (
Rule(LinkExtractor(allow=(/tag/w+/$,)),
follow=True, # 如果有指定回調函數,默認不跟進
callback=parse_item,
process_links=process_links,),

Rule(LinkExtractor(allow=(/tag/w+/page/d+/,), deny=(/tag/w+/page/1/,)),
callback=parse_item,
follow=True,),
)

@staticmethod
def process_links(links): # 對提取到的鏈接進行處理
for link in links:
link.url = link.url + page/1/
yield link

@staticmethod
def parse_item(response): # 解析網頁數據並返回數據字典
quote_block = response.css(div.quote)
for quote in quote_block:
text = quote.css(span.text::text).extract_first()
author = quote.xpath(span/small/text()).extract_first()
item = dict(text=text, author=author)
yield item

流程圖:

代碼詳解:

提取規則1:(① - ④)

Rule(LinkExtractor(allow=(/tag/w+/$,)), # 從主頁提取標籤主頁的地址,利用正則表達式
follow=True, # request標籤主頁得到內容,繼續在該內容上上應用規則提取鏈接
callback=parse_item, # request標籤主頁得到內容,對該內容應用parse_item函數提取數據
process_links=process_links,), # 用process_links方法對提取到的鏈接做處理,將標籤主頁變成/page/1/形式

提取規則2:(⑤、⑥)

Rule(LinkExtractor(allow=(/tag/w+/page/d+/,), deny=(/tag/w+/page/1/,)),
# 提取鏈接格式滿足「/tag/英文字母/page/」數字/形式的,並拒絕第一頁
callback=parse_item, # 指定parse_item作為頁面的處理方法
follow=True,), # 需要在得到的頁面繼續搜索滿足規則的鏈接

parse_start_url(response)

from scrapy.spiders import CrawlSpider

class QuotesSpider(CrawlSpider):
name = "quotes"
custom_settings = {
LOG_LEVEL: INFO,
}
start_urls = [http://quotes.toscrape.com/tag/love/]

def parse_start_url(self, response):
self.logger.info(parse_start_url %s, response.url)
next_page = response.css(li.next a::attr("href")).extract_first()
if next_page is not None:
yield response.follow(next_page, self.next_parse)

def next_parse(self, response):
self.logger.info(next_pares %s, response.url)

rule的幾個參數用法示例:

# -*- coding: utf-8 -*-
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor

class MySpider(CrawlSpider):
name = toscrape.com
custom_settings = {
LOG_LEVEL: INFO, # 設置日誌級別
}
allowed_domains = [toscrape.com]
start_urls = [http://quotes.toscrape.com/]
rules = (
Rule(LinkExtractor(allow=(/tag/w+/$,)),
follow=False, # 為了測試幾個參數的用法簡單設定
callback=parse_item,
cb_kwargs={tag: love}, # 以key名tag作為變數名傳給回調函數parse_item
process_links=process_links, # 對提取的鏈接做處理
process_request=process_req # 對每個請求做處理),
)

@staticmethod
def process_links(links):
for link in links:
link.url = link.url + page/1/
yield link

def process_req(self, req):
if love in req.url: # 我們測試當鏈接中包含love是轉給parse_love處理response
return req.replace(callback=self.parse_love)
elif humor in req.url:
return req # 如果鏈接中包含humor則正常用回調函數parse_item處理response

def parse_love(self, response):
self.logger.info(parse_love %s % response.url)

def parse_item(self, response, tag):
self.logger.info(parse_item %s % response.url)
self.logger.info(not %s % tag)

運行結果:

運行流程:

推薦閱讀:

相關文章