Scrapy 爬蟲框架組件詳細介紹(二)

1 Scrapy 爬蟲框架組件詳細介紹(二)

1.1 CrawlSpiders

20.1.1 爬蟲CrawlSpiders類介紹

CrawlSpiders 類它是 Spider 的派生類,Spider 類的設計原則是隻爬取 start_url 列表中的網頁,而 CrawlSpider 類定義了一些規則( rule )來提供跟進 link 的方便機制,從爬取的網頁中獲取 link,並繼續爬取工作,比Spiders 類模板爬蟲更加適合。

前面案例創建爬蟲,使用的命令如下:

scrapy genspider tencents " tencent.com "

Scrapy 爬蟲框架組件詳細介紹(二)

默認使用”basic”template(模板)去創建爬蟲。

如果想創建 CrawlSpider 類爬蟲,可以通過下面的命令指定 crawl 模板可以快速創建 CrawlSpider 模板的爬蟲。

scrapy genspider -t crawl tencents " tencent.com "

【-t 】: 指定模板。

例子:

在 DOS 下切換到下面目錄。

D:\\scrapy_project\\tencent\\tencent\\spiders

執行命令:

scrapy genspider -t crawl tencents " tencent.com "

Scrapy 爬蟲框架組件詳細介紹(二)

注意:命令創建的時候,爬蟲文件名不能和作業域名相同,可以創建之後再改回來。

查看生成的 tencent.py 爬蟲文件。

import scrapy

from scrapy.linkextractors import LinkExtractor

from scrapy.spiders import CrawlSpider, Rule

class TencentSpider(CrawlSpider):

name = 'tencent'

allowed_domains = [' tencent.com ']

start_urls = ['http:// tencent.com /']

rules = (

Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),

)

def parse_item(self, response):

i = {}

#i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()

#i['name'] = response.xpath('//div[@id="name"]').extract()

#i['description'] = response.xpath('//div[@id="description"]').extract()

return i

從生成的 tencent.py 爬蟲文件和之前的Spider基類爬蟲文件比較,多瞭如下幾部分內容:

(一)需要導入 LinkExtractor、CrawlSpider、 Rule庫

from scrapy.linkextractors import LinkExtractor

from scrapy.spiders import CrawlSpider, Rule

(二)爬蟲類繼承的是 CrawlSpider 類

(三)增加了 rules 。

(四)增加了 LinkExtractor(allow=r'Items/')

(五)新增加了一個回調函數 parse_item(self, response)

20.1.2 CrawlSpider源碼

查看CrawlSpider源碼。

Scrapy 爬蟲框架組件詳細介紹(二)

class CrawlSpider(Spider):

rules = ()

def __init__(self, *a, **kw):

super(CrawlSpider, self).__init__(*a, **kw)

self._compile_rules()

def parse(self, response):

return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True)

def parse_start_url(self, response):

return []

def process_results(self, response, results):

return results

def _build_request(self, rule, link):

r = Request(url=link.url, callback=self._response_downloaded)

r.meta.update(rule=rule, link_text=link.text)

return r

def _requests_to_follow(self, response):

if not isinstance(response, HtmlResponse):

return

seen = set()

for n, rule in enumerate(self._rules):

links = [lnk for lnk in rule.link_extractor.extract_links(response)

if lnk not in seen]

if links and rule.process_links:

links = rule.process_links(links)

for link in links:

seen.add(link)

r = self._build_request(n, link)

yield rule.process_request(r)

def _response_downloaded(self, response):

rule = self._rules[response.meta['rule']]

return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow)

def _parse_response(self, response, callback, cb_kwargs, follow=True):

if callback:

cb_res = callback(response, **cb_kwargs) or ()

cb_res = self.process_results(response, cb_res)

for requests_or_item in iterate_spider_output(cb_res):

yield requests_or_item

if follow and self._follow_links:

for request_or_item in self._requests_to_follow(response):

yield request_or_item

def _compile_rules(self):

def get_method(method):

if callable(method):

return method

elif isinstance(method, six.string_types):

return getattr(self, method, None)

self._rules = [copy.copy(r) for r in self.rules]

for rule in self._rules:

rule.callback = get_method(rule.callback)

rule.process_links = get_method(rule.process_links)

rule.process_request = get_method(rule.process_request)

@classmethod

def from_crawler(cls, crawler, *args, **kwargs):

spider = super(CrawlSpider, cls).from_crawler(crawler, *args, **kwargs)

spider._follow_links = crawler.settings.getbool(

'CRAWLSPIDER_FOLLOW_LINKS', True)

return spider

def set_crawler(self, crawler):

super(CrawlSpider, self).set_crawler(crawler)

self._follow_links = crawler.settings.getbool('CRAWLSPIDER_FOLLOW_LINKS', True)

CrawlSpider 繼承於 Spider 類,除了繼承過來的屬性外(name、allow_domains),還提供了新的屬性和方法。

20.1.3 LinkExtractors

【class scrapy.linkextractors.LinkExtractor】

Link Extractors 的目的很簡單: 提取鏈接。

每個LinkExtractor有唯一的公共方法是 extract_links(),它接收一個 Response 對象,並返回一個 scrapy.link.Link 對象。

Link Extractors 要實例化一次,並且 extract_links 方法會根據不同的 response 調用多次提取鏈接。

class scrapy.linkextractors.LinkExtractor(

allow = (),

deny = (),

allow_domains = (),

deny_domains = (),

deny_extensions = None,

restrict_xpaths = (),

tags = ('a','area'),

attrs = ('href'),

canonicalize = True,

unique = True,

process_value = None

)

主要參數:

【allow】:滿足括號中“正則表達式”的值會被提取,如果為空,則全部匹配。

【deny】:與這個正則表達式(或正則表達式列表)不匹配的 URL 一定不提取。

【allow_domains】:會被提取的鏈接的 domains。

【deny_domains】:一定不會被提取鏈接的 domains。

【restrict_xpaths】:使用 xpath 表達式,和 allow 共同作用過濾鏈接。

20.1.4 rules

from scrapy.spiders import Rule

Scrapy 爬蟲框架組件詳細介紹(二)

查看 Rule 方法實現的源碼。

Scrapy 爬蟲框架組件詳細介紹(二)

在 rules 中包含一個或多個 Rule 對象,每個 Rule 對爬取網站的動作定義了特定操作。如果多個 rule 匹配了相同的鏈接,則根據規則在本集合中被定義的順序,第一個會被使用。

class scrapy.spiders.Rule(

link_extractor,

callback = None,

cb_kwargs = None,

follow = None,

process_links = None,

process_request = None

)

主要參數:

【link_extractor】:是一個 Link Extractor 對象,用於定義需要提取的鏈接。

【callback】: 從 link_extractor 中每獲取到鏈接時,參數所指定的值作為回調函數,該回調函數接受一個 response 作為其第一個參數。

注意:當編寫爬蟲規則時,避免使用 parse 作為回調函數。由於 CrawlSpider 使用 parse方法來實現其邏輯,如果覆蓋了 parse方法,crawl spider 將會運行失敗。

【follow】:是一個布爾( Boolean )值,指定了根據該規則從 response 提取的鏈接是否需要跟進。 如果 callback 為 None,follow 默認設置為 True ,否則默認為 False。

【process_links】:指定該 spider 中哪個的函數將會被調用,從link_extractor 中獲取到鏈接列表時將會調用該函數。該方法主要用來過濾。

【process_request】:指定該 spider 中哪個的函數將會被調用, 該規則提取到每個request 時都會調用該函數。 (用來過濾 request )

20.1.5 爬取規則( Crawling rules )

為了弄清楚爬取規則( Crawling rules )的實現過程,我們以騰訊招聘為例,使用 Scrapy Shell 交互終端一步一步展現實現的過程。

第一步:進入項目的根目錄。

D:\\scrapy_project\\tencent\\tencent

執行下列命令來調用 Scrapy Shell 交互終端,發送請求。

scrapy shell "http://hr.tencent.com/position.php?&start=0"

Scrapy 爬蟲框架組件詳細介紹(二)

Scrapy 爬蟲框架組件詳細介紹(二)

第二步:分析騰訊招聘網站的招聘信息的分頁 URL 鏈接。

Scrapy 爬蟲框架組件詳細介紹(二)

position.php?&start=20

position.php?&start=30

position.php?&start=40

position.php?&start=50

position.php?&start=60

每個鏈接組合 https://hr.tencent.com/ 就是一個完整的騰訊招聘網站的招聘信息的分頁 URL 鏈接地址。

我們要做的就是通過正則表達式把頁面裡的鏈接(position.php?&start=20

…)提取出來。

page_lx = LinkExtractor(allow=('position.php?&start=\\d+'))

第三步:獲取請求響應的內容。

【response.body】

Scrapy 爬蟲框架組件詳細介紹(二)

【response.text】

Scrapy 爬蟲框架組件詳細介紹(二)

通常是使用 response.text 顯示的是 Unicode 完整的字符串,便於匹配,一般情況,只是打印內容的時候,使用 response.body,需要解析請求的內容時,就使用:response.text

第四步:導入 LinkExtractor,創建 LinkExtractor 實例對象。

from scrapy.linkextractors import LinkExtractor

page_lx = LinkExtractor(allow=('position.php?&start=\\d+'))

Scrapy 爬蟲框架組件詳細介紹(二)

allow : LinkExtractor 對象最重要的參數之一,這是一個正則表達式,必須要匹配這個正則表達式(或正則表達式列表)的URL才會被提取,如果沒有給出(或為空), 它會匹配所有的鏈接。

deny : 用法同 allow,只不過與這個正則表達式匹配的 URL 不會被提取)。它的優先級高於 allow 的參數,如果沒有給出(或 None), 將不排除任何鏈接。

第五步:調用 LinkExtractor 實例的 extract_links() 方法查詢匹配結果。

Scrapy 爬蟲框架組件詳細介紹(二)

沒有查到,顯示為:[]

這是由於轉義字符的問題引起,修改正則表達式內容,繼續重新匹配:

page_lx = LinkExtractor(allow=('position\\.php\\?&start=\\d+'))

page_lx.extract_links(response)

或者可以如下獲取鏈接

page_lx = LinkExtractor(allow = ('start=\\d+'))

page_lx.extract_links(response)

Scrapy 爬蟲框架組件詳細介紹(二)

這樣我們就可以獲取到了所有分頁的鏈接地址了。

20.1.6 編寫Item文件

import scrapy

class TencentItem(scrapy.Item):

# define the fields for your item here like:

# 職位名

positionname = scrapy.Field()

# 詳情連接

positionlink = scrapy.Field()

# 職位類別

positionType = scrapy.Field()

# 招聘人數

peopleNum = scrapy.Field()

# 工作地點

workLocation = scrapy.Field()

# 發佈時間

publishTime = scrapy.Field()

20.1.7 編寫爬蟲文件

import scrapy,sys,os

# 導入CrawlSpider類和Rule

from scrapy.spiders import CrawlSpider, Rule

# 導入鏈接規則匹配類,用來提取符合規則的連接

from scrapy.linkextractors import LinkExtractor

path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

sys.path.append(path)

from dg_sun.items import SunItem

class SunSpider(CrawlSpider):

name = 'sun'

allowed_domains = ['wz.sun0769.com']

start_urls = ['http://wz.sun0769.com/index.php/question/questionType?type=4&page=0']

# 投訴分頁鏈接的提取規則,返回的符合匹配規則的鏈接匹配對象的列表(這裡提取的是分頁的鏈接)

pagelink = LinkExtractor(allow=("start=\\d+"))

# 投訴詳情頁內容鏈接的提取規則,返回的符合匹配規則的鏈接匹配對象的列表

Details = LinkExtractor(allow=r'/html/question/\\d+/\\d+.shtml')

#多條 Rule

rules = (

# 提取匹配,並跟進鏈接(沒有 callback 意味著 follow 默認為 True )

Rule(pagelink),

Rule(Details, callback = 'parse_item',follow = True),

)

# 指定的回調函數

def parse_item(self, response):

item = SunItem()

#標題

item['title'] = response.xpath('//div[contains(@class, "pagecenter p3")]//strong/text()').extract()[0]

# 編號

item['id'] = item['title'].split(' ')[-1].split(":")[-1]

# 內容

item['content'] = response.xpath('//div[@class="c1 text14_2"]/text()').extract()[0]

# 鏈接

item['url'] = response.url

yield item注意:callback 千萬不能寫 parse()方法,再次強調:由於 CrawlSpider 使用 parse()方法來實現其邏輯,如果覆蓋了 parse方法,crawl spider 將會運行失敗。

20.1.8 編寫管道文件

import json

class TencentPipeline(object):

def __init__(self):

self.filename = open("tencent.json", "wb")

def process_item(self, item, spider):

text = json.dumps(dict(item), ensure_ascii = False) + ",\\n"

self.filename.write(text.encode("utf-8"))

return item

def close_spider(self, spider):

self.filename.close()

Scrapy 爬蟲框架組件詳細介紹(二)

管道文件跟之前案例的寫法一樣,沒有變化。

20.1.9 修改 settings 文件

Scrapy 爬蟲框架組件詳細介紹(二)

在 settings.py 文件配置裡指定剛才編寫的管道文件名:TencentPipeline。

Scrapy 爬蟲框架組件詳細介紹(二)

下載的數據比較多,需要設定一個下載延時時間,以免下載的數據不全。

Scrapy 爬蟲框架組件詳細介紹(二)

設置爬蟲請求的默認頭信息。

20.1.10 運行爬蟲

在 dos下切換到目錄

D:\\scrapy_project\\tencent\\tencent\\ 下

通過命令運行爬蟲 :scrapy crawl tencent

Scrapy 爬蟲框架組件詳細介紹(二)

Scrapy 爬蟲框架組件詳細介紹(二)

20.1.11查看爬取的結果

查看新建的 tencent.json 數據文件。

Scrapy 爬蟲框架組件詳細介紹(二)

1.2 Logging 日誌

Scrapy 提供了 log 功能,可以通過 logging 模塊使用。

如果我們想把爬蟲的輸入日誌保存到本地,需要在 settings.py 文件裡進行設置。

# 保存日誌信息的文件名

LOG_FILE = "tencent_log.log"

# 保存日誌等級的設置,LOG_LEVEL(可選的級別有: CRITICAL、 ERROR、WARNING、INFO 、DEBUG )(從高到底)

LOG_LEVEL = " DEBUG "

Scrapy 爬蟲框架組件詳細介紹(二)

設置完成之後,重新運行爬蟲,結果顯示如下。

Scrapy 爬蟲框架組件詳細介紹(二)

【Log levels】

Scrapy 提供 5 層 logging 級別:

CRITICAL - 嚴重錯誤( critical )。

ERROR - 一般錯誤(regular errors )。

WARNING - 警告信息( warning messages )。

INFO - 一般信息( informational messages )。

DEBUG - 調試信息( debugging messages )。

【logging設置】

通過在 setting.py 中進行以下設置可以被用來配置 logging:

LOG_ENABLED 默認: True,啟用logging

LOG_ENCODING 默認: 'utf-8',logging 使用的編碼

LOG_FILE 默認: None,在當前目錄裡創建 logging 輸出文件的文件名

LOG_LEVEL 默認: 'DEBUG',log 的最低級別

LOG_STDOUT 默認: False 如果為 True,進程所有的標準輸出(及錯誤)將會被重定向到 log中。


分享到:


相關文章: