用Python實現網絡爬蟲

Web Scraping with Python

這是《Web Scraping with Python》一書的閱讀筆記。該筆記跳過了一些不必要的描述,對書的代碼也做了核實,也引入了一些我自己對爬蟲腳本實現的理解。

第一章 你的第一個網絡爬蟲程序

為了幫助理解,作者用了一個例子。假設Alice有一個網絡服務器。而Bob想用一個臺式機通過瀏覽器訪問Alice的服務器上運行的某個網站。整個訪問過程歸納如下:

1. Bob輸入訪問網站的地址后,Bob的電腦傳輸一段二進制的數據,這些數據包含數據頭數據內容。數據頭包含發送方的mac地址和目的地的ip地址,而數據內容包含了針對Alice網絡服務器的請求,例如,獲得某個網頁頁面。

2. Bob的本地網絡路由器將數據打包傳輸到Alice的ip地址。

3. Bob的數據最后通過物理電纜進行傳輸。

4. Alice的服務器接受到了Bob的數據包。

5. Alice的服務器識別存于數據頭的端口號,發現是80,意味著這是一個網頁請求,于是調用網頁服務器相關的程序。

6. 網頁服務器程序接受到如下信息::

- This is a GET request

- The following file is requested: index.html

7. 網頁服務器程序載入正確的HTML 文件,并打包通過本地路由發送給Bob的電腦.

而Python的庫包含了模擬瀏覽器訪問某個頁面的功能,如下:

from urllib.request import urlopen

html = urlopen("http://pythonscraping.com/pages/page1.html")

print(html.read())

這是一段Python3的程序,請用Python3.X的版本運行它。執行后,該網頁的HTML內容會被打印出來,其實就是Chrome瀏覽器右鍵查看網頁源代碼可以看到的網頁內容。

urllib 還是 urllib2?

Python2中用urllib2,而Python3中用urllib。urllib是Python的標準庫,用于網頁數據請求,處理Cookies,甚至更改請求者的數據頭信息。因為正本書的代碼都會涉及urllib的使用,所以有空的時候可以看看Python的官方文檔:https://docs.python.org/3/library/urllib.html

BeautifulSoup的介紹和安裝

一句話來概括,BeautifulSoup將不可能變成了可能,它將HTML的內容組織成了Python可以識別的對象格式。因為BeautifulSoup不是Python默認的庫,我們要先安裝它。本書用BeautifulSoup的第四個版本。下載地址:https://pypi.python.org/pypi/beautifulsoup4。可下載安裝包:beautifulsoup4-4.5.3.tar.gz(md5)。解壓后使用命令:"python.exe setup.py install" 進行安裝,這種安裝方式在Windows下也可行。當然也可以使用pip命令安裝,省去下載安裝包的過程:"pip install beautifulsoup4",但Windows下,要另外裝pip工具。

BeautifulSoup初體驗

from urllib.request import urlopen

from bs4 import BeautifulSoup

html = urlopen("http://www.pythonscraping.com/exercises/exercise1.html")

bsObj = BeautifulSoup(html.read(), "html.parser");

print(bsObj.h1)

這段代碼解析了exercise1.html這個HTML文件,并輸出了h1這個字段的內容:

<h1>An Interesting Title<h1>

HTML文件的結構

上面這個圖顯示的是HTML的常用結構。bsObj.h1是一個快捷的訪問h1數據的方法,實際上類似這樣的訪問也是有效的:bsObj.html.body.h1,bsObj.body.h1,bsObj.html.h1

通過這個例子,我們應該可以體會到BeautifulSoup的方便。第三章將對BeautifulSoup做更深入的討論,例如:使用正則表達式提取網頁數據。

考慮腳本的穩定性

try:

? ? ? ? html = urlopen("http://www.pythonscraping.com/exercises/exercise1.html")

except HTTPError as e:

? ? ? ? print(e)

? ? ? ? #return null, break, or do some other "Plan B"

else:

? ? ? ? #program continues. Note: If you return or break in the

? ? ? ?#exception catch, you do not need to use the "else" statement

考慮到某些情況下會出現頁面無法訪問的問題,建議加上以上出錯判斷的代碼。如果嘗試訪問的BeautifulSoup標簽不存在,BeautifulSoup會返回None對象。那么問題來了,訪問None對象會拋出AttributeError異常。以下是一個魯棒的獲取數據的腳本:

容錯的腳本例子

第二章 HTML解析的進階

第一章介紹的BeautifulSoup可以很方便地提取需要的帶標簽的數據,但隨著標記深度的遞增,簡單地使用對象數據不利于表達和調試,例如以下代碼就很難理解:

bsObj.findAll("table")[4].findAll("tr")[2].find("td").findAll("div")[1].find("a")

這段代碼不僅不美觀,還不夠魯棒。當爬取的網站做了小幅度的更改后,這段代碼就無效了。有沒有更好的方法呢?

BeautifulSoup的另一個功能

通常,一個HTML頁面都包含有CSS樣式,例如

<span class="green"></span>

<span class="red"></span>

BeautifulSoup可以通過制定class的值,過濾一些不需要的內容。例如

nameList=bsObj.findAll("span", {"class":"green"})

for?name?in?nameList:

print(name.get_text())

這句代碼可以獲得class為green的span內容。其中函數get_text()可以獲得標簽的內容。

findAll和find的函數定義如下

findAll(tag,attributes,recursive,text,limit,keywords)

find(tag,attributes,recursive,text,keywords)

findAll還有很多有用的寫法

.findAll({"h1","h2","h3","h4","h5","h6"})

.findAll("span", {"class":"green","class":"red"})

這些代碼可以列出所有有關的標簽內容。recursive設置為true的話就執行遞歸查詢。默認情況該值為true。

nameList=bsObj.findAll(text="the prince")

print(len(nameList))

以上的代碼可以統計"the prince"字符串出現的次數,輸出為7。(真是太強大了)

allText=bsObj.findAll(id="text") #和bsObj.findAll("", {"id":"text"})是等價的

print(allText[0].get_text())

這段代碼可以根據attribute來選擇內容,代碼的輸出是id是text的div包含的所有文本內容。因為class是Python的關鍵字,bsObj.findAll(class="green")是不允許的,可以用以下代碼替換:

bsObj.findAll(class_="green")

bsObj.findAll("", {"class":"green"}

正則表達式

正則表達式在很多人眼里都是個高大上的工具,什么都不多說了,先來一個例子。

aa*bbbbb(cc)*(d | )

aa*

這里的*指代任何東西

bbbbb

代表5個b,沒有其它的意義

(cc)*

代表任意個重復的c,也可以是0個

(d | )

|代表或的意思,這句代表以d和空格結尾,或者僅僅以空格結尾

一些通用的規則如下,例如E-mail的命名規則:

E-mail能包含的字符為:大小寫字母、數字、點、加號或者下劃線,并且每個E-mail都要包含@符號。這個規則用正則表達式可以這樣寫:[A-Za-z0-9\._+]+

正則表達式非常得智能,它會知道中括號中的內容是指從A到Z的字符都可以,\.代表一個點(period),然后最后的+號以為著這些字符可以出現任意多次,但至少要出現一次。

再看一個更復雜的:[A-Za-z0-9\._+]+@[A-Za-z]+\.(com|org|edu|net),這種正則表達式就可以匹配任意以com|org|edu|net結尾的郵箱地址。

接下來詳細介紹12個在Python中常用的正則表達式

.代表任意一個字符,如果是要查找點的話,就用轉義字符\.

+的作用是將前面一個字符或一個子表達式重復一遍或者多遍。

*跟在其他符號后面表達可以匹配到它0次或多次,例如https*就可以找出http://和https://兩種。

這里舉一個例子介紹正則表達式在實際數據抓取,原書的代碼編譯不過,我做了一些修改。

from urllib.request import urlopen

from bs4 import BeautifulSoup

import re

html = urlopen("http://www.pythonscraping.com/pages/page3.html")

bsObj = BeautifulSoup(html.read(), "html.parser")

images = bsObj.findAll("img", {"src":re.compile("\.\.\/img\/gifts\/img.*\.jpg")})

for image in images:

print(image["src"])


訪問屬性

通過這樣的方式就可以訪問某個屬性:myImgTag.attrs['src']

Lambda表達式

作者針對Lambda的描述是某個函數的參數是另一個函數。我的理解是,某個查找的條件是某個判斷函數的返回值。例如:

bsObj.findAll(lambda tag: len(tag.attrs) == 2)

這句代碼可以找出tag有兩個的條目。返回的是len(tag.attrs) == 2為True的所有條目。

感覺這兩章的內容足夠應付基本的爬蟲應用了,以后有額外的需求,再解讀其他幾章。^__^

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容