scrapy下載中間件

scrapy提供了兩種中間件,下載中間件(Downloader Middleware)和Spider中間件(Spider Middleware)

下載中間件

下載中間件是scrapy提供用于用于在爬蟲過程中可修改Request和Response,用于擴展scrapy的功能堰氓;比如:

  1. 可以在請求被Download之前怔蚌,請求頭部加上某些信息嫁佳;
  2. 完成請求之后纸淮,回包需要解壓等處理允趟;
如何激活下載中間:

在配置文件settings.py中的DOWNLOADER_MIDDLEWARES中配置鍵值對董栽,鍵為要打開的中間件码倦,值為數字,代表優(yōu)先級锭碳,值越低袁稽,優(yōu)先級越高。
scrapy還有一個內部自帶的下載中間件配置DOWNLOADER_MIDDLEWARES_BASE(不可覆蓋)工禾。scrapy在啟用是會結合DOWNLOADER_MIDDLEWARES_BASEDOWNLOADER_MIDDLEWARES运提,若要取消scrapy默認打開的中間,可在DOWNLOADER_MIDDLEWARES將該中間的值置為0闻葵。

DOWNLOADER_MIDDLEWARES_BASE = 
{
    'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
    'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,
    'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
    'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
    'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
    'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560,
    'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,
    'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,
    'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
    'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
    'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
}
如何編寫一個下載中間件
class Scrapy.downloadermiddleares.DownloaderMiddleware
process_request(request, spider)

當每個Request對象經過下載中間件時會被調用民泵,優(yōu)先級越高的中間件,越先調用槽畔;該方法應該返回以下對象:None/Response對象/Request對象/拋出IgnoreRequest異常栈妆;

  1. 返回None:scrapy會繼續(xù)執(zhí)行其他中間件相應的方法;
  2. 返回Response對象:scrapy不會再調用其他中間件的process_request方法厢钧,也不會去發(fā)起下載鳞尔,而是直接返回該Response對象;
  3. 返回Request對象:scrapy不會再調用其他中間件的process_request()方法早直,而是將其放置調度器待調度下載寥假;
  4. 拋出IgnoreRequest異常:已安裝中間件的process_exception()會被調用,如果它們沒有捕獲該異常霞扬,則Request.errback會被調用糕韧;如果再沒被處理枫振,它會被忽略,且不會寫進日志萤彩。
process_response(request, response, spider)

當每個Response經過下載中間件會被調用粪滤,優(yōu)先級越高的中間件,越晚被調用雀扶,與process_request()相反杖小;該方法返回以下對象:Response對象/Request對象/拋出IgnoreRequest異常。

  1. 返回Response對象:scrapy會繼續(xù)調用其他中間件的process_response方法愚墓;
  2. 返回Request對象:停止中間器調用予权,將其放置到調度器待調度下載;
  3. 拋出IgnoreRequest異常:Request.errback會被調用來處理函數转绷,如果沒有處理伟件,它將會被忽略且不會寫進日志。
process_exception(request, exception, spider)

process_exception()process_request()拋出異常時會被調用议经,應該返回以下對象:None/Response對象/Request對象斧账;

  1. 如果返回None:scrapy會繼續(xù)調用其他中間件的process_exception()
  2. 如果返回Response對象:中間件鏈的process_response()開始啟動煞肾,不會繼續(xù)調用其他中間件的process_exception()咧织;
  3. 如果返回Request對象:停止中間器的process_exception()方法調用,將其放置到調度器待調度下載籍救。
from_crawler(cls, crawler)

如果存在該函數习绢,from_crawler會被調用使用crawler來創(chuàng)建中間器對象,必須返回一個中間器對象蝙昙,通過這種方式闪萄,可以訪問到crawler的所有核心部件,如settings奇颠、signals等败去。

scray提供的一些下載中間件

以下講述的是一些常用的下載中間件,更多的下載中間件請查看文檔和代碼

HttpProxyMiddleware

scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware

用以設置代理服務器烈拒,通過設定Request.meta['proxy']來設置代理圆裕,會從環(huán)境變量http_proxyhttps_proxy荆几、no_proxy依次獲认抛薄;我們以http://httpbin.org/ip的返回來測試下:

#shell 命令
export http_proxy='http://193.112.216.55:1234'
# -*- coding: utf-8 -*-
import scrapy

class ProxySpider(scrapy.Spider):
    name = 'proxy'
    allowed_domains = ['httpbin.org']
    start_urls = ['http://httpbin.org/ip']

    def parse(self, response):
        print(response.text)

運行scrapy crawl proxy --nolog吨铸,獲得以下結果:

{"origin":"111.231.115.150, 193.112.216.55"}

返回了我們設置的代理地址IP行拢。

UserAgentMiddleware

scrapy.downloadermiddlewares.useragent.UserAgentMiddleware

通過配置項USER_AGENT設置用戶代理;我們以http://httpbin.org/headers的返回來看看測試下:

settings.py
#...
#UserAgentMiddleware默認打開
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
#...
# -*- coding: utf-8 -*-
import scrapy

class UserAgentSpider(scrapy.Spider):
    name = 'user_agent'
    allowed_domains = ['httpbin.org']
    start_urls = ['http://httpbin.org/headers']

    def parse(self, response):
        print(response.text)

運行scrapy crawl user_agent --nolog诞吱,獲得以下結果:

{"headers":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Accept-Encoding":"gzip,deflate","Accept-Language":"en","Connection":"close","Host":"httpbin.org","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"}}

返回了我們設置的用戶代理剂陡。

使用隨機用戶代理與IP代理

某些網站會通過檢測訪問IPUser-agent來進行反爬蟲狈涮,如果檢測到來自同一IP的大量請求,會判斷該IP正在進行爬蟲鸭栖,故而拒絕請求。有些網站也會檢測User-Agent握巢。我們可以使用多個代理IP和不同的User-Agent來對網站數據進行爬取晕鹊,避免被封禁IP。
我們可以通過繼承HttpProxyMiddlewareUserAgentMiddleware并修改來使得scrapy使用proxy和user-agent按我們的想法來運行暴浦。HttpProxyMiddlewareUserAgentMiddlewarehttpproxy.pyuseragent.py

代碼如下:

#middlewares.py
# -*- coding: utf-8 -*-

# Define here the models for your spider middleware
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/spider-middleware.html

from scrapy import signals
from scrapy.downloadermiddlewares.httpproxy import HttpProxyMiddleware
from scrapy.exceptions import NotConfigured
from collections import defaultdict
from urllib.parse import urlparse
from faker import Faker                 #引入Faker溅话,pip install faker下載
import random

class RandomHttpProxyMiddleware(HttpProxyMiddleware):

    def __init__(self, auth_encoding='latin-1', proxy_list = None):
        if not proxy_list:
            raise NotConfigured
        self.proxies = defaultdict(list)
        for proxy in proxy_list:
            parse = urlparse(proxy)
            self.proxies[parse.scheme].append(proxy)        #生成dict,鍵為協(xié)議歌焦,值為代理ip列表

    @classmethod
    def from_crawler(cls, crawler):
        if not crawler.settings.get('HTTP_PROXY_LIST'):
            raise NotConfigured

        http_proxy_list = crawler.settings.get('HTTP_PROXY_LIST')   #從配置文件中讀取
        auth_encoding = crawler.settings.get('HTTPPROXY_AUTH_ENCODING', 'latin-1')

        return cls(auth_encoding, http_proxy_list)

    def _set_proxy(self, request, scheme):
        proxy = random.choice(self.proxies[scheme])     #隨機抽取選中協(xié)議的IP
        request.meta['proxy'] = proxy
        
class RandomUserAgentMiddleware(object):

    def __init__(self):
        self.faker = Faker(local='zh_CN')
        self.user_agent = ''

    @classmethod
    def from_crawler(cls, crawler):
        o = cls()
        crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
        return o

    def spider_opened(self, spider):
        self.user_agent = getattr(spider, 'user_agent',self.user_agent)

    def process_request(self, request, spider):
        self.user_agent = self.faker.user_agent()       #獲得隨機user_agent
        request.headers.setdefault(b'User-Agent', self.user_agent)
#settings.py
#...
DOWNLOADER_MIDDLEWARES = {
    'newproject.middlewares.RandomHttpProxyMiddleware': 543,
    'newproject.middlewares.RandomUserAgentMiddleware': 550,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware':None,
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware':None,
}
HTTP_PROXY_LIST = [
    'http://193.112.216.55:1234',
    'http://118.24.172.34:1234',
]
#...
#anything.py
# -*- coding: utf-8 -*-
import scrapy
import json
import pprint
class AnythingSpider(scrapy.Spider):
    name = 'anything'
    allowed_domains = ['httpbin.org']
    start_urls = ['http://httpbin.org/anything']

    def parse(self, response):
        ret = json.loads(response.text)
        pprint.pprint(ret)

上面引入了faker庫飞几,該庫是用來偽造數據的庫,十分方便独撇。我們通過訪問http://httpbin.org/anything來得到我們的請求內容屑墨;如下:

#scrapy crawl anything --nolog
{'args': {},
 'data': '',
 'files': {},
 'form': {},
 'headers': {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
             'Accept-Encoding': 'gzip,deflate',
             'Accept-Language': 'en',
             'Cache-Control': 'max-age=259200',
             'Connection': 'close',
             'Host': 'httpbin.org',
             'User-Agent': 'Opera/8.85.(Windows NT 5.2; sc-IT) Presto/2.9.177 '
                           'Version/10.00'},
 'json': None,
 'method': 'GET',
 'origin': '193.112.216.55',
 'url': 'http://httpbin.org/anything'}
 
 #scrapy crawl anything --nolog
 {'args': {},
 'data': '',
 'files': {},
 'form': {},
 'headers': {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
             'Accept-Encoding': 'gzip,deflate',
             'Accept-Language': 'en',
             'Cache-Control': 'max-age=259200',
             'Connection': 'close',
             'Host': 'httpbin.org',
             'User-Agent': 'Mozilla/5.0 (Macintosh; PPC Mac OS X 10_12_3) '
                           'AppleWebKit/5342 (KHTML, like Gecko) '
                           'Chrome/40.0.810.0 Safari/5342'},
 'json': None,
 'method': 'GET',
 'origin': '118.24.172.34',
 'url': 'http://httpbin.org/anything'}

可以看到,我們的spider通過下載中間件纷铣,不斷的更改了IPUser-Agent卵史。

總結

本篇講述了什么是下載中間件以及如何自定義和啟用下載中間件,最后實踐了自定義下載中間件搜立。后面將會學習另一個中間件:Spider中間件以躯。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市啄踊,隨后出現的幾起案子忧设,更是在濱河造成了極大的恐慌,老刑警劉巖颠通,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件址晕,死亡現場離奇詭異,居然都是意外死亡蒜哀,警方通過查閱死者的電腦和手機斩箫,發(fā)現死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撵儿,“玉大人乘客,你說我怎么就攤上這事〉硇” “怎么了易核?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長浪默。 經常有香客問我牡直,道長缀匕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任碰逸,我火速辦了婚禮乡小,結果婚禮上,老公的妹妹穿的比我還像新娘饵史。我一直安慰自己满钟,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布胳喷。 她就那樣靜靜地躺著湃番,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吭露。 梳的紋絲不亂的頭發(fā)上吠撮,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音讲竿,去河邊找鬼泥兰。 笑死,一個胖子當著我的面吹牛戴卜,可吹牛的內容都是我干的逾条。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼投剥,長吁一口氣:“原來是場噩夢啊……” “哼师脂!你這毒婦竟也來了?” 一聲冷哼從身側響起江锨,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤吃警,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后啄育,有當地人在樹林里發(fā)現了一具尸體酌心,經...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年挑豌,在試婚紗的時候發(fā)現自己被綠了安券。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡氓英,死狀恐怖侯勉,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情铝阐,我是刑警寧澤址貌,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響练对,放射性物質發(fā)生泄漏遍蟋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一螟凭、第九天 我趴在偏房一處隱蔽的房頂上張望虚青。 院中可真熱鬧,春花似錦赂摆、人聲如沸挟憔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至政恍,卻和暖如春汪拥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背篙耗。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工迫筑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宗弯。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓脯燃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蒙保。 傳聞我的和親對象是個殘疾皇子辕棚,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內容