前言
我們?cè)趯懸粋€(gè)簡(jiǎn)單的爬蟲時(shí),爬蟲代碼很容易就會(huì)被后臺(tái)檢測(cè)到進(jìn)而封掉牛郑。那么怎么樣才能避免這些事發(fā)生呢怠肋?這一這篇文章我們一起來學(xué)習(xí),如何健壯我們的爬蟲代碼淹朋。
目錄
Spider
Middleware
瞎比比
Spider
當(dāng) start_urls 未被指定笙各,會(huì)調(diào)用 start_requests() ,該方法可以用于在爬取數(shù)據(jù)之前,先進(jìn)行模擬登陸础芍。
import scrapy
from scrapy.http import Request
from scrapy.selector import Selector
from urllib.parse import urljoin
from doubanTop250.items import Doubantop250Item
class DoubanTop250Spider(scrapy.spiders.Spider):
# 此處為上面留下的小坑
name = "douban"
# 設(shè)置允許爬取的域名
allowed_domains = ["douban.com"]
# header信息
my_header = {
'Host': 'www.douban.com',
'Referer': 'https://movie.douban.com',
}
# 表單需要提交的數(shù)據(jù)
form_data = {'user': 'zone', 'pass': 'zone7'}
# 自定義信息杈抢,向下層響應(yīng)(response)傳遞下去
customer_data = {'key1': 'value1', 'key2': 'value2'}
def start_requests(self):
return [scrapy.FormRequest("https://movie.douban.com/login",
formdata=self.form_data, # 表單提交的數(shù)據(jù)
headers=self.my_header,
method='POST', # GET or POST
meta=self.customer_data, # 自定義,向response傳遞數(shù)據(jù)
errback=self.error_handle,
callback=self.logged_in,
# 如果需要多次提交表單仑性,且url一樣惶楼,那么就必須加此參數(shù) dont_filter,防止被當(dāng)成重復(fù)網(wǎng)頁過濾掉了
dont_filter=True
)]
def logged_in(self, response):
# 解析模擬登陸數(shù)據(jù)
pass
def parse(self, response):
# 默認(rèn)回調(diào)函數(shù)
pass
def close(self,reson):
# 關(guān)閉時(shí)調(diào)用
pass
scrapy.FormRequest 部分參數(shù)解析
parse(self, response)
當(dāng) start_requests 沒有指定回調(diào)函數(shù)時(shí)诊杆,默認(rèn)回調(diào) parse(self,response) 方法歼捐。parse 負(fù)責(zé)處理 response 并返回處理的數(shù)據(jù)以及(/或)跟進(jìn)的 URL。
該方法及其他的 Request 回調(diào)函數(shù)必須返回一個(gè)包含 Request晨汹、dict 或 Item 的可迭代的對(duì)象豹储。簡(jiǎn)單的來說,所謂的回調(diào)函數(shù)淘这,其實(shí)就是告訴spider剥扣,在拿到了網(wǎng)站的 response 以后,交給誰來進(jìn)行處理后面的頁面的解析工作铝穷。這個(gè)看上一篇文章的代碼就可以知道朦乏。
Middleware
這里說一下,關(guān)于自定義 middleware 需要重寫的幾個(gè)方法氧骤,可能你看完這一段長(zhǎng)篇大論之后呻疹,還是懵逼的,沒關(guān)系,我也是這樣刽锤。不過镊尺,當(dāng)你繼續(xù)往下看,看一個(gè)實(shí)例之后并思,你就會(huì)明白庐氮。
process_request(request, spider)
當(dāng)每個(gè)request通過下載中間件時(shí),該方法被調(diào)用宋彼。
process_request() 必須返回其中之一: 返回 None 弄砍、返回一個(gè) Response 對(duì)象、返回一個(gè) Request 對(duì)象或 raise IgnoreRequest 输涕。
如果其返回 None 音婶,Scrapy將繼續(xù)處理該request,執(zhí)行其他的中間件的相應(yīng)方法莱坎,直到合適的下載器處理函數(shù)(download handler)被調(diào)用衣式, 該request被執(zhí)行(其response被下載)。
如果其返回 Response 對(duì)象檐什,Scrapy將不會(huì)調(diào)用 任何 其他的 process_request() 或 process_exception() 方法碴卧,或相應(yīng)地下載函數(shù); 其將返回該 response乃正。 已安裝的中間件的 process_response() 方法則會(huì)在每個(gè) response 返回時(shí)被調(diào)用住册。
如果其返回 Request 對(duì)象,Scrapy則停止調(diào)用 process_request方法并重新調(diào)度返回的 request瓮具。當(dāng)新返回的 request 被執(zhí)行后荧飞, 相應(yīng)地中間件鏈將會(huì)根據(jù)下載的 response 被調(diào)用。
如果其 raise 一個(gè) IgnoreRequest 異常搭综,則安裝的下載中間件的 process_exception() 方法會(huì)被調(diào)用。如果沒有任何一個(gè)方法處理該異常划栓, 則 request 的errback(Request.errback) 方法會(huì)被調(diào)用兑巾。如果沒有代碼處理拋出的異常, 則該異常被忽略且不記錄(不同于其他異常那樣)忠荞。
參數(shù):
request(Request 對(duì)象)–處理的 request
spider(Spider 對(duì)象)–該 request 對(duì)應(yīng)的 spider
process_response(request, response, spider)
process_request() 必須返回以下之一: 返回一個(gè) Response 對(duì)象蒋歌、 返回一個(gè) Request 對(duì)象或 raise 一個(gè) IgnoreRequest 異常。
如果其返回一個(gè) Response (可以與傳入的response相同委煤,也可以是全新的對(duì)象)堂油, 該response會(huì)被在鏈中的其他中間件的 process_response() 方法處理。
如果其返回一個(gè) Request 對(duì)象碧绞,則中間件鏈停止府框, 返回的request會(huì)被重新調(diào)度下載。處理類似于 process_request() 返回request所做的那樣讥邻。
如果其拋出一個(gè) IgnoreRequest 異常迫靖,則調(diào)用 request 的 errback( Request.errback)院峡。 如果沒有代碼處理拋出的異常,則該異常被忽略且不記錄(不同于其他異常那樣)系宜。
參數(shù):
request (Request 對(duì)象) – response 所對(duì)應(yīng)的 request
response (Response 對(duì)象) – 被處理的 response
spider (Spider 對(duì)象) – response 所對(duì)應(yīng)的 spider
process_exception(request, exception, spider)
當(dāng)下載處理器(download handler)或 process_request() (下載中間件)拋出異常(包括IgnoreRequest異常)時(shí)照激,Scrapy調(diào)用 process_exception() 。
process_exception() 應(yīng)該返回以下之一: 返回 None 盹牧、 一個(gè) Response 對(duì)象俩垃、或者一個(gè) Request 對(duì)象。
如果其返回 None 汰寓,Scrapy將會(huì)繼續(xù)處理該異常口柳,接著調(diào)用已安裝的其他中間件的 process_exception() 方法,直到所有中間件都被調(diào)用完畢踩寇,則調(diào)用默認(rèn)的異常處理啄清。
如果其返回一個(gè) Response 對(duì)象,則已安裝的中間件鏈的 process_response() 方法被調(diào)用俺孙。Scrapy將不會(huì)調(diào)用任何其他中間件的 process_exception() 方法辣卒。
如果其返回一個(gè) Request 對(duì)象, 則返回的request將會(huì)被重新調(diào)用下載睛榄。這將停止中間件的 process_exception() 方法執(zhí)行荣茫,就如返回一個(gè)response的那樣。
參數(shù):
request (是 Request 對(duì)象) – 產(chǎn)生異常的request
exception (Exception 對(duì)象) – 拋出的異常
spider (Spider 對(duì)象) – request對(duì)應(yīng)的spider
UserAgentMiddleware
多數(shù)情況下场靴,網(wǎng)站都會(huì)通過 user-agent 來識(shí)別訪問的用戶是否為一臺(tái)機(jī)器啡莉,就像爬取豆瓣一樣,如果你沒有設(shè)置 user-agent 的話旨剥,就會(huì)被拒絕訪問咧欣。因此,我們只使用一個(gè) user-agent 顯然是不夠的轨帜,因此魄咕,我們要想個(gè)辦法來設(shè)置隨機(jī)的 user-agent。于是蚌父,我們便可以自定義 UserAgentMiddleware 來解決這個(gè)問題哮兰。
首先,在 setting.py 中添加 user-agent 苟弛,如下:
MY_USER_AGENT = [
'MSIE (MSIE 6.0; X11; Linux; i686) Opera 7.23',
'iTunes/9.0.3 (Macintosh; U; Intel Mac OS X 10_6_2; en-ca)',
'Opera/9.20 (Macintosh; Intel Mac OS X; U; en)',
'Opera/9.0 (Macintosh; PPC Mac OS X; U; en)',
'Mozilla/4.76 [en_jp] (X11; U; SunOS 5.8 sun4u)',
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6',
'Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER',
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)',
'Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20',
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6',
'Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1',
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)',
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070731 Ubuntu/dapper-security Firefox/1.5.0.12',
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)',
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1',
'Mozilla/4.8 [en] (X11; U; SunOS; 5.7 sun4u)'
]
然后在 middlewares.py 中添加如下代碼:
class MyUserAgentMiddleware(UserAgentMiddleware):
def __init__(self, user_agent):
self.user_agent = user_agent
@classmethod
def from_crawler(cls, crawler):
return cls(
user_agent=crawler.settings.get('MY_USER_AGENT')
)
def process_request(self, request, spider):
agent = random.choice(self.user_agent)
request.headers['User-Agent'] = agent
最后啟用這個(gè) middleware 喝滞,在 setting.py 中修改 MyUserAgentMiddleware 的優(yōu)先級(jí),如下:
DOWNLOADER_MIDDLEWARES = {
# 首先禁止其原有的 UserAgentMiddleware
'scrapy.downloadermiddleware.useragent.UserAgentMiddleware': None,
'doubanTop250.middlewares.MyUserAgentMiddleware': 300,
'doubanTop250.middlewares.ProxyMiddleware': 543,
}
ProxyMiddleware
除了要更改 user-agent 之外膏秫,我們還要使用 ip 代理來健壯我們的爬蟲程序右遭。那么,在 scrapy 中如何來設(shè)置我們的 ip 代理呢?
首先狸演,在 setting.py 中添加如下代碼言蛇,為代理 ip:
PROXIES = [
"194.186.20.62:21231"
, "66.153.222.162:54684"
, "219.234.5.128:3128"
, "117.114.149.66:53281"
, "117.114.149.66:53281"
]
ip 來自西刺代理,不過好像好多沒用的代理宵距。
然后在 middlewares.py 中添加如下代碼:
from scrapy import signals
import random
class ProxyMiddleware(object):
def __init__(self, ip):
self.ip = ip
@classmethod
def from_crawler(cls, crawler):
return cls(ip=crawler.settings.get('PROXIES'))
def process_request(self, request, spider):
ip = random.choice(self.ip)
request.meta['proxy'] = ip
最后啟用這個(gè) middleware 腊尚,在 setting.py 中修改 ProxyMiddleware 的優(yōu)先級(jí),如下:
DOWNLOADER_MIDDLEWARES = {
# 首先禁止其原有的 UserAgentMiddleware
'scrapy.downloadermiddleware.useragent.UserAgentMiddleware': None,
'doubanTop250.middlewares.MyUserAgentMiddleware': 300,
'doubanTop250.middlewares.ProxyMiddleware': 543,
}
瞎比比
經(jīng)過這些操作之后满哪,你的代碼等級(jí)就提升了一個(gè)高度了婿斥。