Python 領(lǐng)域運(yùn)用:網(wǎng)絡(luò)爬蟲
1. 爬蟲簡介
網(wǎng)絡(luò)爬蟲,又稱為網(wǎng)頁蜘蛛,是一種按照一定的規(guī)則、自動(dòng)地抓取萬維網(wǎng)信息的程序。爬蟲是一個(gè)自動(dòng)下載網(wǎng)頁的程序,它有選擇的訪問萬維網(wǎng)上的網(wǎng)頁與相關(guān)的鏈接,獲取所需要的信息。
爬蟲有著廣泛的應(yīng)用:
- 搜索引擎,谷歌百度等搜索引擎使用爬蟲抓取網(wǎng)站的頁面
- 輿情分析與數(shù)據(jù)挖掘,通過抓取微博排行榜的文章,掌握輿情動(dòng)向
- 數(shù)據(jù)聚合,比如企查查,抓取企業(yè)官網(wǎng)的詳細(xì)信息
- 導(dǎo)購、價(jià)格比對,通過抓取購物網(wǎng)站的商品頁面獲取商品價(jià)格,為買家提供價(jià)格參考
在面向計(jì)算機(jī)專業(yè)的人才招聘市場上,以爬蟲為關(guān)鍵字搜索,可以獲得大量的職位招聘:
2. 爬蟲的工作原理
爬蟲的工作原理如下:
- 從一個(gè)或若干初始網(wǎng)頁的 URL 開始
- 自動(dòng)下載初始網(wǎng)頁上的 HTML 文件
- 分析 HTML 文件中包含的鏈接,爬取鏈接指向的網(wǎng)頁
- 不斷重復(fù)以上過程,直到達(dá)到某個(gè)條件時(shí)停止
下面以爬取慕課網(wǎng)的 wiki 為例,說明爬蟲的工作原理:
-
排蟲程序選擇 http://idcbgp.cn/wiki 作為入口
-
下載網(wǎng)頁 http://idcbgp.cn/wiki 的內(nèi)容,大致如下:
<html data-n-head-ssr>
<head >
<title>慕課網(wǎng)教程丨Wiki寶典</title>
<meta name="description" content="慕課網(wǎng)wiki教程平臺">
</head>
<body>
<div>
<div class="text">
<a href="/wiki/Javascriptbase">JavaScript 入門教程</a>
<p><span>58小節(jié)</span>
</div>
<div class="text">
<a href="/wiki/typescriptlession">TypeScript 入門教程</a>
<p><span>38小節(jié)</span>
</div>
<div class="text">
<a href="/wiki/vuelession">Vue 入門教程</a>
<p><span>39小節(jié)</span>
</div>
</div>
</body>
</html>
- 分析 HTML 文件中的 a 標(biāo)簽,發(fā)現(xiàn)有如下 3 個(gè) a 標(biāo)簽
- <a href="/wiki/Javascriptbase">JavaScript 入門教程</a>
- <a href="/wiki/typescriptlession">TypeScript 入門教程</a>
- <a href="/wiki/vuelession">Vue 入門教程</a>
-
爬蟲爬取以上 3 個(gè) a 標(biāo)簽中的鏈接
-
不斷重復(fù)以上步驟,可以將慕課網(wǎng)的全部 wiki 文章抓取到本地
3. 爬蟲入門
3.1 基本的爬取技術(shù)
在互聯(lián)網(wǎng)早期,網(wǎng)站的內(nèi)容以靜態(tài)的 HTML 文件為主,不帶任何反爬蟲措施。比如,要爬取某個(gè)博客站點(diǎn)的全部文章,首先獲取網(wǎng)站的首頁,就順著首頁的鏈接爬到文章頁,再把文章的時(shí)間、作者、正文等信息保存下來。
使用 Python 的 requests 庫就可以爬取由靜態(tài)網(wǎng)頁構(gòu)成的網(wǎng)站:
- 使用 requests 庫下載指定 URL 的網(wǎng)頁
- 使用 XPath、BeautifulSoup 或者 PyQuery 對下載的 HTML 文件進(jìn)行解析
- 獲取 HTML 文件中特定的字段,例如文章的時(shí)間、標(biāo)題等信息,將它們保存
- 獲取 HTML 文件中包含的鏈接,并順著鏈接爬取內(nèi)容
- 爬取到數(shù)據(jù)后,可以使用 MySQL、MongoDB 等來保存數(shù)據(jù),實(shí)現(xiàn)持久化存儲,同時(shí)方便以后的查詢操作
3.2 爬取客戶端渲染的網(wǎng)頁
在互聯(lián)網(wǎng)早期,網(wǎng)站的內(nèi)容都是一些簡單的、靜態(tài)的頁面,服務(wù)器后端生成網(wǎng)頁內(nèi)容,然后返回給瀏覽器,瀏覽器獲取 html 文件之后就可以直接解析展示了,這種生成 HTML 文件的方式被稱為服務(wù)器端渲染。
而隨著前端頁面的復(fù)雜性提高,出現(xiàn)了基于 ajax 技術(shù)的前后端分離的開發(fā)模式,即后端不提供完整的 html 頁面,而是提供一些 api 返回 json 格式的數(shù)據(jù),前端調(diào)用后端的 API 獲取 json 數(shù)據(jù),在前端進(jìn)行 html 頁面的拼接,最后后展示在瀏覽器上,這種生成 HTML 文件的方式被稱為客戶端渲染。
簡單的使用 requests 庫無法爬取客戶端渲染的頁面:
- requests 爬下來的頁面內(nèi)容并不包含真正的數(shù)據(jù)
- 只能通過調(diào)用后端的 API 才能獲取頁面的數(shù)據(jù)
有兩種方式爬取客戶端渲染的網(wǎng)頁:
- 分析網(wǎng)頁的調(diào)用后端 API 的接口
這種方法需要分析網(wǎng)站的 JavaScript 邏輯,找到調(diào)用后端 API 的的代碼,分析 API 的相關(guān)參數(shù)。分析后再用爬蟲模擬模擬調(diào)用后端 API,從而獲取真正的數(shù)據(jù)。
很多情況下,后端 API 的接口接口帶著加密參數(shù),有可能花很長時(shí)間也無法破解,從而無法調(diào)用后端 API。
- 用模擬瀏覽器的方式來爬取數(shù)據(jù)
在無法解析后端 API 的調(diào)用方式的情況下,有一種簡單粗暴的方法:直接用模擬瀏覽器的方式來爬取,比如用 Selenium、Splash 等庫模擬瀏覽器瀏覽網(wǎng)頁,這樣爬取到的網(wǎng)頁內(nèi)容包含有真實(shí)的數(shù)據(jù)。這種方法繞過分析 JavaScript 代碼邏輯的過程,大大降低了難度。
3.3 爬蟲相關(guān)的庫和框架
- 實(shí)現(xiàn) HTTP 請求操作
- urllib:提供操作 URL 的功能
- requests:基于 urllib 的 HTTP 請求庫,發(fā)出一個(gè) HTTP 請求,等待服務(wù)器響應(yīng)的網(wǎng)頁
- selenium:自動(dòng)化測試工具,通過這個(gè)庫調(diào)用瀏覽器完成某些操作,比如輸入驗(yàn)證碼
- 從網(wǎng)頁中提取信息
- beautifulsoup:對 html 和 XML 進(jìn)行解析,從網(wǎng)頁中提取信息
- pyquery:jQuery 的 Python 實(shí)現(xiàn),能夠以 jQuery 的語法來操作解析 HTML 文檔
- lxml:對 html 和 XML 進(jìn)行解析,支持 XPath 解析方式
- tesserocr:一個(gè) OCR 庫,在遇到驗(yàn)證碼的時(shí)候,可使用該庫進(jìn)行識別
- 爬蟲框架
-
Scrapy:Scrapy 是用 python 實(shí)現(xiàn)的一個(gè)為了爬取網(wǎng)站數(shù)據(jù),提取結(jié)構(gòu)性數(shù)據(jù)而編寫的應(yīng)用框架。用這個(gè)框架可以輕松爬下來如亞馬遜商品信息之類的數(shù)據(jù)
-
PySpider: pyspider 是用 python 實(shí)現(xiàn)的功能強(qiáng)大的網(wǎng)絡(luò)爬蟲系統(tǒng),能在瀏覽器界面上進(jìn)行腳本的編寫和爬取結(jié)果的實(shí)時(shí)查看,后端使用常用的數(shù)據(jù)庫進(jìn)行爬取結(jié)果的存儲,還能定時(shí)設(shè)置任務(wù)與任務(wù)優(yōu)先級等
-
Newpaper: Newspaper可以用來提取新聞、文章和內(nèi)容分析
4. 提升爬蟲的效率
4.1 多任務(wù)爬蟲
爬蟲是 IO 密集型的任務(wù),大多數(shù)情況下,爬蟲都在等待網(wǎng)絡(luò)的響應(yīng)。如果使用用單線程的爬蟲來抓取數(shù)據(jù),爬蟲必須等待當(dāng)前頁面抓取完畢后,才能請求抓取下一個(gè)網(wǎng)頁。大型網(wǎng)站包含有數(shù)十萬的網(wǎng)頁,使用單線程抓取網(wǎng)頁的速度是無法接受的。
使用多進(jìn)程或者多線程的技術(shù),爬蟲可以同時(shí)請求多個(gè)網(wǎng)頁,可以成倍地提高爬取速度。
假設(shè)爬蟲需要獲取 baidu.com、taobao.com、qq.com 首頁,通過使用多線程技術(shù),可以并行的抓取網(wǎng)頁,代碼如下:
import requests
import threading
def fetch(url):
response = requests.get(url)
print('Get %s: %s' % (url, response))
t0 = threading.Thread(target = fetch, args = ("https://www.baidu.com/",))
t1 = threading.Thread(target = fetch, args = ("https://www.taobao.com/",))
t2 = threading.Thread(target = fetch, args = ("https://www.qq.com/",))
t0.start()
t1.start()
t2.start()
t0.join()
t1.join()
t2.join()
- 函數(shù) fetch,函數(shù) fetch 獲取指定 url 的網(wǎng)頁
- 創(chuàng)建 3 個(gè)線程
- 線程 t0 調(diào)用 fetch 獲取 baidu.com 首頁
- 線程 t1 調(diào)用 fetch 獲取 taobao.com 首頁
- 線程 t2 調(diào)用 fetch 獲取 qq.com 首頁
- 線程是并行執(zhí)行的,可以將抓取速度提高為原來的 3 倍
4.2 分布式爬蟲
多線程、多進(jìn)程能加速爬取速度,但終究還是單機(jī)的爬蟲,性能提升有限。要爬取超大規(guī)模的網(wǎng)站,需要使用分布式爬蟲。分布式爬蟲把爬蟲的關(guān)鍵功能部署到多臺機(jī)器上,多臺機(jī)器同時(shí)爬取數(shù)據(jù)。
下圖展示了一種典型的分布式爬蟲的架構(gòu):
- 分布式爬蟲的功能由 4 臺機(jī)器承擔(dān):1 臺 master 和 3 臺 slave
- 分布式爬蟲的關(guān)鍵是共享一個(gè)請求隊(duì)列,請求隊(duì)列保存了需要爬取的網(wǎng)頁的 URL 地址
- 維護(hù)該隊(duì)列的主機(jī)稱為 master
- 負(fù)責(zé)數(shù)據(jù)的抓取、數(shù)據(jù)處理和數(shù)據(jù)存儲的主機(jī)稱為 slave
- master 負(fù)責(zé)管理 slave 連接、任務(wù)調(diào)度與分發(fā)、結(jié)果回收并匯總等
- slave 從 master 那里領(lǐng)取任務(wù),并獨(dú)自完成任務(wù)最后上傳結(jié)果