Scrapy,Python開發的一個快速、高層次的屏幕抓取和web抓取框架,用於抓取web站點並從頁面中提取結構化的數據。Scrapy用途普遍,能夠用於數據挖掘、監測和自動化測試。css
Scrapy吸引人的地方在於它是一個框架,任何人均可以根據需求方便的修改。它也提供了多種類型爬蟲的基類,如BaseSpider、sitemap爬蟲等,最新版本又提供了web2.0爬蟲的支持。html
Scrap,是碎片的意思,這個Python的爬蟲框架叫Scrapy。python
Scrapy 是基於twisted框架開發而來,twisted是一個流行的事件驅動的python網絡框架。所以Scrapy使用了一種非阻塞(又名異步)的代碼來實現併發。git
1. 這是官方給出的架構圖github
2. 各個組件的做用web
引擎(Engine)
引擎負責控制數據流在系統中全部組件中流動,並在相應動做發生時觸發事件。ajax
調度器(Scheduler)
用來接收引擎發過來的請求, 壓入隊列中, 並在引擎再次請求的時候返回. 至關於一個URL的優先級隊列, 由它來決定下一個要抓取的網址是什麼, 同時去除重複的網址redis
下載器(Downloader)
用於下載網頁內容, 並將網頁內容返回給EGINE,下載器是創建在twisted這個高效的異步模型上的mongodb
下載器中間件(Downloader middlewares)
下載器中間件是在引擎及下載器之間的特定鉤子(specific hook),處理Downloader傳遞給引擎的response(也包括引擎傳遞給下載器的Request)。 其提供了一個簡便的機制,經過插入自定義代碼來擴展Scrapy功能。shell
爬蟲(Spiders)
Spider是Scrapy用戶編寫用於分析response並提取item(即獲取到的item)或額外跟進的URL的類。 每一個spider負責處理一個特定(或一些)網站
爬蟲中間件(Spider middlewares)
Spider中間件是在引擎及Spider之間的特定鉤子(specific hook),處理spider的輸入(response)和輸出(items及requests)。 其提供了一個簡便的機制,經過插入自定義代碼來擴展Scrapy功能。
項目管道(Item Pipeline)
Item Pipeline負責處理被spider提取出來的item。典型的處理有清理、 驗證及持久化(例如存取到數據庫中)。
0. 程序開始運行 1. Spiders用yeild將url發送給Engine(引擎) 2. Engine把url發送給調度器(Scheduler) 3. 調度器(Scheduler)會將url生成request返回給Engine(引擎) 4. Engine(引擎)拿到request,經過下載器中間件(Downloader middlewares)進行層層過濾發送給下載器(Downloader) 5. 下載器(Downloader)在網上獲取到response數據以後,又通過下載器中間件(Downloader middlewares)進行層層過濾發送給Engine(引擎) 6. Engine(引擎)獲取到response數據以後,返回給Spiders,Spiders的parse()方法對獲取到的response數據進行處理,解析出items或者requests 7. 將解析出來的items或者requests發送給Engine(引擎) 8. Engine(引擎)獲取到items或者requests,將items發送給Item Pipeline進行數據的存儲 9. 注意,只有當調度器中不存在任何request了,整個程序纔會中止,也就是說,對於下載失敗的URL,Scrapy也會從新下載
1. process_request(request, spider) 當每一個request經過下載中間件時,該方法被調用,這裏能夠修改UA,代理,Refferer 2. process_response(request, response, spider) 這裏能夠看返回是不是200加入重試機制 3. process_exception(request, exception, spider) 這裏能夠處理超時 4. SpiderMiddleware主要處理解析Item的相關邏輯修正,好比數據不完整要添加默認,增長其餘額外信息等 process_spider_input(response, spider) 當response經過spider中間件時,該方法被調用,處理該response。 rocess_spider_output(response, result, spider) 當Spider處理response返回result時,該方法被調用。 process_spider_exception(response, exception, spider) 當spider或(其餘spider中間件的) process_spider_input() 拋出異常時, 該方法被調用。
1. Linux系統 pip3 install scrapy # 從官網下載 pip3 install -i https://pypi.douban.com/simple/ scrapy # 從豆瓣源下載 2. Windows 1. pip install -i https://pypi.douban.com/simple/ wheel 2. 下載twisted: http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 3. 進入下載目錄,執行命令:pip3 install Twisted-18.9.0-cp36-cp36m-win_amd64.whl 4. pip3 install -i https://pypi.douban.com/simple/ pywin32 5. pip3 install -i https://pypi.douban.com/simple/ scrapy
1. 查看幫助 scrapy -h scrapy <command> -h 2. 有兩種命令:其中Project-only必須切到項目文件夾下才能執行,而Global的命令則不須要 Global commands: startproject # 建立項目 genspider # 建立爬蟲程序 settings # 若是是在項目目錄下,則獲得的是該項目的配置 runspider # 運行一個獨立的python文件,沒必要建立項目 shell # scrapy shell url地址 在交互式調試,如選擇器規則正確與否 fetch # 獨立於程單純地爬取一個頁面,能夠拿到請求頭 view # 下載完畢後直接彈出瀏覽器,以此能夠分辨出哪些數據是ajax請求 version # scrapy version 查看scrapy的版本,scrapy version -v查看scrapy依賴庫的版本 Project-only commands: crawl # 運行爬蟲,必須建立項目才行,確保配置文件中ROBOTSTXT_OBEY = False check # 檢測項目中有無語法錯誤 list # 列出項目中所包含的爬蟲名 edit # 編輯器,通常不用 parse # scrapy parse url地址 --callback 回調函數 #以此能夠驗證咱們的回調函數是否正確 bench # scrapy bentch壓力測試 3. 官網連接 https://docs.scrapy.org/en/latest/topics/commands.html
project_name/ scrapy.cfg project_name/ __init__.py items.py middlewares.py pipelines.py settings.py spiders/ __init__.py 爬蟲1.py 爬蟲2.py 爬蟲3.py 文件說明: scrapy.cfg 項目的主配置信息,用來部署scrapy時使用,爬蟲相關的配置信息在settings.py文件中。 items.py 設置數據存儲模板,用於結構化數據,如:Django的Model middlewares.py 中間件是處於引擎(crawler.engine)和下載器(crawler.engine.download())之間的一層組件 pipelines 數據處理行爲,如:通常結構化的數據持久化 settings.py 配置文件,如:遞歸的層數、併發數,延遲下載等。強調:配置文件的選項必須大寫不然視爲無效 spiders 爬蟲目錄,如:建立文件,編寫爬蟲規則 注意: 1、通常建立爬蟲文件時,以網站域名命名 2、默認只能在終端執行命令,爲了更便捷操做: # 在項目根目錄下新建:entrypoint.py from scrapy.cmdline import execute execute(['scrapy', 'crawl', 'qsbk', '--nolog'])
1、通常建立爬蟲文件時,以網站域名命名 2、默認只能在終端執行命令,爲了更便捷操做: # 在項目根目錄下新建:entrypoint.py from scrapy.cmdline import execute execute(['scrapy', 'crawl', 'qsbk', '--nolog'])
Spiders是定義如何抓取某個站點(或一組站點)的類,包括如何執行爬行(即跟隨連接)以及如何從其頁面中提取結構化數據(即抓取項目)。換句話說,Spiders是爲特定站點(或者在某些狀況下,一組站點)爬網和解析頁面定義自定義行爲的地方。 1. 生成初始的Requests來爬取第一個URLS,而且標識一個回調函數 第一個請求定義在start_requests()方法內默認從start_urls列表中得到url地址來生成Request請求, 默認的回調函數是parse方法。回調函數在下載完成返回response時自動觸發 2. 在回調函數中,解析response而且返回值 返回值能夠4種: 包含解析數據的字典 Item對象 新的Request對象(新的Requests也須要指定一個回調函數) 或者是可迭代對象(包含Items或Request) 3. 在回調函數中解析頁面內容 一般使用Scrapy自帶的Selectors,但很明顯你也可使用Beutifulsoup,lxml或其餘你愛用啥用啥。 4. 最後,針對返回的Items對象將會被持久化到數據庫 經過Item Pipeline組件存到數據庫:https://docs.scrapy.org/en/latest/topics/item-pipeline.html#topics-item-pipeline) 或者導出到不一樣的文件(經過Feed exports:https://docs.scrapy.org/en/latest/topics/feed-exports.html#topics-feed-exports)
使用Scrapy自帶的選擇器,獲取到的數據都是Selectors對象, 須要使用extract方法把Selectors對象的內容提取出來。 1,div.xpath('.//div[@class="author clearfix"]/a/h2/text()') 結果是Selector對象組成的列表:[<Selector xpath='xxx' data='xxx'>,] 2,div.xpath('.//div[@class="author clearfix"]/a/h2/text()').extract() 把Selector對象的內容提取出來,仍是列表:['data的內容',] 3,div.xpath('.//div[@class="author clearfix"]/a/h2/text()').extract()[0] 把提取出來的列表按索引取出某一個值,若只提取一個,也可使用extract_first() div.xpath('.//div[@class="author clearfix"]/a/h2/text()').extract_first() 4,div.xpath('.//div[@class="author clearfix"]/a/h2/text()')[0].extract() 先在列表中取出某個Selector對象,在把Selector對象的內容提取出來
1. 默認使用方式 DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter' Request(...,dont_filter=False) # 若是dont_filter=True則告訴Scrapy這個URL不參與去重。 2. 自定義去重規則 from scrapy.dupefilter import RFPDupeFilter,看源碼,仿照BaseDupeFilter # 步驟一:在項目目錄下自定義去重文件dup.py class UrlFilter(object): def __init__(self): self.visited = set() # 或者放到數據庫 @classmethod def from_settings(cls, settings): return cls() def request_seen(self, request): if request.url in self.visited: return True self.visited.add(request.url) def open(self): # can return deferred pass def close(self, reason): # can return a deferred pass def log(self, request, spider): # log that a request has been filtered pass
1. 在cmd下輸入命令 cd 工做目錄 scrapy startproject 項目名 # 建立項目 cd 項目名 scrapy genspider 應用名稱 爬取的起始url # 建立爬蟲程序 scrapy crawl 應用名稱 # 該種執行形式會顯示執行的日誌信息 scrapy crawl 應用名稱 --nolog # 該種執行形式不會顯示執行的日誌信息 scrapy crawl 應用名稱 -o xxx.xml # 存儲數據文本格式 2. 示例 # 爬取糗事百科 cd E:\SpiderProject\爬蟲\scrapy框架 # 進入工做目錄 scrapy startproject QSBK # 建立一個名爲QSBK的項目 cd QSBK # 進入項目目錄 scrapy genspider qsbk www.qiushibaike.com # 建立爬蟲程序(會在項目下的spiders目錄下建立qsbk.py程序) scrapy crawl qsbk # 執行應用程序 scrapy crawl qsbk -o qsbk.xml # 將爬取到的數據解析後存儲成xml格式的文件 3.spiders目錄下建立的qsbk.py程序 # -*- coding: utf-8 -*- import scrapy class QsbkSpider(scrapy.Spider): name = 'qsbk' # 應用名稱 # 容許爬取的域名(若是遇到非該域名的url則爬取不到數據) allowed_domains = ['www.qiushibaike.com'] # 起始爬取的url start_urls = ['http://www.qiushibaike.com/'] # 訪問起始URL並獲取結果後的回調函數,該函數的response參數就是向起始的url發送請求後,獲取的響應對象. # 該函數返回值必須爲可迭代對象或者NUll def parse(self, response): print(response.text) # 獲取字符串類型的響應內容 print(response.body) # 獲取字節類型的響應內容 4. 項目的配置文件(settings.py)相關配置 修改內容及其結果以下: 19行:假裝請求載體身份(假裝成瀏覽器) USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36' 22行:能夠忽略或者不遵照robots協議 ROBOTSTXT_OBEY = False 注意:settings.py的配置項的名稱都要大寫 5. 爬取糗百首頁中文字段子的內容和做者 對qsbk.py進行修改 """ 注意:記得修改settings.py的配置哦!!! 類QsbkSpider是繼承了scrapy.Spider這個類的, scrapy.Spider中的start_requests方法是咱們爬蟲主邏輯、策略, 由於start_requests方法默認只是作了一些簡單的爬取措施, 若是要爬取的網頁有了很強的反爬措施,此時就須要咱們重寫start_requests方法,破解反爬措施。 start_requests方法必須yield Request對象,所以須要導入 from scrapy import Request """ import scrapy from scrapy import Request # from scrapy.http import Request # 跟上面導入的Request是同樣的 class QsbkSpider(scrapy.Spider): name = 'qsbk' # 應用名稱 # 容許爬取的域名(若是遇到非該域名的url則爬取不到數據) allowed_domains = ['www.qiushibaike.com'] # 起始爬取的url start_urls = ['https://www.qiushibaike.com/text/'] def start_requests(self): # 爬蟲主邏輯 策略 yield Request( url="https://www.qiushibaike.com/text/", callback=self.parse, # 定義回調函數,不寫默認就是 parse ) def parse(self, response): # 用xpath或者BS或者 css selector進行數據解析 # 這裏的response自帶xpath方法,能夠將xpath表達式直接做用於該函數中 odiv = response.xpath('//div[@id="content-left"]/div') content_list = [] # 用於存儲解析到的數據 # xpath函數返回的爲列表,列表中存放的數據爲Selector類型的數據 # 咱們解析到的內容被封裝在了Selector對象中,須要調用extract()函數將解析的內容從Selecor中取出 for div in odiv: # 1.獲取用戶名author author = div.xpath('.//div[@class="author clearfix"]/a/h2/text()') if author: author = author[0].extract() else: author = "匿名用戶" # 2.獲取這個用戶的段子的內容contents contents = div.xpath('.//div[@class="content"]/span/text()') # 遇到換行br就會生成一個Selector對象 content_list.append({ "author": author, "content": "".join([selector.extract().strip() for selector in contents]) }) print("content_list", content_list) return content_list
""" 除了解析代碼,其他代碼跟上面的例子同樣 """
from bs4 import BeautifulSoup
def parse(self, response): print(response.text) soup = BeautifulSoup(response.text, 'lxml') odiv = soup.find_all('div', class_='article') content_list = [] # 用於存儲解析到的數據 for div in odiv: author = div.find('div', class_='author').text print(author) content = div.find('div', class_='content').text print(content) dic = { '做者': author, '內容': content } print(dic) # 將數據存儲到content_list這個列表中 content_list.append(dic) return content_list
1.主爬蟲程序 """ 登陸亞馬遜官方,搜索:iphone xs max 須要解析出來的數據:商品簡介、價格、配送方 爬取前10頁 注意在settings中設置UA反爬和不遵照robots """ import scrapy from scrapy import Request class AmazonSpider(scrapy.Spider): name = 'amazon' allowed_domains = ['www.amazon.cn'] start_urls = ['https://www.amazon.cn/'] def start_requests(self): yield Request( url="https://www.amazon.cn/s/ref=nb_sb_noss_1?__mk_zh_CN=%E4%BA%9A%E9%A9%AC%E9%80%8A%E7%BD%91%E7%AB%99&url=search-alias%3Daps&field-keywords=iphone+xs+max", callback=self.parse_index, dont_filter=True ) def parse_index(self, response): ''' 解析商品首頁獲取幾十個商品詳情的url,發請求 解析函數的返回值: (1) 字典或者迭代數據(好比列表套字典) (2) 請求Request對象 (3) item對象(經過pipeline作持久化的) ''' # 商品列表頁的全部商品的URL detail_urls = response.xpath('//*[contains(@id,"result_")]/div/div[3]/div[1]/a/@href').extract() for detail_url in detail_urls: print("detail_url", detail_url) yield Request( url=detail_url, callback=self.parse_detail, # parse_detail解析詳情頁的函數 dont_filter=True ) # 解析一個下一頁的URL,通過測試發現下面xpath解析出來的url是相對路徑,所以須要咱們拼接出完整的url # response.urljoin方法能幫咱們把要爬取的網頁域名和須要拼接的url進行拼接 next_url = response.urljoin(response.xpath('//*[@id="pagnNextLink"]/@href').extract_first()) yield Request( url=next_url, callback=self.parse_index, dont_filter=True ) def parse_detail(self, response): ''' response: 某一個商品詳情頁的響應體 ''' title = response.xpath('//*[@id="productTitle"]/text()')[0].extract().strip() price = response.xpath('//*[@id="priceblock_ourprice"]/text()')[0].extract().strip() delivery = response.xpath('//*[@id="ddmMerchantMessage"]/*[1]/text()').extract() print(title) print(price) print(delivery) 2.免費代理網站 http://www.goubanjia.com/ 3.設置代理池 當你的ip訪問亞馬遜太過頻繁的時候,亞馬遜會對你進行反爬(暫封你的ip), 若你發現沒法爬取亞馬遜網頁的時候,可設置代理池(下面會詳細講,如今先把網頁爬下來) 步驟: 1. 在免費代理網站上找一個能用的代理ip 2. 在middlewares.py中找到AmazonDownloaderMiddleware類下的process_request方法 3. 在process_request中寫入以下幾行 proxy = 'http://77.70.115.104:8080' request.meta['download_timeout'] = 20 request.meta['proxy'] = proxy
Item對象是用於收集抓取數據的簡單容器。它們提供相似字典的 API,並具備用於聲明其可用字段的方便語法。 Scrapy Items相似於Django Models,可是Scrapy Items更簡單,由於沒有不一樣字段類型的概念。 1.聲明Item 在項目中找到items.py,使用簡單的類定義語法和Field對象聲明項目 import scrapy class AmazonItem(scrapy.Item): # define the fields for your item here like: title = scrapy.Field() price = scrapy.Field() delivery = scrapy.Field() 2.使用Item # 在須要使用的地方(爬蟲主程序)導入這個類 from ..items import AmazonItem # 實例化對象並添加數據 item = AmazonItem() item["title"] = title item["price"] = price item["delivery"] = delivery 3. Demo # 上面亞馬遜的parse_detail能夠這樣寫(先實例化再設置數據) def parse_detail(self, response): ''' response: 某一個商品詳情頁的響應體 ''' title = response.xpath('//*[@id="productTitle"]/text()')[0].extract().strip() price = response.xpath('//*[@id="priceblock_ourprice"]/text()')[0].extract().strip() delivery = response.xpath('//*[@id="ddmMerchantMessage"]/*[1]/text()').extract() print(title) print(price) print(delivery) item = AmazonItem() # 先實例化 item["title"] = title # 再設置數據 item["price"] = price item["delivery"] = delivery return item # 還能夠這樣寫(實例化的時候初始化數據) def parse_detail(self, response): ''' response: 某一個商品詳情頁的響應體 ''' title = response.xpath('//*[@id="productTitle"]/text()')[0].extract().strip() price = response.xpath('//*[@id="priceblock_ourprice"]/text()')[0].extract().strip() delivery = response.xpath('//*[@id="ddmMerchantMessage"]/*[1]/text()').extract() print(title) print(price) print(delivery) item = AmazonItem(title=title, price=price, delivery=delivery) # 實例化的時候初始化數據 # 獲取字段值 print(item['title']) print(item.get('price')) # 訪問全部鍵 print(item.keys()) # 訪問全部的鍵值對 print(item.items()) return item
在一個項目被蜘蛛抓取以後,它被髮送到項目管道,該項目管道經過順序執行的幾個組件處理它。
每一個項目管道組件(有時簡稱爲「項目管道」)是一個實現簡單方法的Python類。他們收到一個項目並對其執行操做,同時決定該項目是否應該繼續經過管道或被丟棄而且再也不處理。
項目管道的典型用途是:
cleansing HTML data(清洗數據)
validating scraped data (checking that the items contain certain fields)(校驗數據)
checking for duplicates (and dropping them)(去重)
storing the scraped item in a database(排序存儲)
PipeLine只接收Item對象,PipeLine在項目中的pipelines.py裏面定義。
定義好的PipeLine類須要在settings裏面進行聲明,只要在主程序中return item就會自動去settings中找到聲明的PipeLine類,
而後對item進行處理,存儲。
# 大概在settings的67行設置pipeline類, ITEM_PIPELINES = { 'Amazon.pipelines.AmazonPipeline': 300, }
每一個項管道組件都是一個如下方法的Python類: # item進入pipeline前會調用這個方法 open_spider(self,蜘蛛) 打開蜘蛛時會調用此方法。 # 必須實現的方法,用來處理item數據的方法(核心方法) process_item(self,項目,蜘蛛) 爲每一個項目管道組件調用此方法。process_item() 返回帶數據的dict,返回一個Item (或任何後代類)對象,返回Twisted Deferred或引起 DropItem異常。丟棄的項目再也不由其餘管道組件處理。 # item處理完畢後調用這個方法 close_spider(self,蜘蛛) 當蜘蛛關閉時調用此方法。 # 當pipeline類實例化的時候,若是有from_crawler方法會先執行這個方法,再執行init方法 # 參數crawler表明這個爬蟲程序,能夠在此拿到爬蟲的名字、域名、配置參數等,均可以從這裏拿到 from_crawler(cls,crawler) 若是存在,則調用此類方法以從a建立管道實例Crawler。它必須返回管道的新實例。Crawler對象提供對全部Scrapy核心組件的訪問, 如設置和信號; 它是管道訪問它們並將其功能掛鉤到Scrapy的一種方式。
1. settings中的配置 # 與Mongodb數據庫相關配置 HOST = "127.0.0.1" PORT = 27017 USER = "root" PWD = "" DB = "amazon" TABLE = "goods" # 聲明pipeline類 ITEM_PIPELINES = { 'Amazon.pipelines.MongodbPipeline': 300, } 2. 定義pipeline類 from pymongo import MongoClient class MongodbPipeline(object): def __init__(self, host, port, user, pwd, db, table): self.host = host self.port = port self.user = user self.pwd = pwd self.db = db self.table = table @classmethod def from_crawler(cls, crawler): """ Scrapy會先經過getattr判斷咱們是否自定義了from_crawler,有則調它來完成實例化 從爬蟲程序crawler中的settings配置拿到咱們須要的數據,返回給這個類,而後調用init實例化 """ HOST = crawler.settings.get('HOST') PORT = crawler.settings.get('PORT') USER = crawler.settings.get('USER') PWD = crawler.settings.get('PWD') DB = crawler.settings.get('DB') TABLE = crawler.settings.get('TABLE') # 必須返回這個類的對象 return cls(HOST, PORT, USER, PWD, DB, TABLE) def open_spider(self, spider): """ 爬蟲剛啓動時執行一次 """ # self.client = MongoClient('mongodb://%s:%s@%s:%s' %(self.user,self.pwd,self.host,self.port)) self.client = MongoClient(host=self.host, port=self.port) def close_spider(self, spider): """ 爬蟲關閉時執行一次 """ self.client.close() def process_item(self, item, spider): # 操做並進行持久化 d = dict(item) # 把item轉換成字典類型 if all(d.values()): # 當字典不爲空的時候插入數據 self.client[self.db][self.table].save(d) # save方法也等於插入數據並保存 return item
1. 基於上面的settings再進行配置 # 聲明pipeline類 ITEM_PIPELINES = { 'Amazon.pipelines.MongodbPipeline': 300, 'Amazon.pipelines.FilePipeline': 400, } 2. 再定義一個pipeline類 class FilePipeline(object): def open_spider(self, spider): """ 爬蟲剛啓動時執行一次 """ print("文件寫入一個item") # self.client = MongoClient('mongodb://%s:%s@%s:%s' %(self.user,self.pwd,self.host,self.port)) self.file = open("file_pipeline.txt", "w") def close_spider(self, spider): """ 爬蟲關閉時執行一次 """ self.file.close() def process_item(self, item, spider): # 操做並進行持久化 d = dict(item) import json self.file.write(json.dumps(d) + "\n") 3. 多個管道的流程 ITEM_PIPELINES = { 'Amazon.pipelines.MongodbPipeline': 300, 'Amazon.pipelines.FilePipeline': 400, } 300和400是權重,設置多少都行,看本身喜愛,權重小的管道先執行, 所以MongodbPipeline先執行,執行完畢後,process_item方法必須return item, return item後會把item傳給下一個管道FilePipeline進行處理。 若是你想處理完後再也不給別人處理了,那麼能夠 from scrapy.exceptions import DropItem def process_item(self, item, spider): if 某種狀況: raise DropItem("Duplicate item found: %s" % item)
scrapy的下載中間件個Django的中間件相似, process_request、process_response等方法也是相似的 不一樣的是: Django中process_request返回一個對象後,process_response是從process_request對應的process_response開始返回, 而scrapy的下載中間件process_request返回一個對象後,process_response是從第一個process_response開始返回, 1. settings中的配置 # 大概在settings的55行 DOWNLOADER_MIDDLEWARES = { 'Amazon.middlewares.MyDownMiddleware': 543, } 2. 權重 process_request權重小的先執行 process_response權重大的先執行 process_request權重小的先執行 3. 各個方法的做用 class MyDownMiddleware(object): def process_request(self, request, spider): """ 請求須要被下載時,通過全部下載器中間件的process_request調用 :param request: :param spider: :return: None,繼續後續中間件去下載; Response對象,中止process_request的執行,開始執行process_response Request對象,中止中間件的執行,將Request從新調度 raise IgnoreRequest異常,中止process_request的執行,開始執行process_exception """ pass def process_response(self, request, response, spider): """ spider處理完成,返回時調用 :param response: :param result: :param spider: :return: Response 對象:轉交給其餘中間件process_response Request 對象:中止中間件,request會被從新調度下載 raise IgnoreRequest 異常:調用Request.errback """ print('response1') return response def process_exception(self, request, exception, spider): """ 當下載處理器(download handler)或 process_request() (下載中間件)拋出異常 :param response: :param exception: :param spider: :return: None:繼續交給後續中間件處理異常; Response對象:中止後續process_exception方法 Request對象:中止中間件,request將會被從新調用下載 """ return None 所以咱們更換代理的時候,應該放在process_exception中 def process_exception(self, request, exception, spider): # Called when a download handler or a process_request() # (from other downloader middleware) raises an exception. # Must either: # - return None: continue processing this exception # - return a Response object: stops process_exception() chain # - return a Request object: stops process_exception() chain proxy = 'http://77.70.115.104:8080' request.meta['download_timeout'] = 20 request.meta['proxy'] = proxy return request # 返回request,即把請求返回給調度器,調度器再從新去發請求
咱們跟換代理的時候,應該是在某個代理網站上,把可用的代理所有爬下來,
當出現異常的時候就去代理池中把真正能用的ip跟換到個人程序中,
所以還須要寫一個爬代理ip網站的程序,這個不用擔憂,去github中搜就能夠,
把搜到的應用結合到咱們的爬蟲程序中
1. 把下載到的程序放到咱們爬蟲程序中 2. 讀README.md文件,根據步驟進行一些初始化配置 3. 把爬ip的程序啓動後,把爬取到的代理ip存到MongoDB中 [DB] ;Configure the database information ;type: SSDB/MONGODB if use redis, only modify the host port,the type should be SSDB type = MONGODB host = 127.0.0.1 port = 27017 name = proxy 4. 若是要在爬蟲代碼中使用的話, 能夠將此api封裝成函數直接使用,例如: import requests def get_proxy(): return requests.get("http://127.0.0.1:5010/get/").content def delete_proxy(proxy): requests.get("http://127.0.0.1:5010/delete/?proxy={}".format(proxy)) 開發的api接口在這裏設置 [API] # API config http://127.0.0.1:5010 # The ip specified when starting the web API ip = 0.0.0.0 # he port on which to run the web API port = 5010
1. 首先確保已經把代理ip的那個網站爬下來了 2. 確保DB和API設置好了 3. 在咱們的爬蟲程序中新建一個proxy.py文件 代碼以下: import requests def get_proxy(): return requests.get("http://127.0.0.1:5010/get/").content def delete_proxy(proxy): requests.get("http://127.0.0.1:5010/delete/?proxy={}".format(proxy)) 4. 咱們爬蟲程序的下載中間件代碼 from .proxy import get_proxy,delete_proxy class AmazonDownloaderMiddleware(object): def process_exception(self, request, exception, spider): # Called when a download handler or a process_request() # (from other downloader middleware) raises an exception. # Must either: # - return None: continue processing this exception # - return a Response object: stops process_exception() chain # - return a Request object: stops process_exception() chain proxy = "http://"+get_proxy() request.meta['download_timeout'] = 20 request.meta["proxy"] = proxy return request