簡介
中間件是Scrapy裏面的一個核心概念。使用中間件可以在爬蟲的請求發起之前或者請求返回之後對數據進行定製化修改,從而開發出適應不同情況的爬蟲。
在Scrapy中有兩種中間件:下載器中間件(Downloader Middleware)和爬蟲中間件(Spider Middleware)。
安裝
上一章已經安裝過了,所以這一步可以跳過
知識
爬蟲中間件與下載中間件
在項目工程中,有一個middlewares.py文件,如下:
關於每個函數調用的時刻,可以通過調試看日誌輸出,會更清晰
class JobspiderSpiderMiddleware(object):
@classmethod
def from_crawler(cls, crawler):
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
print("類名:JobspiderSpiderMiddleware 方法:from_crawler 觸發條件(調用時間):創建爬蟲對象時調用")
return s
def process_spider_input(self, response, spider):
print("類名:JobspiderSpiderMiddleware 方法:process_spider_input 觸發條件(調用時間):應答輸入時調用")
return None
def process_spider_output(self, response, result, spider):
print("類名:JobspiderSpiderMiddleware 方法:process_spider_output 觸發條件(調用時間):應答輸出時調用")
for i in result:
yield i
def process_spider_exception(self, response, exception, spider):
print("類名:JobspiderSpiderMiddleware 方法:process_spider_exception 觸發條件(調用時間):解析或處理輸入的時候代碼異常調用")
pass
def process_start_requests(self, start_requests, spider):
print("類名:JobspiderSpiderMiddleware 方法:process_start_requests 觸發條件(調用時間):爬蟲發起請求時調用")
for r in start_requests:
yield r
def spider_opened(self, spider):
print("類名:JobspiderSpiderMiddleware 方法:spider_opened 觸發條件(調用時間):爬蟲打開時調用")
spider.logger.info('Spider opened: %s' % spider.name)
lass JobspiderDownloaderMiddleware(object):
@classmethod
def from_crawler(cls, crawler):
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
print("類名:JobspiderDownloaderMiddleware 方法:from_crawler 觸發條件(調用時間):創建爬蟲對象時調用")
return s
def process_request(self, request, spider):
print("類名:JobspiderDownloaderMiddleware 方法:process_request 觸發條件(調用時間):請求通過下載器時調用 在下載之前連接Internet網頁信息之前調用 ")
return None
def process_response(self, request, response, spider):
print("類名:JobspiderDownloaderMiddleware 方法:process_response 觸發條件(調用時間):下載器返回應答時調用")
return response
def process_exception(self, request, exception, spider):
print("類名:JobspiderDownloaderMiddleware 方法:process_exception 觸發條件(調用時間):請求異常時調用")
pass
def spider_opened(self, spider):
print("類名:JobspiderDownloaderMiddleware 方法:spider_opened 觸發條件(調用時間):爬蟲打開時時調用")
spider.logger.info('Spider opened: %s' % spider.name)
在中間件中,可以實現換ip,處理ua,cookie等反爬措施,一般會放在process_request中處理。如果需要自定義的話,也很簡單,只要保證函數名一直,然後添加到配置文件中啓動就行了。比如:
class MyDownloaderMiddleware(object):
def process_request(self, request, spider):
print("自定義中間件 process_request 方法調用")
然後在setting中,打開即可
# Enable or disable spider middlewares
# See https://doc.scrapy.org/en/latest/topics/spider-middleware.html
SPIDER_MIDDLEWARES = {
'JobSpider.middlewares.JobspiderSpiderMiddleware': 543,
}
# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
'JobSpider.middlewares.MyDownloaderMiddleware': 400,
'JobSpider.middlewares.JobspiderDownloaderMiddleware': 543,
}
利用中間修改請求頭
只需要修改下載中間件中的process_requests方法即可,參數request是整個框架傳遞的請求,你可以在原來的請求基礎之上,進行請求頭修改
def process_request(self, request, spider):
print("類名:JobspiderDownloaderMiddleware 方法:process_request 觸發條件(調用時間):請求通過下載器時調用 在下載之前連接Internet網頁信息之前調用")
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"
}
request.headers = Headers(headers)
return None
各個模塊詳細的說明
item對象
用於定義數據字段,用來處理爬蟲模塊與管道模塊的數據傳輸
spider爬蟲對象
yield item 用來傳遞信息對象,傳給pipeline管道
yield request 生成新的請求,傳給調度器進行新的url處理
request對象
Scrapy使用request對象來爬取web站點
request對象:
scrapy.http.Request(url[,callback,method=‘GET’,headers,body,cookies,meta,encoding=‘utf-8’,priority=0,dont_filter=False,errback])
一個request對象代表一個HTTP請求,通常有Spider產生,經Downloader執行從而產生一個Response。
參數:
url: 用於請求的URL
callback:指定一個回調函數,該回調函數以這個request對應的response作爲第一個參數。如果未指定callback,則默認使用spider的parse()方法。
method:HTTP請求的方法,默認爲GET
meta:指定Request.meta屬性的初始值,字典類型。可以在回調函數們之間傳遞參數
body:請求體
headers:request的頭信息
cookies:cookie
encoding:請求的編碼,默認爲utf-8
priority:請求的優先級
dont_filter(boolean):指定該請求是否被Scheduler過濾。該參數可以是request重複使用(Scheduler默認過濾重複請求)。謹慎使用!!errback:處理異常的回調函數。
Response應答對象
一個Response對象表示的HTTP響應,這通常由下載器提供給到爬蟲進行處理
常見屬性
url包含響應的URL的字符串。
status表示響應的HTTP狀態的整數。示例:200,404。
headers包含響應標題的類字典對象。
body正文。
meta獲得Request.meta從您的爬蟲發送的原始屬性。
使用ImagesPipeline下載圖片
ImagesPipeline簡介
Scrapy用ImagesPipeline類提供一種方便的方式來下載和存儲圖片。
可以將下載圖片轉換成通用的JPG和RGB格式
ImagesPipeline重載的方法
需要在自定義的ImagePipeline類中重載的方法:
方法一:get_media_requests(self,item, info):
Pipeline將從item中獲取圖片的URLs並下載它們,並返回一個Request對象所以必須重載get_media_requests
方法二:item_completed(self, results, item, info):
當完成下載後,結果將以元組形式發送到item_completed方法,圖片下載完畢後,處理結果會以元組的方式返回給item_completed()函數。這個元組定義如下:
(success, image_info_or_failure)
其中,第一個元素表示圖片是否下載成功;第二個元素是一個字典。
如果success=true,表示成功下載,image_info_or_error詞典包含以下鍵值對:url:原始URL
,path:本地存儲路徑,checksum:校驗碼。
如果success=false,表示下載失敗,image_info_or_error則包含一些出錯信息。
方法三:file_path(self, request, response=None, info=None):
返回圖片存儲路徑
爬取鬥魚直播圖片
創建scrapy項目
scrapy startproject douyuspider
編寫items.py
class DouyuItem(scrapy.Item):
# 暱稱 爲後邊修改文件名準備
nickname = scrapy.Field()
# 圖片鏈接地址 這個參數名如果不修改源文件的話 必須定義爲image_urls
image_urls = scrapy.Field()
創建爬蟲模板
cd douyuspider
scrapy genspider douyu http://capi.douyucdn.cn
編寫爬蟲文件
import scrapy
import json
from douyu.items import DouyuItem
class DouspiderSpider(scrapy.Spider):
name = 'douspider'
start_urls = ["http://capi.douyucdn.cn/api/v1/getVerticalRoom?limit=20&offset="]
def parse(self, response):
jsonInfo = json.loads(response.text)
dataList = jsonInfo["data"]
for data in dataList:
item = DouyuItem()
item["nickname"] =data["nickname"]
item["image_urls"] = data["vertical_src"]
yield item
編寫管道文件
使用request中的meta屬性,傳遞參數,用來當做存儲路徑。
class DouyuPipeline(ImagesPipeline):
# 傳遞url下載的
def get_media_requests(self, item, info):
meta = {"item":item }
yield Request(url=item["image_urls"],meta=meta)
# 返回下載結果信息
def item_completed(self, results, item, info):
print(results)
print(type(results))
# 修改文件名
def file_path(self, request, response=None, info=None):
nickname = request.meta["item"]["nickname"]
return nickname + ".jpg"
修改設置文件
ITEM_PIPELINES = {'douyuSpider.pipelines.DownloadimgPipeline': 1}# Images的存放位置,之後會在pipelines.py裏調用
# 設置圖片存儲路徑
IMAGES_STORE = './Image'
測試
from scrapy import cmdline
name = "douspider"
cmd = "scrapy crawl {0}".format(name)
cmdline.execute(cmd.split())
任務
爬取表情包 鬥圖啦前10頁圖片
http://www.doutula.com/