CrawlSpider是爬取那些具有一定規(guī)則網(wǎng)站的常用的爬蟲(chóng)胖眷,它基于Spider并有一些獨(dú)特屬性
- rules: 是Rule對(duì)象的集合尊惰,用于匹配目標(biāo)網(wǎng)站并排除干擾
- parse_start_url: 用于爬取起始響應(yīng)臂拓,必須要返回Item蝇率,Request中的一個(gè)著蛙。
rules是Rule對(duì)象的集合
- rules的參數(shù)
link_extractor, : linkExtractor對(duì)象
callback=None, : 設(shè)置回調(diào)函數(shù)
follow=None, : 設(shè)置是否跟進(jìn)
process_links=None, :可以設(shè)置回調(diào)函數(shù)泌枪,對(duì)所有提取到的url進(jìn)行攔截
process_request=identity : 可以設(shè)置回調(diào)函數(shù),對(duì)request對(duì)象進(jìn)行攔截
其中的link_extractor既可以自己定義螺戳,也可以使用已有LinkExtractor類(lèi)搁宾,主要參數(shù)為:
* allow:滿(mǎn)足括號(hào)中“正則表達(dá)式”的值會(huì)被提取,如果為空倔幼,則全部匹配盖腿。
* deny:與這個(gè)正則表達(dá)式(或正則表達(dá)式列表)不匹配的URL一定不提取。
* allow_domains:會(huì)被提取的鏈接的domains。
* deny_domains:一定不會(huì)被提取鏈接的domains翩腐。
* **restrict_xpaths**:使用**xpath**表達(dá)式鸟款,和**allow**共同作用過(guò)濾鏈接。還有一個(gè)類(lèi)似的restrict_css
以Chinaz為例:
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from china.items import ChinazprojectWebInfoItem
class ChinazSpider(CrawlSpider):
# 爬蟲(chóng)名稱(chēng)
name = 'chinaz'
# 允許爬去的域
allowed_domains = ['chinaz.com']
# 起始url地址
start_urls = ['http://top.chinaz.com/hangyemap.html']
# 存放定制的獲取鏈接的規(guī)則對(duì)象(可以是一個(gè)列表也可以是元組)
# 根據(jù)規(guī)則提取到的所有鏈接茂卦,會(huì)由crawlspider構(gòu)建request對(duì)象欠雌,并交給引擎處理
'''
LinkExtractor : 設(shè)置提取鏈接的規(guī)則(正則表達(dá)式)
allow=(), : 設(shè)置允許提取的url
restrict_xpaths=(), :根據(jù)xpath語(yǔ)法,定位到某一標(biāo)簽下提取鏈接
restrict_css=(), :根據(jù)css選擇器疙筹,定位到某一標(biāo)簽下提取鏈接
deny=(), : 設(shè)置不允許提取的url(優(yōu)先級(jí)比allow高)
allow_domains=(), : 設(shè)置允許提取url的域
deny_domains=(), :設(shè)置不允許提取url的域(優(yōu)先級(jí)比allow_domains高)
unique=True, :如果出現(xiàn)多個(gè)相同的url只會(huì)保留一個(gè)
strip=True :默認(rèn)為T(mén)rue,表示自動(dòng)去除url首尾的空格
'''
'''
rule
link_extractor, : linkExtractor對(duì)象
callback=None, : 設(shè)置回調(diào)函數(shù)
follow=None, : 設(shè)置是否跟進(jìn)
process_links=None, :可以設(shè)置回調(diào)函數(shù),對(duì)所有提取到的url進(jìn)行攔截
process_request=identity : 可以設(shè)置回調(diào)函數(shù)禁炒,對(duì)request對(duì)象進(jìn)行攔截
'''
rules = (
# 規(guī)則對(duì)象'<a
Rule(LinkExtractor(allow=r'http://top.chinaz.com/hangye/index_.*?html',
restrict_xpaths='//div[@class="Taright"]'),
callback='parse_item',
follow=True),
)
# 注意: CrawlSpider中一定不要出現(xiàn)parse回調(diào)方法
def parse_item(self, response):
print(response.status, response.url)
'''
解析分頁(yè)的網(wǎng)站數(shù)據(jù)而咆,提取后交給item處理
'''
webInfos = response.xpath('//ul[@class="listCentent"]/li')
for webInfo in webInfos:
web_item = ChinazprojectWebInfoItem()
# 封面圖片
web_item['coverImage'] = webInfo.xpath('.//div[@class="leftImg"]/a/img/@src').extract_first('')
# 標(biāo)題
web_item['title'] = webInfo.xpath('.//h3[@class="rightTxtHead"]/a/text()').extract_first('')
# 域名
web_item['domenis'] = webInfo.xpath('.//h3[@class="rightTxtHead"]/span[@class="col-gray"]/text()').extract_first('')
# 周排名
web_item['weekRank'] = webInfo.xpath('.//div[@class="RtCPart clearfix"]/p[1]/a/text()').extract_first('')
# 反連接數(shù)
web_item['ulink'] = webInfo.xpath('.//div[@class="RtCPart clearfix"]/p[4]/a/text()').extract_first('')
# 網(wǎng)站簡(jiǎn)介
web_item['info'] = webInfo.xpath('.//p[@class="RtCInfo"]/text()').extract_first('')
# 得分
web_item['score'] = webInfo.xpath('.//div[@class="RtCRateCent"]/span/text()').re('\d+')[0]
# 排名
web_item['rank'] = webInfo.xpath('.//div[@class="RtCRateCent"]/strong/text()').extract_first('')
print(web_item)
yield web_item
CrawlSpider如何工作的?
因?yàn)镃rawlSpider繼承了Spider幕袱,所以具有Spider的所有函數(shù)暴备。
首先由start_requests
對(duì)start_urls
中的每一個(gè)url發(fā)起請(qǐng)求(make_requests_from_url
),這個(gè)請(qǐng)求會(huì)被parse接收们豌。在Spider里面的parse需要我們定義涯捻,但CrawlSpider定義parse
去解析響應(yīng)(self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True)
)
_parse_response根據(jù)有無(wú)callback
,follow
和self.follow_links
執(zhí)行不同的操作
eg:
def _parse_response(self, response, callback, cb_kwargs, follow=True):
##如果傳入了callback,使用這個(gè)callback解析頁(yè)面并獲取解析得到的reques或item
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
## 其次判斷有無(wú)follow望迎,用_requests_to_follow解析響應(yīng)是否有符合要求的link障癌。
if follow and self._follow_links:
for request_or_item in self._requests_to_follow(response):
yield request_or_item
其中_requests_to_follow
又會(huì)獲取link_extractor
(這個(gè)是我們傳入的LinkExtractor)解析頁(yè)面得到的link(link_extractor.extract_links(response))
,對(duì)url進(jìn)行加工(process_links,需要自定義)辩尊,對(duì)符合的link發(fā)起Request涛浙。使用.process_request
(需要自定義)處理響應(yīng)。
CrawlSpider如何獲取rules摄欲?
CrawlSpider類(lèi)會(huì)在__init__
方法中調(diào)用_compile_rules
方法轿亮,然后在其中淺拷貝rules
中的各個(gè)Rule
獲取要用于回調(diào)(callback),要進(jìn)行處理的鏈接(process_links)和要進(jìn)行的處理請(qǐng)求(process_request)
eg:
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)
Rule
是怎么樣定義的
class Rule(object):
def __init__(self, link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=identity):
self.link_extractor = link_extractor
self.callback = callback
self.cb_kwargs = cb_kwargs or {}
self.process_links = process_links
self.process_request = process_request
if follow is None:
self.follow = False if callback else True
else:
self.follow = follow
因此LinkExtractor會(huì)傳給link_extractor胸墙。
有callback的是由指定的函數(shù)處理我注,如果沒(méi)有callback的由哪個(gè)函數(shù)處理?
`_parse_response`會(huì)處理有`callback`的(響應(yīng))respons迟隅。
cb_res = callback(response, **cb_kwargs) or ()
而`_requests_to_follow`會(huì)將`self._response_downloaded`傳給`callback`用于對(duì)頁(yè)面中匹配的url發(fā)起請(qǐng)求(request)但骨。
r = Request(url=link.url, callback=self._response_downloaded)
如何在CrawlSpider進(jìn)行模擬登陸
CrawlSpider和Spider一樣,都要使用start_requests發(fā)起請(qǐng)求
以知乎為例:
##替換原來(lái)的start_requests玻淑,callback為
def start_requests(self):
return [Request("http://www.zhihu.com/#signin", meta = {'cookiejar' : 1}, callback = self.post_login)]
def post_login(self, response):
print 'Preparing login'
#下面這句話用于抓取請(qǐng)求網(wǎng)頁(yè)后返回網(wǎng)頁(yè)中的_xsrf字段的文字, 用于成功提交表單
xsrf = Selector(response).xpath('//input[@name="_xsrf"]/@value').extract()[0]
print xsrf
#FormRequeset.from_response是Scrapy提供的一個(gè)函數(shù), 用于post表單
#登陸成功后, 會(huì)調(diào)用after_login回調(diào)函數(shù)
return [FormRequest.from_response(response, #"http://www.zhihu.com/login",
meta = {'cookiejar' : response.meta['cookiejar']},
headers = self.headers,
formdata = {
'_xsrf': xsrf,
'email': 'z1996914@outlook.com',
'password': '********'
},
callback = self.after_login,
dont_filter = True
)]
#make_requests_from_url會(huì)調(diào)用parse嗽冒,就可以與CrawlSpider的parse進(jìn)行銜接了
def after_login(self, response) :
for url in self.start_urls :
yield self.make_requests_from_url(url)
僅為個(gè)人學(xué)習(xí)小結(jié),若有錯(cuò)處补履,歡迎指正~