Python網(wǎng)絡(luò)數(shù)據(jù)采集_第1頁
Python網(wǎng)絡(luò)數(shù)據(jù)采集_第2頁
Python網(wǎng)絡(luò)數(shù)據(jù)采集_第3頁
Python網(wǎng)絡(luò)數(shù)據(jù)采集_第4頁
Python網(wǎng)絡(luò)數(shù)據(jù)采集_第5頁
已閱讀5頁,還剩275頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

Python

網(wǎng)絡(luò)數(shù)據(jù)采集

目錄

第一部分創(chuàng)建爬蟲

第1章初見網(wǎng)絡(luò)爬蟲

1.1網(wǎng)絡(luò)連接

1.2BeautifulSoup簡介

1.2.1安裝BeautifulSoup

122運行BeautifulSoup

123可靠的網(wǎng)絡(luò)連接

第2章復(fù)雜HTML解析

2.1不是一直都要用錘子

2.2再端——碗BeautifulSoup

2.2.1BeautifulSoup的find口和findAll。

222其他BeautifulSoup對象

2.2.3導(dǎo)航樹

2.3正則表達式

2.4正貝U表達式和BeautifulSoup

2.5獲取屬性

2.6Lambda表達式

2.7超越BeautifulSoup

第3章開始采集

3.1遍歷單個域名

3.2采集整個網(wǎng)站

收集整個網(wǎng)站數(shù)據(jù)

3.3通過互聯(lián)網(wǎng)采集

3.4用ScraDV采集

第4章使用API

4.1API概述

4.2API通用規(guī)則

421方法

4.2.2驗證

4.3服務(wù)器響應(yīng)

API調(diào)用

4.4EchoNest

幾個示例

4.5TwitterAPI

4.5.1開始

4.5.2幾個示例

4.6GoogleAPI

4.6.1開始

4.6.2幾個示例

4.7解析ISON數(shù)據(jù)

4.8M到主題

4.9再說一點API

第5章存儲數(shù)據(jù)

5.1媒體文件

5.2把數(shù)據(jù)存儲到CSV

5.3MySQL

5.3.1安裝MySOL

5.3.2基本命令

5.3.3與Python整合

534數(shù)據(jù)庫技術(shù)與最佳實踐

5.3.5MySQL里的“六度空間游戲),

5.4Email

第6章讀取文檔

6.1文檔編碼

6.2純文本

文本編碼和全球互聯(lián)網(wǎng)

6.3CSV

讀取CSV文件

6.4PDF

6.5微軟Word和.docx

第二部分高級數(shù)據(jù)采集

第7章數(shù)據(jù)清洗

7.1編寫代碼清洗數(shù)據(jù)

數(shù)據(jù)標準化

7.2數(shù)據(jù)存儲后再清洗

OpenRefine

第8章自然語言處理

8.1概括數(shù)據(jù)

8.2馬爾可夫模型

維基白科六度分割:終結(jié)篇

8.3自然語言工具包

8.3.1安裝與設(shè)置

8.3.2用NLTK做統(tǒng)計分析

8.3.3用NLTK做詞性分析

8.4其他資源

第9章穿越網(wǎng)頁表單與登錄窗口進行采集

9.1PythonRequests庫

9.2提交一個基本表單

9.3單選按鈕、復(fù)選框和其他輸入

9.4提交文件和圖像

9.5處理登錄和cookie

HTTP基本接入認證

9.6其他表單問題

第10章采集JavaScript

10.1lavaScript簡介

常用lavaScriDt庫

10.2Ajax和動態(tài)HTML

在Python中用Selenium執(zhí)行l(wèi)avaScript

10.3處理重定向

第11章圖像識別與文字處理

11.1OCR庫概述

11.1.1Pillow

11.1.2Tesseract

11.1.3NumPy

11.2處理格式規(guī)范的文字

從網(wǎng)站圖片中抓取文字

1L3讀取驗證碼與訓(xùn)I練Tesseract

訓(xùn)練Tesseract

11.4獲取驗證碼提交答案

第12章避開采集陷阱

12.1道德規(guī)范

12.2讓網(wǎng)絡(luò)機器人看起來像人類用戶

1221修改請求頭

12.2.2處壬里cookie

12.2.3時間就是一切

12.3常見表單安全措施

12.3.1隱含輸入字段值

12.3.2避免蜜罐

12.4問題檢查表

第13章用爬蟲測試網(wǎng)站

13.1測試簡介

什么是單元測試

13.2Pvthon單元測試

測試維基百科

13.3Selenium單元測試

與網(wǎng)站進行交互

134Python單元測試與Selenium單元測試的選擇

第14章遠程采集

14.1為什么要用遠程服務(wù)器

14.1.1避免IP地址被封殺

14.1.2移植性與擴展性

14.2Tor代理服務(wù)器

PySocks

14.3遠程主機

14.3.1從網(wǎng)站主機運行

1432從云主機運行

14.4其他資源

14.5勇往直前

附錄APython簡介

安裝與"Hello.World!”

附錄B互聯(lián)網(wǎng)簡介

附錄C網(wǎng)絡(luò)數(shù)據(jù)采集的法律與道德約束

C.1商標、版權(quán)、專利

版權(quán)法

C.2侵犯動產(chǎn)

C.3計算機欺詐與濫用法

C.4robots.txt和服務(wù)協(xié)議

C.5三個網(wǎng)絡(luò)爬蟲

C.5.1eBay起訴Bidder'sEdge與侵犯動產(chǎn)

C.5.2美國政府起訴Auernheimer與《計算機欺詐與濫用法》

C.5.3Field起訴Google:版權(quán)和robots.txt

第一部分創(chuàng)建爬蟲

這部分內(nèi)容重點介紹網(wǎng)絡(luò)數(shù)據(jù)采集的基本原理:如何用Python從網(wǎng)絡(luò)服務(wù)器請求信息,

如何對服務(wù)器的響應(yīng)進行基本處理,以及如何以自動化手段與網(wǎng)站進行交互。最終,你將

輕松游弋于網(wǎng)絡(luò)空間,創(chuàng)建出具有域名切換、信息收集以及信息存儲功能的爬蟲。

說實話,如果你想以較少的預(yù)先投入獲取較高的回報,網(wǎng)絡(luò)數(shù)據(jù)采集肯定是一個值得踏入

的神奇領(lǐng)域。大體上,你遇到的90%的網(wǎng)絡(luò)數(shù)據(jù)采集項目使用的都是接下來的六章里介

紹的技術(shù)。這部分內(nèi)容涵蓋了一般人(也包括技術(shù)達人)在思考“網(wǎng)絡(luò)爬蟲”時通常的想法

?通過網(wǎng)站域名獲取HTML數(shù)據(jù)

?根據(jù)目標信息解析數(shù)據(jù)

?存儲目標信息

?如果有必要,移動到另一個網(wǎng)頁重復(fù)這個過程

這將為你學(xué)習(xí)本書第二部分中更復(fù)雜的項目奠定堅實的基礎(chǔ)。不要天真地認為這部分內(nèi)容

沒有第二部分里的一些比較高級的項目重要。其實,當(dāng)你寫自己的網(wǎng)絡(luò)爬蟲時,幾乎每天

都要用到第一部分的所有內(nèi)容。

第1章初見網(wǎng)絡(luò)爬蟲

一旦你開始采集網(wǎng)絡(luò)數(shù)據(jù),就會感受到瀏覽器為我們做的所有細節(jié)。網(wǎng)絡(luò)上如果沒有

HTML文本格式層、CSS樣式層、JavaScript執(zhí)行層和圖像渲染層,乍看起來會有點兒嚇

人,但是在這一章和下一章,我們將介紹如何不通過瀏覽器的幫助來格式化和理解數(shù)據(jù)。

本章將首先向網(wǎng)絡(luò)服務(wù)器發(fā)送GET請求以獲取具體網(wǎng)頁,再從網(wǎng)頁中讀取HTML內(nèi)容,

最后做一些簡單的信息提取,將我們要尋找的內(nèi)容分離出來。

1.1網(wǎng)絡(luò)連接

如果你沒在網(wǎng)絡(luò)或網(wǎng)絡(luò)安全上花過太多時間,那么互聯(lián)網(wǎng)的原理可能看起來有點兒神秘。

準確地說,每當(dāng)打開瀏覽器連接http:〃的時候,我們不會思考網(wǎng)絡(luò)正在做什

么,而且如今也不必思考。實際上,我認為很神奇的是,計算機接口已經(jīng)如此先進,讓大

多數(shù)人上網(wǎng)的時候完全不思考網(wǎng)絡(luò)是如何工作的。

但是,網(wǎng)絡(luò)數(shù)據(jù)采集需要拋開一些接口的遮擋,不僅是在瀏覽器層(它如何解釋所有的

HTML、CSS和JavaScript),有時也包括網(wǎng)絡(luò)連接層。

我們通過下面的例子讓你對瀏覽器獲取信息的過程有一個基本的認識。Alice有一臺網(wǎng)絡(luò)

服務(wù)器。Bob有一個臺式機正準備連接Alice的服務(wù)器。當(dāng)一臺機器想與另一臺機器對話

時,下面的某個行為將會發(fā)生。

1.Bob的電腦發(fā)送一串1和0比特值,表示電路上的高低電壓。這些比特構(gòu)成了一種信息,

包括請求頭和消息體。請求頭包含當(dāng)前Bob的本地路由器MAC地址和Alice的IP地址。

消息體包含Bob對Alice服務(wù)器應(yīng)用的請求。

2.Bob的本地路由器收到所有1和0比特值,把它們理解成一個數(shù)據(jù)包(packet),從

Bob自己的MAC地址"寄到"Alice的IP地址。他的路由器把數(shù)據(jù)包"蓋上"自己的IP地址作

為“發(fā)件”地址,然后通過互聯(lián)網(wǎng)發(fā)出去。

3.Bob的數(shù)據(jù)包游歷了一些中介服務(wù)器,沿著正確的物理/電路路徑前進,到了Alice的

服務(wù)器。

4.Alice的服務(wù)器在她的IP地址收到了數(shù)據(jù)包。

5.Alice的服務(wù)器讀取數(shù)據(jù)包請求頭里的目標端口(通常是網(wǎng)絡(luò)應(yīng)用的80端口,可以理解

成數(shù)據(jù)包的“房間號",IP地址就是“街道地址”),然后把它傳遞到對應(yīng)的應(yīng)用一一網(wǎng)絡(luò)服

務(wù)器應(yīng)用上。

6.網(wǎng)絡(luò)服務(wù)器應(yīng)用從服務(wù)器處理器收到一串?dāng)?shù)據(jù),數(shù)據(jù)是這樣的:

?這是一個GET請求

?請求文件index.html

7.網(wǎng)絡(luò)服務(wù)器應(yīng)用找到對應(yīng)的HTML文件,把它打包成一個新的數(shù)據(jù)包發(fā)送給Bob,然后

通過它的本地路由器發(fā)出去,用同樣的過程回傳到Bob的機器上。

瞧!我們就這樣實現(xiàn)了互聯(lián)網(wǎng)。

那么,在這場數(shù)據(jù)交換中,網(wǎng)絡(luò)瀏覽器從哪里開始參與的?完全沒有參與。其實,在互聯(lián)

網(wǎng)的歷史中,瀏覽器是一個比較年輕的發(fā)明,始于1990年的Nexus瀏覽器。

的確,網(wǎng)絡(luò)瀏覽器是一個非常有用的應(yīng)用,它創(chuàng)建信息的數(shù)據(jù)包,發(fā)送它們,然后把你獲

取的數(shù)據(jù)解釋成漂亮的圖像、聲音、視頻和文字。但是,網(wǎng)絡(luò)瀏覽器就是代碼,而代碼是

可以分解的,可以分解成許多基本組件,可重寫、重用,以及做成我們想要的任何東西。

網(wǎng)絡(luò)瀏覽器可以讓服務(wù)器發(fā)送一些數(shù)據(jù),到那些對接無線(或有線)網(wǎng)絡(luò)接口的應(yīng)用上,

但是許多語言也都有實現(xiàn)這些功能的庫文件。

讓我們看看Python是如何實現(xiàn)的:

fromurllib.request

html

你可以把這段代碼保存為scrapetestpy,然后在終端里運行如下命令:

scrapetest.py

注意,如果你的設(shè)備上安裝了Python2.x,可能需要直接指明版本才能運行Python3.x代

碼:

fpython

這將會輸出http:〃/pages/page/.html這個網(wǎng)頁的全部HTML代碼。

更準確地說,這會輸出在域名為http:〃的服務(wù)器上<網(wǎng)絡(luò)應(yīng)用根地

址>/pages文件夾里的HTML文件pagel.html的源代碼。

有什么區(qū)別?現(xiàn)在大多數(shù)網(wǎng)頁需要加載許多相關(guān)的資源文件??赡苁菆D像文件、

JavaScript文件、CSS文件,或你需要連接的其他各種網(wǎng)頁內(nèi)容。當(dāng)網(wǎng)絡(luò)瀏覽器遇到一個

標簽時,比如src="cb(t&Kittei^.jpg">,會向服務(wù)器發(fā)起另一個請求,以獲取

cuteKittemjpg文件中的數(shù)據(jù)為用戶充分渲染網(wǎng)頁。但是,我們的Python程序沒有返回并

向服務(wù)器請求多個文件的邏輯,它只能讀取我們已經(jīng)請求的單個HTML文件。

那么它是怎樣做的呢?幸好Python語法接近正常英文,下面這行代碼

fromurllib.requestimporturlopen

其實已經(jīng)顯示了它的含義:它查找Python的request模塊(在urllib庫里面),只導(dǎo)入一

個ur/open函數(shù)。

urllib還是urllib2?

如果你用過Python2.x里的urllib2庫,可能會發(fā)現(xiàn)urllib2與urllib有些不同。在

Python3.x里,urllib2改名為urllib,被分成一些子模塊:u"他.request、

urllib.parse和urllib.erroro盡管函數(shù)名稱大多和原來一樣,但是在用新的urllib

庫時需要注意哪些函數(shù)被移動到子模塊里了。

urllib是Python的標準庫(就是說你不用額外安裝就可以運行這個例子),包含了從網(wǎng)

絡(luò)請求數(shù)據(jù),處理cookie,甚至改變像請求頭和用戶代理這些元數(shù)據(jù)的函數(shù)。我們將在

本書中廣泛使用”/他,所以建議你讀讀這個庫的Python文檔

(https:〃/3/library/urllib.html)。

ur/opc八用來打開并讀取一個從網(wǎng)絡(luò)獲取的遠程對象。因為它是一個非常通用的庫(它

可以輕松讀取HTML文件、圖像文件,或其他任何文件流),所以我們將在本書中頻繁

地使用它。

1.2BeautifulSoup簡介

"美味的湯綠色的濃湯

在熱氣騰騰的蓋碗里裝

誰不愿意嘗一嘗,這樣的好湯?

晚餐用的湯,美味的湯!”

BeautifulSoup庫的名字取自劉易斯?卡羅爾在《愛麗絲夢游仙境》里的同名詩歌。在故事

中,這首詩是素甲魚1唱的。

iMockTurtle,它本身是一個雙關(guān)語,指英國維多利亞時代的流行菜肴素甲魚湯,其實不是甲魚而是牛肉,如同

中國的豆制品素雞,名為素雞,其實與雞無關(guān)。

就像它在仙境中的說法一樣,BeautifulSoup嘗試化平淡為神奇。它通過定位HTML標簽

來格式化和組織復(fù)雜的網(wǎng)絡(luò)信息,用簡單易用的Python對象為我們展現(xiàn)XML結(jié)構(gòu)信息。

1.2.1安裝BeautifulSoup

由于BeautifulSoup庫不是Python標準庫,因此需要單獨安裝。在本書中,我們將使用最

新的BeautifulSoup4版本(也叫BS4)。BeautifulSoup4的所有安裝方法都在

/software/BeautifulSoup/bs4/doc/里面。Linux系統(tǒng)上的基本

安裝方法是:

apt-getiiastaltpgth。八-bs4

對于Mac系統(tǒng),首先用

癡d。easyj^tallPip

安裝Python的包管理器pip,然后運行

$pipi^talIbeautifulsoup4

來安裝庫文件。

另外,注意如果你的設(shè)備同時安裝了Python2.x和Python3.x,你需要用pgthon3運行

Python3.x:

MgSwipt.pg

當(dāng)你安裝包的時候,如果有可能安裝到了Python2.x而不是Python3.x里,就需要使用:

$sud。pythonsetup.p9install

如果用pip安裝,你還可以用pip3安裝Python3.x版本的包:

$pip3installheautiMsoup4

在Windows系統(tǒng)上安裝與在Mac和Linux上安裝差不多。從上面的下載鏈接下載最新的

BeautifulSoup4源代碼,解壓后進入文件,然后執(zhí)行:

〉pgth。八setup.pyinstall

這樣就可以了!BeautifulSoup將被當(dāng)作設(shè)備上的一個Python庫。你可以在Python終端

里導(dǎo)入它測試一下:

$pythok\

>frombs4importBeautifulSoup

如果沒有錯誤,說明導(dǎo)入成功了。

另外,還有一個Windows版pip(https:〃/pypi/setuptools)的.exe格式

安裝器,裝了之后你就可以輕松安裝和管理包了:

>pipinstallbeautifulsoup4

用虛擬環(huán)境保存庫文件

如果你同時負責(zé)多個Python項目,或者想要輕松打包某個項目及其關(guān)聯(lián)的庫文件,

再或者你擔(dān)心已安裝的庫之間可能有沖突,那么你可以安裝一個Python虛擬環(huán)境來

分而治之。

當(dāng)一個Python庫不用虛擬環(huán)境安裝的時候,你實際上是全局安裝它。這通常需要有

管理員權(quán)限,或者以root身份安裝,這個庫文件對設(shè)備上的每個用戶和每個項目都

是存在的。好在創(chuàng)建虛擬環(huán)境非常簡單:

virtuate,K\vscrapiiagEiav

這樣就創(chuàng)建了一個叫作scrapingEnv的新環(huán)境,你需要先激活它再使用:

于cdscrapingEnv/

*sourcebi^/activate

激活環(huán)境之后,你會發(fā)現(xiàn)環(huán)境名稱出現(xiàn)在命令行提示符前面,提醒你當(dāng)前處于虛擬環(huán)

境中。后面你安裝的任何庫和執(zhí)行的任何程序都是在這個環(huán)境下運行。

在新建的scrapingEnv環(huán)境里,可以安裝并使用BeautifulSoup:

(scmpi八gEnv)電〃八*pipitostallbeautifu(soup4

(sc叼2i八gE八v)電〃八*pgtk。八

>frombs4importBeautifulSoup

>

當(dāng)不再使用虛擬環(huán)境中的庫時,可以通過釋放命令來退出環(huán)境:

(scrapdeactivate

ryaircfpython

>frombs4importBeautifulSoup

Traceback(mostrecentcalllast):

FileUM1,in<module>

ImportError:Nomoduleiaa^v\ed仇4,

將項目關(guān)聯(lián)的所有庫單獨放在一個虛擬環(huán)境里,還可以輕松打包整個環(huán)境發(fā)送給其他

人。只要他們的Python版本和你的相同,你打包的代碼就可以直接通過虛擬環(huán)境運

行,不需要再安裝任何庫。

盡管本書的例子都不要求你使用虛擬環(huán)境,但是請記住,你可以在任何時候激活并使

用它。

1.2.2運行BeautifulSoup

BeautifulSoup庫最常用的對象恰好就是BeautifulSoup對象。讓我們把本章開頭的例子調(diào)

整一下運行看看:

fromurllib.requestimportuHope八

FROMbs4importBeautifulSoup

〃“ope八("kttp://www.pgtk。八scK4pM/p4ges/p4gel.ktMp')

bsObj=BeautifulSoup(ht^(.read0)

print(bsObj.hl,)

輸出結(jié)果是:

<k2>A八tinterestingTitle</hl>

和前面例子一樣,我們導(dǎo)入ur/open,然后調(diào)用hti^l.readQ獲取網(wǎng)頁的HTML內(nèi)容。

這樣就可以把HTML內(nèi)容傳到BeautifulSoup對象,轉(zhuǎn)換成下面的結(jié)構(gòu):

?htmlt<html><head>...</head><body>...</body></html>

一head-><head><title>AUsefulPage<title></head>

—titlet<title>AUsefulPage</title>

—bodyt<body><hl>AnInt...</hl><div>Loremip...</div></body>

一hl-><hl>AnInterestingTitle</hl>

一divt<div>LoremIpsumdolor...</div>

可以看出,我們從網(wǎng)頁中提取的<M>標簽被嵌在BeautifulSoup對象bsObj結(jié)構(gòu)的第二

層(html-body-hl)。但是,當(dāng)我們從對象里提取hl標簽的時候,可以直接調(diào)用它

bsObj.hl

其實,下面的所有函數(shù)調(diào)用都可以產(chǎn)生同樣的結(jié)果:

bsObj.hti^l.body.hl

bsObj.body.kl

bsObj.kti^l.kl

希望這個例子可以向你展示BeaulMSoup庫的強大與簡單。其實,任何HTML(或

XML)文件的任意節(jié)點信息都可以被提取出來,只要目標信息的旁邊或附近有標記就行。

在第3章,我們將進一步探討一些更復(fù)雜的BeautifulSoup函數(shù),還會介紹正則表達式,

以及如何把正則表達式用于BeaiAtifbdSoup以對網(wǎng)站信息進行提取。

1.2.3可靠的網(wǎng)絡(luò)連接

網(wǎng)絡(luò)是十分復(fù)雜的。網(wǎng)頁數(shù)據(jù)格式不友好,網(wǎng)站服務(wù)器宕機,目標數(shù)據(jù)的標簽找不到,都

是很麻煩的事情。網(wǎng)絡(luò)數(shù)據(jù)采集最痛苦的遭遇之一,就是爬蟲運行的時候你洗洗睡了,夢

想著明天一早數(shù)據(jù)就都會采集好放在數(shù)據(jù)庫里,結(jié)果第二天醒來,你看到的卻是一個因某

種數(shù)據(jù)格式異常導(dǎo)致運行錯誤的爬蟲,在前一天當(dāng)你不再盯著屏幕去睡覺之后,沒過一會

兒爬蟲就不再運行了。那個時候,你可能想罵發(fā)明互聯(lián)網(wǎng)(以及那些奇葩的網(wǎng)絡(luò)數(shù)據(jù)格式)

的人,但是你真正應(yīng)該斥責(zé)的人是你自己,為什么一開始不估計可能會出現(xiàn)的異常!

讓我們看看爬蟲import語句后面的第一行代碼,如何處理那里可能出現(xiàn)的異常:

Mtml■Hope八C""P://www.pgtk。八Serq/'ng.c。小/p〃ges/p“ge:t.kthA(")

這行代碼主要可能會發(fā)生兩種異常:

?網(wǎng)頁在服務(wù)器上不存在(或者獲取頁面的時候出現(xiàn)錯誤)

?服務(wù)器不存在

第一種異常發(fā)生時,程序會返回HTTP錯誤。HTTP錯誤可能是"404PageNotFound""500

InternalServerError"等。所有類似情形,八函數(shù)都會拋出"HTTPError"異常。我們

可以用下面的方式處理這種異常:

try:

=urlope^(l,http://\A/w\A/.pytho^crapiiag.coi^/pages/pagd.ktkv\lu')

exceptHTTPErrora$e:

priiat(e)

#返回空值,中斷程序,或者執(zhí)行另一個方案

eke:

#程序繼續(xù)。注意:如果你已經(jīng)在上面異常捕捉那一段代碼里返回或中斷(break),

#那么就不需要使用eke語句了,這段代碼也不會執(zhí)行

如果程序返回HTTP錯誤代碼,程序就會顯示錯誤內(nèi)容,不再執(zhí)行dsc語句后面的代碼。

如果服務(wù)器不存在(就是說鏈接http:〃/打不開,或者是URL

鏈接寫錯了),會返回一個None對象。這個對象與其他編程語言中的八期類

似。我們可以增加一個判斷語句檢測返回的向儂(是不是None:

ifisNone:

pnntC'URLisi^ot

eke:

#程序繼續(xù)

當(dāng)然,即使網(wǎng)頁已經(jīng)從服務(wù)器成功獲取,如果網(wǎng)頁上的內(nèi)容并非完全是我們期望的那樣,

仍然可能會出現(xiàn)異常。每當(dāng)你調(diào)用BeautifulSoup對象里的一個標簽時,增加一個檢查條

件保證標簽確實存在是很聰明的做法。如果你想要調(diào)用的標簽不存在,BeautifulSoup就

會返回None對象。不過,如果再調(diào)用這個None對象下面的子標簽,就會發(fā)生

A.ttribut&Erroir錯誤。

下面這行代碼(nonExistentTag是虛擬的標簽,BeautifulSoup對象里實際沒有)

的八妙s。句.八。八Exist?八七T〃g)

會返回一個None對象。處理和檢查這個對象是十分必要的。如果你不檢查,直接調(diào)用這

個None對象的子標簽,麻煩就來了。如下所示。

print(bsObj.noi^ExistentTagsoi^eTag)

這時就會返回一個異常:

AttributeError:'N。八eTgpe'object八。attribute'soi^eTag1

那么我們怎么才能避免這兩種情形的異常呢?最簡單的方式就是對兩種情形進行檢查:

trg:

badCoiatei^t-bs。初.八。八Existi八gTag.a八othe-Tag

exceptAttributeErrorase:

print("Tagw〃sMtfouiad")

eke:

ifbadCoiateiat==None:

priiat("Tagw〃s

else:

priiat(badColatent)

初看這些檢查與錯誤處理的代碼會覺得有點兒累贅,但是,我們可以重新簡單組織一下代

碼,讓它變得不那么難寫(更重要的是,不那么難讀)。例如,下面的代碼是上面爬蟲的

另一種寫法:

fromurllib.requestimport“Hope八

fromurllib.errorimportHTTPError

frombs4importBeautifulSoiAp

defg=下汨

try:

Mtm/=urlopM(url)

exceptHTTPError4se:

returnNOM

try:

bsObj=Beautifu(Soup(kt^.l.^ead())

title=bsObj.bodg.hl

exceptAttributeErrorase:

return.NOM

returntitle

title=get77Ye("kttp://www.pgtk0nscK〃/hg.C0HA/p〃ges/?age,k±M/”)

iftitle==NOM:

print(nTitlecouldnotbefoui^d")

else:

priiat(title)

在這個例子中,我們創(chuàng)建了一個getTitlc函數(shù),可以返回網(wǎng)頁的標題,如果獲取網(wǎng)頁的

時候遇到問題就返回一個None對象。在g&tTitle函數(shù)里面,我們像前面那樣檢查了

HTTPError,然后把兩行BeautifulSoup代碼封裝在一個try語句里面。這兩行中的任

何一行有問題,AttiribiAt&Eirror都可能被拋出(如果服務(wù)器不存在,向儂/就是一■"Is-

Zone對象,hti^l.re.ad.0就會拋出Attr^uteError)(>其實,我們可以在try語句里

面放任意多行代碼,或者放一個在任意位置都可以拋出AttnfeuteError的函數(shù)。

在寫爬蟲的時候,思考代碼的總體格局,讓代碼既可以捕捉異常又容易閱讀,這是很重要

的。如果你還希望能夠很大程度地重用代碼,那么擁有像gctSitcHTML和g&tTitle,這

樣的通用函數(shù)(具有周密的異常處理功能)會讓快速穩(wěn)定地網(wǎng)絡(luò)數(shù)據(jù)采集變得簡單易行。

第2章復(fù)雜HTML解析

當(dāng)米開朗基羅被問及如何完成《大衛(wèi)》這樣匠心獨具的雕刻作品時,他有一段著名的回答

“很簡單,你只要用錘子把石頭上不像大衛(wèi)的地方敲掉就行了。”

雖然網(wǎng)絡(luò)數(shù)據(jù)采集和大理石雕刻大相徑庭,但是當(dāng)我們從復(fù)雜的網(wǎng)頁中尋覓信息時.,也必

須持有類似的態(tài)度。在我們找到目標信息之前,有很多技巧可以幫我們"敲掉”網(wǎng)頁上那些

不需要的信息。這一章我們將介紹解析復(fù)雜的HTML頁面的方法,從中抽取出我們需要

的信息。

2.1不是一直都要用錘子

面對頁面解析難題(GordianKnot)的時候,不假思索地直接寫幾行語句來抽取信息是非

常直接的做法。但是,像這樣魯莽放縱地使用技術(shù),只會讓程序變得難以調(diào)試或脆弱不堪,

甚至二者兼具。在開始解析網(wǎng)頁之前,讓我們看一些在解析復(fù)雜的HTML頁面時需要避

免的問題。

假如你已經(jīng)確定了目標內(nèi)容,可能是采集一個名字、一組統(tǒng)計數(shù)據(jù),或者一段文字。你的

目標內(nèi)容可能隱藏在一個HTML“爛泥堆”的第20層標簽里,帶有許多沒用的標簽或

HTML屬性。假如你不經(jīng)考慮地直接寫出下面這樣一行代碼來抽取內(nèi)容:

雖然也可以達到目標,但這樣看起來并不是很好。除了代碼欠缺美感之外,還有一個問題

是,當(dāng)網(wǎng)站管理員對網(wǎng)站稍作修改之后,這行代碼就會失效,甚至可能會毀掉整個網(wǎng)絡(luò)爬

蟲。那么你應(yīng)該怎么做呢?

?尋找"打印此頁”的鏈接,或者看看網(wǎng)站有沒有HTML樣式更友好的移動版(把

自己的請求頭設(shè)置成處于移動設(shè)備的狀態(tài),然后接收網(wǎng)站移動版,更多內(nèi)容在

第12章介紹)。

?尋找隱藏在JavaScript文件里的信息。要實現(xiàn)這一點,你可能需要查看網(wǎng)頁加

載的JavaScript文件。我曾經(jīng)要把一個網(wǎng)站上的街道地址(以經(jīng)度和緯度呈現(xiàn)

的)整理成格式整潔的數(shù)組時,查看過內(nèi)嵌谷歌地圖的JavaScript文件,里面

有每個地址的標記點。

?雖然網(wǎng)頁標題經(jīng)常會用到,但是這個信息也許可以從網(wǎng)頁的URL鏈接里獲取。

?如果你要找的信息只存在于一個網(wǎng)站上,別處沒有,那你確實是運氣不佳。如

果不只限于這個網(wǎng)站,那么你可以找找其他數(shù)據(jù)源。有沒有其他網(wǎng)站也顯示了

同樣的數(shù)據(jù)?網(wǎng)站上顯示的數(shù)據(jù)是不是從其他網(wǎng)站上抓取后攢出來的?

尤其是在面對埋藏很深或格式不友好的數(shù)據(jù)時,千萬不要不經(jīng)思考就寫代碼,一定要三思

而后行。如果你確定自己不能另辟蹊徑,那么本章后面的內(nèi)容就是為你準備的。

2.2再端一碗BeautifulSoup

在第1章里,我們快速演示了BeautifulSoup的安裝與運行過程,同時也實現(xiàn)了每次選擇

一個對象的解析方法。在這一節(jié),我們將介紹通過屬性查找標簽的方法,標簽組的使用,

以及標簽解析樹的導(dǎo)航過程。

基本上,你見過的每個網(wǎng)站都會有層疊樣式表(CascadingStyleSheet,CSS)?雖然你可

能會認為,專門為了讓瀏覽器和人類可以理解網(wǎng)站內(nèi)容而設(shè)計一個展現(xiàn)樣式的層,是一件

愚蠢的事,但是CSS的發(fā)明卻是網(wǎng)絡(luò)爬蟲的福音。CSS可以讓HTML元素呈現(xiàn)出差異化,

使那些具有完全相同修飾的元素呈現(xiàn)出不同的樣式。比如,有一些標簽看起來是這樣:

="gwen八〉

而另一些標簽看起來是這樣:

<spaia

網(wǎng)絡(luò)爬蟲可以通過c/ass屬性的值,輕松地區(qū)分出兩種不同的標簽。例如,它們可以用

BeautifulSoup抓取網(wǎng)頁上所有的紅色文字,而綠色文字一個都不抓。因為CSS通過屬性

準確地呈現(xiàn)網(wǎng)站的樣式,所以你大可放心,大多數(shù)新式網(wǎng)站上的c/ass和id屬性資源都

非常豐富。

下面讓我們創(chuàng)建一個網(wǎng)絡(luò)爬蟲來抓取

http:〃/pages/warandpeace.html這個網(wǎng)頁。

在這個頁面里,小說人物的對話內(nèi)容都是紅色的,人物名稱都是綠色的。你可以看到網(wǎng)頁

源代碼里的平。八標簽,引用了對應(yīng)的CSS屬性,如下所示:

"<spanclass="red">Heaven$!whatavirulentattack!</span>"replied.<spain.class=

“green”>theprMce</sp<m>,not認the.leastdiscom.ce.rtedbythisreception..

我們可以抓出整個頁面,然后創(chuàng)建一個BeautifulSoup對象,和第1章里使用的程序類似:

fromurllib.requestimport“Hope八

frombs4importBeautifulSoup

htMl=urlopen("http://www.pythoiasctraping.coM/pages/waran£lpeace.htMl")

bsObjBeautifulSo(Ap(ktkv\l)

通過BeautifulSoup對象,我們可以用fin4AH函數(shù)抽取只包含在〈span

c/ass="grcen">〈/span>標簽里的文字,這樣就會得到一個人物名稱的Python列表

(fMdAH是一個非常靈活的函數(shù),我們后面會經(jīng)常用到它):

,,,,

namgL/st=b$Obj.findAll(spaiaJ{"cSss":"g「eeW})

foriaakv\eM

代碼執(zhí)行以后就會按照《戰(zhàn)爭與和平》中的人物出場順序顯示所有的人名。這是怎么實現(xiàn)

的呢?之前,我們調(diào)用打。旬工agMwnc只能獲取頁面中的第一個指定的標簽?,F(xiàn)在,我

們調(diào)用bsObj.fin4AM(tagNa3C,tagAttributes)可以獲取頁面中所有指定的標簽,不

再只是第一個了。

獲取人名列表之后,程序遍歷列表中所有的名字,然后打印na3&gct_tex20,就可以

把標簽中的內(nèi)容分開顯示了。

什么時候使用getJ:extO與什么時候應(yīng)該保留標簽?

.gct_tcxt()會把你正在處理的HTML文檔中所有的標簽都清除,然后返回一個只包

含文字的字符串。假如你正在處理一個包含許多超鏈接、段落和標簽的大段源代碼,

那么.gct_tcxt。會把這些超鏈接、段落和標簽都清除掉,只剩下一串不帶標簽的文

字。

用BeautifulSoup對象查找你想要的信息,比直接在HTML文本里查找信息要簡單得

多。通常在你準備打印、存儲和操作數(shù)據(jù)時,應(yīng)該最后才使用.g組text。。一般情

況下,你應(yīng)該盡可能地保留HTML文檔的標簽結(jié)構(gòu)。

2.2.1BeautifulSoup的fi八W()和findAl1()

BeautifulSoup里的fMWO和可能是你最常用的兩個函數(shù)。借助它們,你可以通

過標簽的不同屬性輕松地過濾HTML頁面,查找需要的標簽組或單個標簽。

這兩個函數(shù)非常相似,BeautifulSoup文檔里兩者的定義就是這樣:

fii^dAl^tagjattributes,recursive,text,keywords)

FMd(tag,attribute,recursive,text,keywords)

很可能你會發(fā)現(xiàn),自己在95%的時間里都只需要使用前兩個參數(shù):能的和attributes。

但是,我們還是應(yīng)該仔細地觀察所有的參數(shù)。

標簽參數(shù)tag前面已經(jīng)介紹過一一你可以傳一個標簽的名稱或多個標簽名稱組成的

Python列表做標簽參數(shù)。例如,下面的代碼將返回一個包含HTML文檔中所有標題標簽

的列表:1

?如果你想獲得文檔里的一組h<$omejevel>標簽,可以用更簡潔的方法寫代碼來完成。我們將在2.4節(jié)介紹這

類問題的處理方法。

屬性參數(shù)attributes是用一個Python字典封裝一個標簽的若干屬性和對應(yīng)的屬性值。例

如,下面這個函數(shù)會返回HTML文檔里紅色與綠色兩種顏色的印4八標簽:

.fi^dAllC'spa^'j("class":{"green","red."}})

遞歸參數(shù)recursive是一個布爾變量。你想抓取HTML文檔標簽結(jié)構(gòu)里多少層的信息、?如

果匕cursive設(shè)置為True,fMdA"就會根據(jù)你的要求去查找標簽參數(shù)的所有子標簽,以

及子標簽的子標簽。如果recursiW設(shè)置為False,finAM就只查找文檔的一級標簽。

fMdA"默認是支持遞歸查找的(匕cursive默認值是True);一般情況下這個參數(shù)不需

要設(shè)置,除非你真正了解自己需要哪些信息,而且抓取速度非常重要,那時你可以設(shè)置遞

歸參數(shù)。

文本參數(shù)text有點不同,它是用標簽的文本內(nèi)容去匹配,而不是用標簽的屬性。假如我

們想查找前面網(wǎng)頁中包含“theprince”內(nèi)容的標簽數(shù)量,我們可以把之前的findAH方法換

成下面的代碼:

b$Obj.fiiadAll(text=,,theprince")

p力八況/c八(八“meList))

輸出結(jié)果為"7"。

范圍限制參數(shù)/沁七顯然只用于行ndA"方法。fMd其實等價于fMdA"的/沁/等于1

時的情形。如果你只對網(wǎng)頁中獲取的前x項結(jié)果感興趣,就可以設(shè)置它。但是要注意,這

個參數(shù)設(shè)置之后,獲得的前幾項結(jié)果是按照網(wǎng)頁上的順序排序的,未必是你想要的那前兒

項。

還有一個關(guān)鍵詞參數(shù)keyword,可以讓你選擇那些具有指定屬性的標簽。例如:

allTextbQbj.ficdAH(id="te&")

pext[O].get_te>ctQ)

關(guān)鍵詞參數(shù)的注意事項

雖然關(guān)鍵詞參數(shù)keyword在一些場景中很有用,但是,它是BeautifulSoup在技術(shù)

上做的一個冗余功能。任何用關(guān)鍵詞參數(shù)能夠完成的任務(wù),同樣可以用本章后面將介

紹的技術(shù)解決(請參見2.3節(jié)和2.6節(jié))。

例如,下面兩行代碼是完全一樣的:

b$Obj,fiiadAll(id=',texti,)

bs0tj.fi21AH(叫{"id〃:〃text'11)

另外,用keyword偶爾會出現(xiàn)問題,尤其是在用class屬性查找標簽的時候,因為

class是Python中受保護的關(guān)鍵字。也就是說,class是Python語言的保留字,在

Python程序里是不能當(dāng)作變量或參數(shù)名使用的(和前面介紹的

Beautifu(Soup.fin4A(10里的keyword無關(guān))2。假如你運行下面的代碼,Python

就會因為你誤用c/ass保留字而產(chǎn)生一個語法錯誤:

bs。0.fMdAH(c(ass="green")

不過,你可以用BeautifulSoup提供的有點兒臃腫的方案,在class后面增加一個下

劃線:

bsOtj.fi~lAH(dass_="giree八")

另外,你也可以用屬性參數(shù)把dass用引號包起來:

bsObj.findAHC11^{"class":,,gtreek\"})

2Python語言參考里提供了關(guān)鍵詞列表(https:〃docs.python,org/3/reference/lexicalanalysis.html#keywords)°

看到這里,你可能會捫心自問:“現(xiàn)在我是不是已經(jīng)知道如何用標簽屬性獲取一組標簽了

一一用字典把屬性傳到函數(shù)里就行了?”

回憶一下前面的內(nèi)容,通過標簽參數(shù)tag把標簽列表傳到出認里獲取一列標簽,其

實就是一個“或”關(guān)系的過濾器(即選擇所有帶標簽1或標簽2或標簽3的一列標簽)。

如果你的標簽列表很長,就需要花很長時間才能寫完。而關(guān)鍵詞參數(shù)keyword可以讓你

增加一個"與"關(guān)系的過濾器來簡化工作。

2.2.2其他BeautifulSoup對象

看到這里,你已經(jīng)見過BeautifulSoup庫里的兩種對象了。

?BeautifulSoup對象

前面代碼示例中的bsObj

?標簽Tag對象

BcautifulSoup對象通過find和findAM,或者直接調(diào)用子標簽獲取的一列對象或單個

對象,就像:

bsObj.div.kt-

但是,這個庫還有另外兩種對象,雖然不常用,卻應(yīng)該了解一下。

?NavigableString對象

用來表示標簽里的文字,不是標簽(有些函數(shù)可以操作和生成NavigablcString對象,

而不是標簽對象)。

?COMMCht對象

用來查找HTML文檔的注釋標簽,<!--像這樣-->

這四個對象是你用BeautifulSoup庫時會遇到的所有對象(寫作本書的時候)。

2.2.3導(dǎo)航樹

findAH函數(shù)通過標簽的名稱和屬性來查找標簽。但是如果你需要通過標簽在文檔中的位

置來查找標簽,該怎么辦?這就是導(dǎo)航樹(NavigatingTrees)的作用。在第1章里,我

們看過用單一方向進行BeautifulSoup標簽樹的導(dǎo)航:

bsObj.tagsubTag.aMtherSubTag

現(xiàn)在我們用虛擬的在線購物網(wǎng)站http:〃/pages/page3.html作為

要抓取的示例網(wǎng)頁,演示HTML導(dǎo)航樹的縱向和橫向?qū)Ш剑ㄈ鐖D2-1所示)。

繳管TotallyNormalGifts

Hereisacollectionoftotallynormal,totallyreasonablegiftsthatyourfriendsaresuretolove!Ourcollectionishand-curak

Wehaven'tfiguredouthowtomakeonlineshoppingcartsyet.butyoucansendusacheckto:

123MainSt.

Abuja,Nigeria

Wewillthensendyourtotallyamazinggift,pronto!PleaseincludeanextraS5.00forgiRwrapping.

ItemTitleDescription

Thisvegetablebasketistheperfectgiftforyour

Vegetable

healthconscious(oroverweight)friends!Now

Basket

withsuper-colorfulbellpeppers!

Hand-paintedbytrainedmonkeys,these

Russian

exquisitedollsarepriceless!AndbyMpriceless,M

Nesting

wemean"extremelyexpensive"!8entiredolls

Dolls

perset!Octuplethepresents!

Ifsomethingseemsfishyaboutthispainting,ifs

Fish

becauseit'safish!Alsohand-paintedbytrained

Painting

monkeys!

Dead

Thisisanex-parrot!Ormaybehersonlyresting?

Parrot

圖2?1:/pages/page3.html截圖

這個HTML頁面可以映射成一棵樹(為了簡潔,省略了一些標簽),如下所示:

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論