基於某一款抓包工具 : fiddler ,青花瓷 ,miteproxyphp
什麼是Fiddler?html
Fiddler是位於客戶端和服務器端的HTTP代理,也是目前最經常使用的http抓包工具之一 。 它可以記錄客戶端和服務器之間的全部 HTTP請求,能夠針對特定的HTTP請求,分析請求數據、設置斷點、調試web應用、修改請求的數據,甚至能夠修改服務器返回的數據,功能很是強大,是web調試的利器。python
Fiddler安裝mysql
Fiddler下載地址:https://www.telerik.com/fiddlerweb
傻瓜式安裝,一鍵到底。Fiddler軟件界面如圖所示:正則表達式
Fiddler設置打開Fiddler軟件,打開工具的設置。(Fiddler軟件菜單欄:Tools->Options)在HTTPS中設置以下:redis
在Connections中設置以下sql
這裏使用默認8888端口,固然也能夠本身更改,可是注意不要與已經使用的端口衝突:Allow remote computers to connect:容許別的機器把請求發送到fiddler上來chrome
安全證書下載windows
在電腦瀏覽器中輸入地址:http://localhost:8888/,點擊FiddlerRoot certificate,下載安全證書:
安全證書安裝
證書是須要在手機上進行安裝的,這樣在電腦Fiddler軟件抓包的時候,手機使用電腦的網卡上網纔不會報錯。
Android手機安裝:
把證書放入手機的內置或外置存儲卡上,而後經過手機的"系統安全-》從存儲設備安裝"菜單安裝證書。而後找到拷貝的FiddlerRoot.cer
進行安裝便可。安裝好以後,能夠在信任的憑證中找到咱們已經安裝好的安全證書。
蘋果手機安裝:
保證手機網絡和fiddler所在機器網絡是同一個網段下的在safari中訪問http://fiddle機器ip:fiddler端口,進行證書下載。而後進行安裝證書操做。在手機中的設置-》通用-》關於本機-》證書信任設置-》開啓fiddler證書信任
局域網設置 想要使用Fiddler進行手機抓包,首先要確保手機和電腦的網絡在一個內網中,可使用讓電腦和手機都鏈接同一個路由器。固然,也可讓電腦開放WIFI熱點,手機連入。這裏,我使用的方法是,讓手機和電腦同時連入一個路由器中。最後,讓手機使用電腦的代理IP進行上網。 在手機上,點擊鏈接的WIFI進行網絡修改,添加代理。進行手動設置,ip和端口號都是fiddler機器的ip和fiddler上設置的端口號。
Fiddler手機抓包測試
上述步驟都設置完成以後,用手機瀏覽器打開百度首頁,咱們就能夠順利抓包了
tools --> options --> connection -->allow remote
http: fiddler所在pc機的ip :8888/ 訪問到一張提供了證書下載功能的界面
fiddler所在機器和手機在同一網段下 :在手機上瀏覽器中訪問 http: fiddler所在pc機的ip :8888 獲取子頁面進行證書的下載和安裝(證書信任的操做)
配置你手機的代理 :將手機的代理配置成 fiddler所對應的pc機的ip和手機本身的端口
就可讓fiddler捕獲手機發起的http和https的請求
#總結: #爬蟲文件中的屬性和方法 name :爬蟲文件惟一標識 start_url:該列表中的url會被自動的進行請求發送 #自動請求發送的過程: def start_requests(self): for url in self.start_urls: yield scrapy.Request(url,callback=self.parse) #數據解析: scrapy中封裝的xpath進行數據解析 #scrapy中的xpath 和 etree中的xpath的區別 scrapy的xpath進行數據解析後返回的列表元素爲Selector對象,extract或extract_first這兩個方法將Selector對象中對應的數據取出
什麼是框架?如何學習
框架就是一個集成各類功能且具備很強通用性(能夠被應用在各類不一樣的需求中)的一個項目模板
咱們只須要學習框架中封裝好的相關功能便可
scrapy 集成的功能
高性能的數據解析操做 ,持久化存儲,高性能的數據下載操做......
環境的安裝(windows)
pip install wheel
下載twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
進入下載目錄,執行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl
pip3 install pywin32
pip3 install scrapy
建立一個工程 :scrapy startproject firstBlood
項目結構:
project_name/ scrapy.cfg: #項目的主配置信息。(真正爬蟲相關的配置信息在settings.py文件中) project_name/ __init__.py items.py #設置數據存儲模板,用於結構化數據,如:Django的Model pipelines.py #數據持久化處理 settings.py #配置文件,如:遞歸的層數、併發數,延遲下載等 spiders/ #爬蟲目錄,如:建立文件,編寫爬蟲解析規則 __init__.py
建立爬蟲應用程序:(必須在spider這個目錄下建立一個爬蟲文件)
cd proName
scrapy genspider spiderName www.xx.com
編寫爬蟲文件:在步驟2執行完畢後,會在項目的spiders中生成一個應用名的py爬蟲文件
import scrapy class FirstSpider(scrapy.Spider): #爬蟲文件的名稱:爬蟲文件的惟一標識(在spiders子目錄下是能夠建立多個爬蟲文件) name = 'first' #容許的域名 # allowed_domains = ['www.baidu.com'] #起始的url列表:列表中存放的url會被scrapy自動的進行請求發送 start_urls = ['https://www.baidu.com/','https://www.sogou.com/'] #用做於數據解析:將start_urls列表中對應的url請求成功後的響應數據進行解析 def parse(self, response): print(response.text) #獲取字符串類型的響應內容 print(response.body)#獲取字節類型的相應內容
設置修改settings.py配置文件相關配置
# settings.py 文件中 #不聽從robots協議 #進行UA假裝 #進行日誌等級設定: LOG_LEVEL = False
setting.py中 ----- 基於終端指令的持久化存儲操做
BOT_NAME = 'firstBlood' SPIDER_MODULES = ['firstBlood.spiders'] NEWSPIDER_MODULE = 'firstBlood.spiders' # Crawl responsibly by identifying yourself (and your website) on the user-agent USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36' #進行ua假裝 # Obey robots.txt rules ROBOTSTXT_OBEY = False #不聽從robotstx協議 LOG_LEVEL = 'ERROR' #輸出錯誤類型的日誌
setting.py ------------- 基於管道的持久化存儲
BOT_NAME = 'qiubaiPro' USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36' SPIDER_MODULES = ['qiubaiPro.spiders'] NEWSPIDER_MODULE = 'qiubaiPro.spiders' ITEM_PIPELINES = { 'qiubaiPro.pipelines.QiubaiproPipeline': 300, #300表示的是優先級(數值越小優先級越大) }
執行爬蟲程序:scripy crawl spiderName
不輸出日誌(錯誤信息會在日誌中輸出,不要使用)
scripy crawl spiderName --nolog
#持久化存儲 #基於終端指令 特性 : 只能將 parse 方法的返回值存儲到本地的磁盤文件中 指令 : scripy crawl spiderName -o filepath #基於管道 #實現流程 1.數據解析 2.在item類中定義相關屬性 3.將解析的數據封裝到一個 item 對象中(item文件中對應類的對象) 4.向管道提交item 5.在管道文件中的 process_item 方法中接收 item 進行持久化存儲 6.在配置文件中開啓管道 #管道中需注意細節: 1.配置文件中開啓的管道是一個字典,字典中的鍵值表示的就是某一個管道 2.在管道對應的源文件中其實能夠定義多個管道類,一種形式的持久化存儲 3.在process_item方法中的 return item 表示的是提交給下一個即將被執行的管道類 4.爬蟲文件中yield item 只能夠將item傳遞給第一個被執行的(優先級最高的)管道 #將同一份數據持久化存儲到不一樣平臺中: #分析 1.管道文件中的一個管道內負責數據的一種形式的持久化存儲
setting.py中 ----- 基於終端指令的持久化存儲操做
BOT_NAME = 'firstBlood' SPIDER_MODULES = ['firstBlood.spiders'] NEWSPIDER_MODULE = 'firstBlood.spiders' # Crawl responsibly by identifying yourself (and your website) on the user-agent USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36' #進行ua假裝 # Obey robots.txt rules ROBOTSTXT_OBEY = False #不聽從robotstx協議 LOG_LEVEL = 'ERROR' #輸出錯誤類型的日誌
setting.py ------------- 基於管道的持久化存儲
BOT_NAME = 'qiubaiPro' USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36' SPIDER_MODULES = ['qiubaiPro.spiders'] NEWSPIDER_MODULE = 'qiubaiPro.spiders' ITEM_PIPELINES = { 'qiubaiPro.pipelines.QiubaiproPipeline': 300, #300表示的是優先級(數值越小優先級越大) }
糗事百科
qiubai.py
import scrapy from qiubaiPro.items import QiubaiproItem class QiubaiSpider(scrapy.Spider): name = 'qiubai' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.qiushibaike.com/text/'] #基於終端指令的持久化存儲操做 # def parse(self, response): # div_list = response.xpath('//*[@id="content-left"]/div') # all_data = [] # for div in div_list: # #scrapy中的xpath返回的列表的列表元素必定是Selector對象,咱們最終想要的解析的 # #數據必定是存儲在該對象中 # #extract()將Selector對象中data參數的值取出 # # author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract() # author = div.xpath('./div[1]/a[2]/h2/text()').extract_first() # #列表直接調用extract表示的是將extract做用到每個列表元素中 # content = div.xpath('./a[1]/div/span//text()').extract() # content = ''.join(content) # dic = { # 'author':author, # 'content':content # } # all_data.append(dic) # return all_data #基於管道的持久化存儲 def parse(self, response): div_list = response.xpath('//*[@id="content-left"]/div') all_data = [] for div in div_list: #scrapy中的xpath返回的列表的列表元素必定是Selector對象,咱們最終想要的解析的 #數據必定是存儲在該對象中 #extract()將Selector對象中data參數的值取出 # author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract() author = div.xpath('./div[1]/a[2]/h2/text()').extract_first() #列表直接調用extract表示的是將extract做用到每個列表元素中 content = div.xpath('./a[1]/div/span//text()').extract() content = ''.join(content) #將解析的數據存儲到item對象 item = QiubaiproItem() item['author'] = author item['content'] = content #將item提交給管道 yield item #item必定是提交給了優先級最高的管道類
itims.py
import scrapy class QiubaiproItem(scrapy.Item): # define the fields for your item here like: author = scrapy.Field() #Field能夠將其理解成是一個萬能的數據類型 content = scrapy.Field()
pipelines.py 管道文件
import pymysql from redis import Redis class QiubaiproPipeline(object): fp = None def open_spider(self,spider): print('開始爬蟲......') self.fp = open('qiushibaike.txt','w',encoding='utf-8') #使用來接收爬蟲文件提交過來的item,而後將其進行任意形式的持久化存儲 #參數item:就是接收到的item對象 #該方法每接收一個item就會調用一次 def process_item(self, item, spider): author = item['author'] content= item['content'] self.fp.write(author+':'+content+'\n') return item #item是返回給了下一個即將被執行的管道類 def close_spider(self,spider): print('結束爬蟲!') self.fp.close() #負責將數據存儲到mysql class MysqlPL(object): conn = None cursor = None def open_spider(self,spider): self.conn = pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='123',db='spider',charset='utf8') print(self.conn) def process_item(self,item,spider): author = item['author'] content = item['content'] sql = 'insert into qiubai values ("%s","%s")'%(author,content) self.cursor = self.conn.cursor() try: self.cursor.execute(sql) self.conn.commit() except Exception as e: print(e) self.conn.rollback() return item def close_spider(self,spider): self.cursor.close() self.conn.close() #基於redis的管道存儲 class RedisPL(object): conn = None def open_spider(self,spider): self.conn = Redis(host='127.0.0.1',port=6379) print(self.conn) def process_item(self,item,spider): self.conn.lpush('all_data',item) # name value #注意:若是將字典寫入redis報錯:pip install -U redis==2.10.6
使用場景 :爬取多個頁碼對應的頁面源碼數據
yield scrapy.Request(url,callback)
import scrapy from qiubaiPro.items import QiubaiproItem class QiubaiSpider(scrapy.Spider): name = 'qiubai' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.qiushibaike.com/text/'] #將多個頁碼對應的頁面數據進行爬取和解析的操做 url = 'https://www.qiushibaike.com/text/page/%d/'#通用的url模板 pageNum = 1 #parse第一次調用表示的是用來解析第一頁對應頁面中的段子內容和做者 def parse(self, response): div_list = response.xpath('//*[@id="content-left"]/div') all_data = [] for div in div_list: # scrapy中的xpath返回的列表的列表元素必定是Selector對象,咱們最終想要的解析的 # 數據必定是存儲在該對象中 # extract()將Selector對象中data參數的值取出 # author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract() author = div.xpath('./div[1]/a[2]/h2/text()').extract_first() # 列表直接調用extract表示的是將extract做用到每個列表元素中 content = div.xpath('./a[1]/div/span//text()').extract() content = ''.join(content) # 將解析的數據存儲到item對象 item = QiubaiproItem() item['author'] = author item['content'] = content # 將item提交給管道 yield item # item必定是提交給了優先級最高的管道類 if self.pageNum <= 5: self.pageNum += 1 new_url = format(self.url%self.pageNum) #手動請求(get)的發送 yield scrapy.Request(new_url,callback=self.parse)
問題:在以前代碼中,咱們歷來沒有手動的對start_urls列表中存儲的起始url進行過請求的發送,可是起始url的確是進行了請求的發送,那這是如何實現的呢?
解答:實際上是由於爬蟲文件中的爬蟲類繼承到了Spider父類中的start_requests(self)這個方法,該方法就能夠對start_urls列表中的url發起請求:
def start_requests(self): for u in self.start_urls: yield scrapy.Request(url=u,callback=self.parse)
【注意】該方法默認的實現,是對起始的url發起get請求,若是想發起post請求,則須要子類重寫該方法。
方法: 重寫start_requests方法,讓其發起post請求:
def start_requests(self): #請求的url post_url = 'http://fanyi.baidu.com/sug' # post請求參數 formdata = { 'kw': 'wolf', } # 發送post請求 yield scrapy.FormRequest(url=post_url, formdata=formdata, callback=self.parse)
#引擎(Scrapy) 用來處理整個系統的數據流處理, 觸發事務(框架核心) #調度器(Scheduler) 用來接受引擎發過來的請求, 壓入隊列中, 並在引擎再次請求的時候返回. 能夠想像成一個URL(抓取網頁的網址或者說是連接)的優先隊列, 由它來決定下一個要抓取的網址是什麼, 同時去除重複的網址 #下載器(Downloader) 用於下載網頁內容, 並將網頁內容返回給蜘蛛(Scrapy下載器是創建在twisted這個高效的異步模型上的) #爬蟲(Spiders) 爬蟲是主要幹活的, 用於從特定的網頁中提取本身須要的信息, 即所謂的實體(Item)。用戶也能夠從中提取出連接,讓Scrapy繼續抓取下一個頁面 #項目管道(Pipeline) 負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體、驗證明體的有效性、清除不須要的信息。當頁面被爬蟲解析後,將被髮送到項目管道,並通過幾個特定的次序處理數據。
工做流程
#步驟 1.spider中的url被封裝成請求對象交給引擎(每個url對應一個請求對象); 2.引擎拿到請求對象以後, 將其所有交給調度器; 3.調度器拿到全部請求對象後, 經過內部的過濾器過濾掉重複的url, 最後將去重後的全部url對應的請求對象壓入到隊列中, 隨後調度器調度出其中一個請求對象, 並將其交給引擎; 4.引擎將調度器調度出的請求對象交給下載器; 5.下載器拿到該請求對象去互聯網中下載數據; 6.數據下載成功後會被封裝到response中, 隨後response會被交給下載器; 7.下載器將response交給引擎; 8.引擎將response交給spiders; 9.spiders拿到response後調用回調方法進行數據解析, 解析成功後產生item, 隨後spiders將item交給引擎; 10.引擎將item交給管道, 管道拿到item後進行數據的持久化存儲.
示例 :校花網圖片的爬取
項目的建立
scrapy startproject imgPro
cd imgPro
scrapy genspider img www.xxx.com
如何基於scrapy進行圖片的爬取
1.在爬蟲文件中只須要解析出圖片地址,而後將圖片地址提交給管道 2.配置文件中添加 IMAGES_STORE = './imasLib' 1.在管道文件中進行管道類的制定: from scrapy.pipelines.images import ImagesPipeline 將管道的父類修改爲 ImagesPipeline 重寫父類的三個方法
代碼:
img.py
import scrapy from imgPro.items import ImgproItem class ImgSpider(scrapy.Spider): name = 'img' # allowed_domains = ['www.x.com'] start_urls = ['http://www.521609.com/daxuemeinv/'] url = 'http://www.521609.com/daxuemeinv/list8%d.html' pageNum = 1 def parse(self, response): li_list = response.xpath('//*[@id="content"]/div[2]/div[2]/ul/li') for li in li_list: img_src = 'http://www.521609.com' + li.xpath('./a[1]/img/@src').extract_first() item = ImgproItem() item['src'] = img_src yield item if self.pageNum < 4: self.pageNum += 1 new_url = format(self.url%self.pageNum) yield scrapy.Request(new_url,callback=self.parse)
setting.py
##不聽從robotstx協議 ROBOTSTXT_OBEY = False #輸出錯誤類型的日誌 LOG_LEVEL = 'ERROR' # LOG_FILE = './log.txt' #圖片存儲地址 IMAGES_STORE = './imasLib' ITEM_PIPELINES = { 'imgPro.pipelines.ImgproPipeline': 300, }
pipelines.py
import scrapy from scrapy.pipelines.images import ImagesPipeline class ImgproPipeline(ImagesPipeline): #對某一個媒體資源進行請求發送 # item 就是接收到的spider提交的數據 def get_media_requests(self, item, info): yield scrapy.Request(item['src']) #指定媒體數據存儲的名稱 def file_path(self, request, response=None, info=None): #返回原始圖片名稱 name = request.url.split('/')[-1] print("正在下載:",name) return name #將 item 傳遞給下一個即將被執行的管道類 def item_completed(self, results, item, info): return item
items.py
import scrapy class ImgproItem(scrapy.Item): # define the fields for your item here like: src = scrapy.Field()
只須要將以下五個步驟配置在配置文件中便可:
#增長併發 默認scrapy開啓的併發線程爲32個,能夠適當進行增長。在settings配置文件中修改CONCURRENT_REQUESTS = 100值爲100,併發設置成了爲100。 #下降日誌級別: 在運行scrapy時,會有大量日誌信息的輸出,爲了減小CPU的使用率。能夠設置log輸出信息爲INFO或者ERROR便可。在配置文件中編寫:LOG_LEVEL = ‘INFO’ #禁止cookie: 若是不是真的須要cookie,則在scrapy爬取數據時能夠禁止cookie從而減小CPU的使用率,提高爬取效率。在配置文件中編寫:COOKIES_ENABLED = False #禁止重試: 對失敗的HTTP進行從新請求(重試)會減慢爬取速度,所以能夠禁止重試。在配置文件中編寫:RETRY_ENABLED = False #減小下載超時: 若是對一個很是慢的連接進行爬取,減小下載超時能夠能讓卡住的連接快速被放棄,從而提高效率。在配置文件中進行編寫:DOWNLOAD_TIMEOUT = 10 超時時間爲10s
實現深度爬取:爬取多個層級對應的頁面數據
使用場景:爬取的數據沒有在同一張頁面中
在手動請求的時候傳遞item:yield scrapy.Request(url,callback,meta={'item':item})
將meta這個字典傳遞給callback
在callback中接收meta:item = response.meta['item']
代碼:
movie.py
import scrapy from moviePro.items import MovieproItem class MovieSpider(scrapy.Spider): name = 'movie' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.4567tv.tv/index.php/vod/show/class/動做/id/5.html'] url = 'https://www.4567tv.tv/index.php/vod/show/class/動做/id/5/page/%d.html' pageNum = 1 def parse(self, response): li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li') for li in li_list: title = li.xpath('./div[1]/a/@title').extract_first() detail_url = "https://www.4567tv.tv" + li.xpath('./div[1]/a/@href').extract_first() item = MovieproItem() item['title'] = title # meta參數是一個字典,該參數能夠傳遞給callback指定的回調函數, yield scrapy.Request(detail_url,callback=self.parse_detail,meta={'item':item}) if self.pageNum < 5: self.pageNum += 1 new_url = format(self.url%self.pageNum) yield scrapy.Request(new_url,callback=self.parse) def parse_detail(self,response): #接收參數 response.meta item = response.meta['item'] #簡介 desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first() item['desc'] = desc yield item
setting.py
BOT_NAME = 'moviePro' SPIDER_MODULES = ['moviePro.spiders'] NEWSPIDER_MODULE = 'moviePro.spiders' # UA假裝 USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36' # Obey robots.txt rules ROBOTSTXT_OBEY = False #輸出錯誤類型的日誌 LOG_LEVEL = 'ERROR' ITEM_PIPELINES = { 'moviePro.pipelines.MovieproPipeline': 300, }
items.py
import scrapy class MovieproItem(scrapy.Item): # define the fields for your item here like: title = scrapy.Field() desc = scrapy.Field()
pipelines.py
class MovieproPipeline(object): def process_item(self, item, spider): print(item) return item
user_agent_list = [ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 " "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1", "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 " "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 " "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 " "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 " "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 " "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 " "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 " "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 " "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24" ]
PROXY_http = [ '153.180.102.104:80', '195.208.131.189:56055', ] PROXY_https = [ '120.83.49.90:9000', '95.189.112.214:35508', ]
做用 :批量攔截請求和響應
UA假裝 :將全部的請求儘量多的設定成不一樣的請求載體身份標識
代理操做
rom scrapy import signals import random user_agent_list = [ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 " "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1", "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 " "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 " "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 " "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 " "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 " "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 " "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 " "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 " "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24" ] PROXY_http = [ '153.180.102.104:80', '195.208.131.189:56055', ] PROXY_https = [ '120.83.49.90:9000', '95.189.112.214:35508', ] # 下載中間件 class MovieproDownloaderMiddleware(object): # 攔截正常的請求,參數 request 就是攔截到請求對象 def process_request(self, request, spider): print("i am process_request") #實現:將攔截到的請求儘量多的設定成不一樣的請求載體身份標識 request.headers['USER_AGENT'] = random.choice(user_agent_list) if request.url.split(":")[0] == "http": request.meta['proxy'] = 'http://' + random.choice(PROXY_http) else: request.meta['proxy'] = 'https://' + random.choice(PROXY_https) return None # 攔截響應,參數 request 就是攔截到響應 def process_response(self, request, response, spider): print("i am process_response") return response # 攔截髮生異常的請求 def process_exception(self, request, exception, spider): print("i am process_exception") #攔截到異常的請求,而後對其進行修正,而後從新進行請求發送 #代理操做 if request.url.split(":")[0] == "http": request.meta['proxy'] = 'http://' + random.choice(PROXY_http) else: request.meta['proxy'] = 'https://' + random.choice(PROXY_https) #將修正後的請求進行從新發送 return request
篡改響應數據或直接替換響應對象
需求 : 爬取網易新聞 國內,國際,軍事,航空,無人機這五個板塊下對應的新聞標題和內容
#分析: 1.每個板塊對應的新聞數據是動態加載出來的 # selenium在scrapy中的應用: 實例化瀏覽器對象:卸載爬蟲類的構造方法中
wangyi.py
import scrapy from selenium import webdriver from wangyiPro.items import WangyiproItem class WangyiSpider(scrapy.Spider): name = 'wangyi' # allowed_domains = ['www.xxx.com'] start_urls = ['https://news.163.com'] five_model_urls = [] #實例化一個瀏覽器對象 bro = webdriver.Chrome(executable_path=r'E:\飛秋\爬蟲+數據\tools\chromedriver.exe') #用來解析五個板塊對應的url,而後對齊進行手動請求發送 def parse(self, response): model_index = [3,4,6,7,8] li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li') for index in model_index: li = li_list[index] #獲取五個板塊對應的url model_url = li.xpath('./a/@href').extract_first() self.five_model_urls.append(model_url) #對每一個板塊的url進行手動請求發送 yield scrapy.Request(model_url,callback=self.parse_model) #用做與解析每一個板塊中的新聞標題和新聞詳情頁的url #問題:response(不知足需求的response)沒有包含每個板塊中動態加載的新聞數據 def parse_model(self,response): div_list = response.xpath('/html/body/div/div[3]/div[4]/div[1]/div/div/ul/li/div/div') for div in div_list: title = div.xpath('./div/div[1]/h3/a/text()').extract_first() detail_url = div.xpath('./div/div[1]/h3/a/@href').extract_first() item = WangyiproItem() item['title'] = title #對詳情頁發起請求解析出新聞內容 yield scrapy.Request(detail_url,callback=self.prase_new_detail,meta={'item':item}) def prase_new_detail(self,response): item = response.meta['item'] content = response.xpath('//*[@id="endText"]//text()').extract() content = ''.join(content) item['content'] = content yield item #最後執行 def closed(self,spider): self.bro.quit()
items.py
import scrapy class WangyiproItem(scrapy.Item): # define the fields for your item here like: title = scrapy.Field() content = scrapy.Field()
middlewares.py
from scrapy import signals from scrapy.http import HtmlResponse from time import sleep class WangyiproDownloaderMiddleware(object): def process_request(self, request, spider): return None # spider 就是爬蟲文件中爬蟲類實例化的對象 def process_response(self, request, response, spider): #進行全部響應對象的攔截 # 1.將全部響應對象中那五個不知足需求的響應對象找出 #每一個響應對象對應惟一一個請求對象 #若是咱們定位到五個響應對象的請求對象後,就能夠經過該請求對象定位到指定的響應對象 #能夠經過五個板塊的 url 定位請求對象 #總結: url ==> request ==> response # 2.將找出的五個不知足需求的響應對象進行修正(替換) # spider.five_model_urls :五個板塊對應的url bro = spider.bro if request.url in spider.five_model_urls: bro.get(request.url) sleep(1) #包含了動態加載的新聞數據 page_text = bro.page_source #若是if條件成立,則該response就是五個板塊對應的響應對象 new_response = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request) return new_response return response def process_exception(self, request, exception, spider): pass
pipelines.py
class WangyiproPipeline(object): def process_item(self, item, spider): print(item) return item
setting.py
BOT_NAME = 'wangyiPro' SPIDER_MODULES = ['wangyiPro.spiders'] NEWSPIDER_MODULE = 'wangyiPro.spiders' # UA假裝 USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36' ROBOTSTXT_OBEY = False LOG_LEVEL = 'ERROR' DOWNLOADER_MIDDLEWARES = { 'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543, } ITEM_PIPELINES = { 'wangyiPro.pipelines.WangyiproPpeline': 300, }
CrawSpider簡介
CrawSpider就是爬蟲類中 Spider的一個子類
除了繼承到Spider的特性和功能外,還派生除了其本身獨有的更增強大的特性和功能。其中最顯著的功能就是」LinkExtractors連接提取器「。Spider是全部爬蟲的基類,其設計原則只是爲了爬取start_url列表中網頁,而從爬取到的網頁中提取出的url進行繼續的爬取工做使用CrawlSpider更合適。
使用流程
1.建立一個基於CrawSpider的爬蟲文件 : scrapy startproject sunPro cd sunPro scrapy genspider -t crawl spiderName www.xxx.com 2.構造連接提取器和規則解析器 連接提取器 做用:可根據指定的規則進行指定連接的提取 提取規則:allow = '正則表達式' 規則解析器 做用:獲取連接提取器提取到的連接,而後進行請求發送,根據指定規則對請求到的頁面源碼數據進行數據解析 follow = 'True' :將連接提取器 繼續做用到 連接提取器所提取到的頁碼連接所對應的頁面中
數據連接地址 :http://wz.sun0769.com/index.php/question/questionType?type=4&page=
sun.py
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule #未實現深度爬取:爬取的只是每一個頁面對應的數據 class SunSpider(CrawlSpider): name = 'sun' # allowed_domains = ['www.xxx.com'] start_urls = ['http://wz.sun0769.com/index.php/question/questionType?type=4&page='] #連接提取器 type=4&page=\d+ link = LinkExtractor(allow=r'type=4&page=\d+') rules = ( #實例化一個Rule(規則解析器)的對象 Rule(link, callback='parse_item', follow=True), ) def parse_item(self, response): tr_list = response.xpath('//*[@id="morelist"]/div/table[2]//tr/td/table//tr') for tr in tr_list: title = tr.xpath('./td[2]/a[2]/@title').extract_first() status = tr.xpath('./td[3]/span/text()').extract_first() print(title,status)
setting.py
BOT_NAME = 'sunPro' SPIDER_MODULES = ['sunPro.spiders'] NEWSPIDER_MODULE = 'sunPro.spiders' # UA假裝 USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36' ROBOTSTXT_OBEY = False LOG_LEVEL = 'ERROR'
sun.py
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from sunPro.items import SunproItem,SunproItem_detail class SunSpider(CrawlSpider): name = 'sun' # allowed_domains = ['www.xxx.com'] start_urls = ['http://wz.sun0769.com/index.php/question/questionType?type=4&page='] #連接提取器 link = LinkExtractor(allow=r'type=4&page=\d+') #詳情頁url question/201909/426989.shtml link_detail = LinkExtractor(allow=r'question/\d+/\d+\.shtml') rules = ( #實例化一個Rule(規則解析器)的對象 Rule(link, callback='parse_item', follow=True), Rule(link_detail, callback='parse_datail'), ) def parse_item(self, response): tr_list = response.xpath('//*[@id="morelist"]/div/table[2]//tr/td/table//tr') for tr in tr_list: title = tr.xpath('./td[2]/a[2]/@title').extract_first() status = tr.xpath('./td[3]/span/text()').extract_first() num = tr.xpath('./td[1]/text()').extract_first() item = SunproItem() item['title'] = title item['status'] = status item['num'] = num if num: yield item def parse_datail(self,response): content = response.xpath('/html/body/div[9]/table[2]/tbody/tr[1]//text()').extract() content = ''.join(content) num = response.xpath('/html/body/div[9]/table[1]/tbody/tr/td[2]/span[2]/text()').extract_first() if num: num = num.split(':')[-1] item = SunproItem_detail() item['content'] = content item['num'] = num yield item
pipelines.py
class SunproPipeline(object): def process_item(self, item, spider): if item.__class__.__name__ == "SunproItem_detail": content = item['content'] num = item['num'] print(content,num) else: title = item['title'] status = item['status'] num = item['num'] print(title,status,num)
items.py
class SunproItem(scrapy.Item): title = scrapy.Field() status = scrapy.Field() num = scrapy.Field() class SunproItem_detail(scrapy.Item): content = scrapy.Field() num = scrapy.Field()
什麼是分佈式爬蟲?
基於多臺電腦組建一個分佈式機羣,而後讓機羣中的每一臺電腦執行同一組程序,而後讓它們對同一個網站的數據進行分佈爬取
爲何要用分佈式爬蟲?
提高爬取數據的效率
如何實現分佈式爬蟲?
基於scrapy + redis 的形式實現分佈式
scrapy 結合着 scrapy-redis組件實現分佈式
原生的scrapy沒法實現分佈式緣由?
調度器沒法被分佈式羣共享
管道沒法被共享
scrapy-redis組件的做用
提供能夠被共享的調度器和管道
環境安裝:
pip insatll redis
pip install scrapy-redis
#1.建立一個基於CrawSpider的爬蟲文件 : scrapy startproject sunPro cd sunPro scrapy genspider -t crawl spiderName www.xxx.com #2.修改當前的爬蟲文件 1.導包 : from scrapy_redis.spiders import RedisCrawlSpider 2.將當前爬蟲類的父類修改爲RedisCrawlSpider 3.將start_urls替換成redis_key = 'xxx' #表示的是可被共享調度器中隊列的名稱 4.編寫爬蟲類爬取數據的操做 #3.對setting進行操做 #指定管道 開啓可被共享的管道 : ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 } #指定可被共享的調度器 # 增長了一個去重容器類的配置, 做用使用Redis的set集合來存儲請求的指紋數據, 從而實現請求去重的持久化 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis組件本身的調度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 配置調度器是否要持久化, 也就是當爬蟲結束了, 要不要清空Redis中請求隊列和去重指紋的set。若是是True, 就表示要持久化存儲, 就不清空數據, 不然清空數據 SCHEDULER_PERSIST = True #指定redis服務 REDIS_HOST = 'redis服務的ip地址' REDIS_PORT = 6379 #4.對redis配置文件進行配置 (redis.windows.conf) 56行 : #bind 127.0.0.1 75行 : protected-mode yes --> protected-mode no #5.攜帶配置文件啓動redis服務 redis-server .\redis.windows.conf 地址: E:\飛秋\爬蟲+數據\tools\redis\Redis-x64-3.2.100 #6.啓動redis客戶端 : redis-cli #7.執行當前的工程 進入到爬蟲文件對應的目錄中:scrapy runspider xxx.py #8.向調度器隊列中仍入一個起始的url: 隊列在哪裏呢? 答:隊列在redis中 lpush fbsQueue www.xxx.com
代碼
fbs.py
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from scrapy_redis.spiders import RedisCrawlSpider from fbsPro.items import FbsproItem class FbsSpider(RedisCrawlSpider): name = 'fbs' # allowed_domains = ['www.xxx.com'] # start_urls = ['http://www.xxx.com/'] # redis_key表示的是可被共享調度器中隊列的名稱 redis_key = 'fbsQueue' rules = ( Rule(LinkExtractor(allow=r'type=4&page=\d+'), callback='parse_item', follow=True), ) def parse_item(self, response): tr_list = response.xpath('//*[@id="morelist"]/div/table[2]//tr/td/table//tr') for tr in tr_list: title = tr.xpath('./td[2]/a[2]/@title').extract_first() status = tr.xpath('./td[3]/span/text()').extract_first() item = FbsproItem() item['title'] = title item['status'] = status yield item
items.py
import scrapy class FbsproItem(scrapy.Item): title = scrapy.Field() status = scrapy.Field()
settings.py
BOT_NAME = 'fbsPro' SPIDER_MODULES = ['fbsPro.spiders'] NEWSPIDER_MODULE = 'fbsPro.spiders' ROBOTSTXT_OBEY = True #開啓可被共享的管道 ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 } #指定可被共享的調度器 # 增長了一個去重容器類的配置, 做用使用Redis的set集合來存儲請求的指紋數據, 從而實現請求去重的持久化 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis組件本身的調度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 配置調度器是否要持久化, 也就是當爬蟲結束了, 要不要清空Redis中請求隊列和去重指紋的set。若是是True, 就表示要持久化存儲, 就不清空數據, 不然清空數據 SCHEDULER_PERSIST = True #指定redis服務 REDIS_HOST = '192.168.11.50' REDIS_PORT = 6379