標籤:3.5 self benchmark 解析 元素 make 查看 原理 入門
背景
部門(東方IC、圖蟲)業務驅動,需要搜集大量圖片資源,做資料分析,以及正版圖片維權。前期主要用node做爬蟲(業務比較簡單,對node比較熟悉)。隨著業務需求的變化,大規模爬蟲遇到各種問題。python爬蟲具有先天優勢,社區資源比較齊全,各種架構也完美支援。爬蟲效能也得到極大提升。本次分享從基礎知識入手,涉及python 的兩大爬蟲架構pyspider、scrapy,並基於scrapy、scrapy-redis 做了分布式爬蟲的介紹(直接粘貼的ppt)會涉及 redis、mongodb等相關知識。
對於反防盜鏈(自動登入、自動註冊... 以及常見策略)、代理、爬蟲快照、對象資源入TOS 未做過多介紹。這個雙月我們正在做爬蟲平台的可視化,可配置化,流程化。
一、前沿1.1 爬蟲是什嗎?
網路爬蟲(又被稱為網頁蜘蛛,網路機器人,在FOAF社區中間,更經常的稱為網頁追逐者),是一種按照一定的規則,自動的抓取全球資訊網資訊的程式或者指令碼。
1.2 為什麼是Python?
簡單易學:簡單到沒有學習過任何程式設計語言的人稍微看下資料就能編寫出爬蟲 解釋型程式設計語言:編寫完後可以直接執行,無須編譯
代碼重用性高:可以直接把包含某個功能的模組帶入其它程式中使用
跨平台性:幾乎所有的python程式都可以不加修改的運行在不同的作業系統
二、基礎知識2.1 Robots協議
Robots 協議也被稱作爬蟲協議、機器人協議,它的全名叫做網路爬蟲排除標準(Robots Exclusion Protocol),用來告訴爬蟲和搜尋引擎哪些頁面可以抓取,哪些不可以抓取。它通常是一個叫做 robots.txt 的文字檔,放在網站的根目錄下。
當搜尋爬蟲訪問一個網站時,它首先會檢查下這個網站根目錄下是否存在 robots.txt 檔案,如果存在,搜尋爬蟲會根據其中定義的爬取範圍來爬取。如果沒有找到這個檔案,那麼搜尋爬蟲便會訪問所有可直接存取的頁面。
2.2 URL 的含義
概念:
URL(協議(服務方式) + IP地址(包括連接埠號碼) + 具體地址),即統一資源定位器,也就是我們說的網址,統一資源定位器是對可以從互連網上得到的資源的位置和存取方法的一種簡潔的表示,是互連網上標準資源的地址。互連網上的每個檔案都有一個唯一的URL,它包含的資訊指出檔案的位置以及瀏覽器應該怎麼處理它。爬蟲爬取資料時必須要有一個目標的URL才可以擷取資料,因此,它是爬蟲擷取資料的基本依據。
相關:
URI = Universal Resource Identifier 統一資源標誌符
URL = Universal Resource Locator 統一資源定位器
URN = Universal Resource Name 統一資源名稱
image
2.3 瀏覽網頁的過程
在使用者瀏覽網頁的過程中,我們可能會看到許多好看的圖片,比如 http://image.baidu.com/ ,我們會看到幾張的圖片以及百度搜尋方塊,這個過程其實就是使用者輸入網址之後,經過 DNS 伺服器,找到伺服器主機,向伺服器發出一個請求,伺服器經過解析之後,發送給使用者的瀏覽器 HTML、JS、CSS 等檔案,瀏覽器解析出來,使用者便可以看到形形色色的圖片了,其實就是一次http請求的過程
2.4 代理基本原理
2.4.1基本原理
在本機和伺服器之間搭建了一個橋,此時本機不是直接向 Web 服務器發起請求,而是向Proxy 伺服器發出請求,這個過程 Web 服務器識別出的真實的 IP 就不再是我們原生 IP 了,就成功實現了 IP 偽裝,這就是代理的基本原理
2.4.2代理的作用
1、突破自身 IP 訪問限制,訪問一些平時不能訪問的網站
2、訪問一些單位或團體內部資源
3、隱藏真實 IP
2.4.3爬蟲代理
在爬取過程中可能遇到同一個 IP 訪問過於頻繁的問題,網站就會讓我們輸入驗證碼或登入或者直接封鎖 IP,這樣會給爬取帶來極大的不便。讓伺服器誤以為是Proxy 伺服器的在請求自己。這樣在爬取過程中通過不斷更換代理,就不會被封鎖,可以達到很好的爬取效果
2.4.4代理分類
FTP Proxy 伺服器、主要用於訪問 FTP 伺服器
HTTP Proxy 伺服器,主要用於訪問網頁
SSL/TLS 代理,主要用於訪問加密網站
2.4.5常見代理設定
免費代理
付費代理
image
三、爬蟲入門
image
3.1 常用爬蟲lib
請求庫:requests、selenium(自動化測試載入器)+ChromeDrive(chrome 磁碟機)、PhantomJS(無介面瀏覽器)
解析庫: LXML(html、xml、Xpath方式)、BeautifulSoup(html、xml)、PyQuery(支援css選取器)、Tesserocr(光學字元辨識,驗證碼)
資料庫: mongo、mysql、redis
存放庫: pymysql、pymongo、redispy、RedisDump(Redis 資料匯入匯出的工具)
web庫: Flask(輕量級的 Web 服務程式)、Django
其它工具: Charles(網路抓包工具)
3.2 一個入門栗子
image
image
3.3 複雜一點的栗子
問題:“防盜鏈”
防盜鏈,伺服器會識別 headers 中的 referer 是不是它自己,如果不是,有的伺服器不會響應,所以我們還可以在 headers 中加入 referer等資訊
反“防盜鏈”
1、完全類比瀏覽器的工作
2、構造cookie資訊
3、設定header資訊
4、Proxy 代理設定
其它策略
Timeout設定
3.4 動態渲染頁面抓取
Splash 是一個 JavaScript 渲染服務,是一個帶有 HTTP API 的輕量級瀏覽器,同時它對接了 Python 中的 Twisted 和 QT 庫,利用它我們同樣可以實現動態渲染頁面的抓取。
非同步方式處理多個網頁渲染過程
擷取渲染後的頁面的原始碼或
通過關閉圖片渲染或者使用 Adblock 規則來加快頁面渲染速度
可執行特定的 JavaScript 指令碼
可通過 Lua 指令碼來控制頁面渲染過程
擷取渲染的詳細過程並通過 HAR(HTTP Archive)格式呈現
3.5 爬蟲完整流程
image
四、爬蟲架構4.1 PySpider簡介
一個國人編寫的強大的網路爬蟲系統並帶有強大的WebUI。採用Python語言編寫,分布式架構,支援多種資料庫後端,強大的WebUI支援指令碼編輯器,任務監視器,專案管理器以及結果查看器
image
4.2 PySpider特性
1、python 指令碼控制,可以用任何你喜歡的html解析包(內建 pyquery)
2、WEB 介面編寫調試指令碼,起停指令碼,監控執行狀態,查看活動曆史,擷取結果產出
3、資料存放區支援MySQL, MongoDB, Redis, SQLite, Elasticsearch; PostgreSQL 及 SQLAlchemy
4、佇列服務支援RabbitMQ, Beanstalk, Redis 和 Kombu
5、支援抓取 JavaScript 的頁面
6、組件可替換,支援單機/分布式部署,支援 Docker 部署
7、強大的調度控制,支援逾時重爬及優先順序設定
8、支援python2&3
image
image
image
4.3 Scrapy簡介
Scrapy是一個為了爬取網站資料,提取結構性資料而編寫的應用程式框架。 可以應用在包括資料採礦,資訊處理或儲存曆史資料等一系列的程式中。
image
4.4 Scrapy運行流程
1、調度器(Scheduler)從待下載連結中取出一個連結(URL)
2、調度器啟動採集模組Spiders模組
3、採集模組把URL傳給下載器(Downloader),下載器把資源下載下來
4、提取目標資料,抽取出目標對象(Item),則交給實體管道(item pipeline)進行進一步的處理;比如存入資料庫、文本
5、若是解析出的是連結(URL),則把URL插入到待爬取隊列當中
五、scrapy架構5.1 Scrapy基本使用
建立項目:scrapy startproject tutorial
建立spider:scrapy genspider quotes quotes.toscrapy.com
運行項目:scrapy crawl dmoz
互動調試:scrapy shell quotes.toscrape.com
儲存資料(多種格式):scrapy crawl quotes -o quoqes.json
image
5.2 scrapy全域指令
startproject:建立項目
genspider:建立爬蟲
settings:擷取Scrapy的設定
runspider:在未建立項目的情況下,運行一個編寫在Python檔案中的spider
shell:以給定的URL(如果給出)或者空(沒有給出URL)啟動Scrapy shell
fetch:使用Scrapy下載器(downloader)下載給定的URL,並將擷取到的內容送到標準輸出
view:在瀏覽器中開啟給定的URL,並以Scrapy spider擷取到的形式展現
Version:輸出Scrapy版本
5.3 scrapy項目指令
crawl:使用spider進行爬取
check:檢查項目是否有錯
list: 列出當前項目中所有可用的spider,每行輸出一個spider
edit:僅僅是提供一個捷徑。開發人員可以自由選擇其他工具或者IDE來編寫調試spiderparse
parse:擷取給定的URL並使用相應的spider分析處理
bench:運行benchmark測試
5.4 scrapy選取器
BeautifulSoup 是在程式員間非常流行的網頁分析庫,它基於HTML代碼的結構來構造一個Python對象, 對不良標記的處理也非常合理,但它有一個缺點:慢。
lxml 是一個基於 ElementTree (不是Python標準庫的一部分)的python化的XML解析庫(也可以解析HTML)。
Scrapy提取資料有自己的一套機制。它們被稱作選取器(seletors),因為他們通過特定的 XPath 或者 CSS 運算式來“選擇” HTML檔案中的某個部分。
XPath 是一門用來在XML檔案中選擇節點的語言,也可以用在HTML上。
CSS 是一門將HTML文檔樣式化的語言。選取器由它定義,並與特定的HTML元素的樣式相關連。
Scrapy選取器構建於 lxml 庫之上,這意味著它們在速度和解析準確性上非常相似。
image
5.5 spiders
Spider類定義了如何爬取某個(或某些)網站。包括了爬取的動作(例如:是否跟進連結)以及如何從網頁的內容中提取結構化資料(爬取item)。 換句話說,Spider就是您定義爬取的動作及分析某個網頁(或者是有些網頁)的地方。
以初始的URL初始化Request,並設定回呼函數。 當該request下載完畢並返回時,將產生response,並作為參數傳給該回呼函數。
spider中初始的request是通過調用 startrequests() 來擷取的。 startrequests() 讀取 start_urls 中的URL, 並以 parse 為回呼函數產生 Request 。
在回呼函數內分析返回的(網頁)內容,返回 Item 對象、dict、 Request 或者一個包括三者的可迭代容器。 返回的Request對象之後會經過Scrapy處理,下載相應的內容,並調用設定的callback函數(函數可相同)。
在回呼函數內,您可以使用 選取器(Selectors) (您也可以使用BeautifulSoup, lxml 或者您想用的任何解析器) 來分析網頁內容,並根據分析的資料產生item。
最後,由spider返回的item將被存到資料庫(由某些 Item Pipeline 處理)或使用 Feed exports 存入到檔案中。
屬性
name: 定義spider名字的字串(string)
allowed_domains: 包含了 spider 允許爬取的網域名稱(domain)列表(list)
start_urls: URL列表。當沒有制定特定的URL時,spider將從該列表中開始進行爬取
custom_settings: 該設定是一個dict.當啟動spider時,該設定將會覆蓋項目級的設定. 由於設定必須在初始化(instantiation)前被更新,所以該屬性 必須定義為class屬性
crawler: 該屬性在初始化class後,由類方法 from_crawler() 設定, 並且連結了本spider執行個體對應的 Crawler 對象
settings: crawler 的組態管理員,擴充(extensions)和中介軟體(middlewares)使用它用來訪問 Scrapy 的配置
logger: self.logger.info(‘日誌:%s‘, response.status)
方法
from_crawler: 如果存在,則調用此類方法以從 Crawler 建立 pipeline 執行個體。它必須返回一個新的pipeline執行個體。 Crawler 對象提供對所有 Scrapy 核心組件(如settings 和 signals)的訪問; 它是 pipeline 訪問它們並將其功能掛鈎到Scrapy中的一種方法
start_requests: 該方法必須返回一個可迭代對象(iterable)。該對象包含了spider用於爬取的第一個Request
make_requests_from_url: 該方法接受一個URL並返回用於爬取的 Request 對象
parse: 當response沒有指定回呼函數時,該方法是Scrapy處理下載的response的預設方法
log: 使用 scrapy.log.msg() 方法記錄(log)message
closed: 當spider關閉時,該函數被調用
5.6 Item Pipeline
當 Item 在 Spider 中被收集之後,它將會被傳遞到Item Pipeline,一些組件會按照一定的順序執行對Item的處理。每個item pipeline組件(有時稱之為“Item Pipeline”)是實現了簡單方法的Python類。他們接收到Item並通過它執行一些行為,同時也決定此Item是否繼續通過pipeline,或是被丟棄而不再進行處理。
以下是item pipeline的一些典型應用:
1、清理HTML資料
2、驗證爬取的資料(檢查item包含某些欄位)
3、查重(並丟棄)
4、將爬取結果儲存到資料庫中
方法
process_item(self, item, spider): 每個item pipeline組件都需要調用該方法,這個方法必須返回一個具有資料的dict,或是 Item (或任何繼承類)對象, 或是拋出 DropItem 異常,被丟棄的item將不會被之後的pipeline組件所處理。
open_spider: 當spider被開啟時,這個方法被調用。
close_spider: 當spider被關閉時,這個方法被調用
from_crawler: 擷取setting 配置資訊
例子:資料持久化到mongo
image
5.7 Downloader Middleware
下載器中介軟體是介於 Scrapy 的 request/response 處理的鉤子架構。 是用於全域修改Scrapy request 和 response 的一個輕量、底層的系統。
啟用
要啟用下載器中介軟體組件,將其加入到DOWNLOADER_MIDDLEWARES 設定中。 該設定是一個字典(dict),鍵為中介軟體類的路徑,值為其中介軟體的順序(order)。
方法
1、process_request(request, spider): 當每個request通過下載中介軟體時,該方法被調用
2、processresponse(request, response, spider): processrequest() 必須返回以下之一: 返回一個 Response 對象、 返回一個 Request 對象或raise一個 IgnoreRequest 異常。如果其返回一個 Response (可以與傳入的response相同,也可以是全新的對象), 該response會被在鏈中的其他中介軟體的 process_response() 方法處理。
3、processexception(request, exception, spider): 當下載處理器(download handler)或 processrequest() (下載中介軟體)拋出異常(包括 IgnoreRequest 異常)時, Scrapy調用 process_exception()
例子:添加代理
image
六、scrapy 搭建項目6.1 爬蟲思路
image
6.2 實際項目分析
image
起始頁
image
項目結構
image
七、分布式爬蟲7.1 單主機爬蟲架構
本機維護一個爬蟲隊列,scheduler 進行調度
Q:多台主機協作的關鍵是什嗎?
A:共用爬蟲隊列
image
單主機爬蟲架構
image
7.2 分布式爬蟲架構
image
分布式爬蟲架構
image
7.3 問題
Q1:隊列怎麼選?
A1: Redis隊列
Redis 非關係型資料庫,key-value形式儲存,結構靈活
是記憶體中的資料結構儲存系統,處理速度快,效能好
提供隊列、集合等多種儲存結構,方便隊列維護
Q2:怎麼去重?
A2:Redis集合
Redis 提供集合資料結構,在redis集合中儲存每個request的指紋
在向redis集合中儲存request指紋的時候,先驗證指紋是否存在?
[如果存在]:則不添加request到隊列
[不存在]:則將request排入佇列,並將指紋加入集合
Q3:怎樣防止中斷?
A3:啟動判斷
在每台從機 scrapy 啟動時 都會首先判斷當前 redis reqeust隊列是否為空白
[不為空白]:從隊列中取得下一個 request ,進行執行爬蟲
[空]:重新開始爬取,第一台從機執行爬取向隊列中添加request
Q4:怎樣實現該架構?
A4:Scrapy-Redis
scrapy是Python的一個非常好用的爬蟲架構,功能非常強大,但是當我們要爬取的頁面非常多的時候,單個主機的處理能力就不能滿足我們的需求了(無論是處理速度還是網路請求的並發數),這時候分布式爬蟲的優勢就顯現出來,人多力量大。而scrapy-Redis就是結合了分散式資料庫redis,重寫了scrapy一些比較關鍵的代碼(scrapy的調度器、隊列等組件),將scrapy變成一個可以在多個主機上同時啟動並執行分布式爬蟲。
github地址: https://github.com/rmax/scrapy-redis
7.4 源碼解讀
閱讀源碼前:需要瞭解 scrapy 的運行原理,否則並沒什麼用。
scrapy-redis 工程的主體還是 redis 和 scrapy 兩個庫,將兩個庫的核心功能結合,實現分布式。
image
1 connection.py
負責根據setting中配置執行個體化redis串連。被dupefilter和scheduler調用,總之涉及到redis存取的都要使用到這個模組
image
2 dupefilter.py
通過繼承 BaseDupeFilter 重寫他的方法,實現了基於redis的 request 判重。scrapy-redis使用 redis的一個 set 中插入 fingerprint(不同spider的key不同)
spider名字+DupeFilter的key就是為了在不同主機上的不同爬蟲執行個體,只要屬於同一種spider,就會訪問到同一個set,而這個set就是他們的url判重池 。
DupeFilter 判重會在 scheduler 類中用到,每一個request在進入調度之前都要進行判重,如果重複就不需要參加調度,直接捨棄就好了
image
3 picklecompat.py
loads 和 dumps 兩個函數,其實就是實現了一個 serializer,因為 redis 資料庫不能儲存複雜物件(value部分只能是字串,字串列表,字串集合和hash,key部分只能是字串),所以儲存前需要先序列化成文本才行
這個 serializer 主要用於 scheduler 存 reuqest 對象。
為什麼不用json格式?(item pipeline 的序列化預設用的就是 json)
image
4 pipelines.py
pipeline 檔案實現了一個 item pipieline 類,和 scrapy 的 item pipeline 是同一個對象,從settings中擷取我們配置的REDISITEMSKEY作為key,把item序列化之後存入redis資料庫對應的value中(這個value是list,我們的每個item是這個list中的一個結點),這個pipeline把提取出的item存起來,主要是為了方便我們延後處理資料。
image
5 queue.py
這裡實現了三種方式的 Queue
SpiderQueue(隊列):先進先出
SpiderStack(棧):先進後出
SpiderPriorityQueue(優先順序隊列)
這些容器類都會作為scheduler調度request的容器,scheduler在每個主機上都會執行個體化一個,並且和spider一一對應,所以分布式運行時會有一個spider的多個執行個體和一個scheduler的多個執行個體存在於不同的主機上,但是,因為scheduler都是用相同的容器,而這些容器都串連同一個redis伺服器,又都使用spider名加queue來作為key讀寫資料,所以不同主機上的不同爬蟲執行個體公用一個request調度池,實現了分布式爬蟲之間的統一調度。
image
6 scheduler.py
重寫了scheduler類,代替scrapy.core.scheduler 的原有調度器,對原有調度器的邏輯沒有很大的改變,主要是使用了redis作為資料存放區的媒介,以達到各個爬蟲之間的統一調度。scheduler負責調度各個spider的request請求,scheduler初始化時,通過settings檔案讀取queue和dupefilters的類型,配置queue和dupefilters使用的key。每當一個request要被調度時,enqueuerequest被調用,scheduler使用dupefilters來判斷這個url是否重複,如果不重複,就添加到queue的容器中(可以在settings中配置)。當調度完成時,nextrequest被調用,scheduler就通過queue容器的介面,取出一個request,把他發送給相應的spider,讓spider進行爬取工作。
python學習交流群:125240963
7 spider.py
設計的這個spider從redis中讀取要爬的url,然後執行爬取,若爬取過程中返回更多的url,那麼繼續進行直至所有的request完成。之後繼續從redis中讀取url,迴圈這個過程。
分析:在這個spider中通過connect signals.spideridle訊號實現對crawler狀態的監視。當idle時,返回新的makerequestsfromurl(url)給引擎,進而交給調度器調度。
轉載:https://www.jianshu.com/p/ec3dfaec3c9b?utm_source=tuicool&utm_medium=referral
python3 分布式爬蟲