用Python實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲

Web Scraping with Python

這是《Web Scraping with Python》一書的閱讀筆記亩码。該筆記跳過了一些不必要的描述弹谁,對書的代碼也做了核實(shí)瞬场,也引入了一些我自己對爬蟲腳本實(shí)現(xiàn)的理解买鸽。

第一章 你的第一個(gè)網(wǎng)絡(luò)爬蟲程序

為了幫助理解,作者用了一個(gè)例子贯被。假設(shè)Alice有一個(gè)網(wǎng)絡(luò)服務(wù)器眼五。而Bob想用一個(gè)臺式機(jī)通過瀏覽器訪問Alice的服務(wù)器上運(yùn)行的某個(gè)網(wǎng)站。整個(gè)訪問過程歸納如下:

1. Bob輸入訪問網(wǎng)站的地址后彤灶,Bob的電腦傳輸一段二進(jìn)制的數(shù)據(jù)看幼,這些數(shù)據(jù)包含數(shù)據(jù)頭數(shù)據(jù)內(nèi)容。數(shù)據(jù)頭包含發(fā)送方的mac地址和目的地的ip地址幌陕,而數(shù)據(jù)內(nèi)容包含了針對Alice網(wǎng)絡(luò)服務(wù)器的請求诵姜,例如,獲得某個(gè)網(wǎng)頁頁面搏熄。

2. Bob的本地網(wǎng)絡(luò)路由器將數(shù)據(jù)打包傳輸?shù)紸lice的ip地址棚唆。

3. Bob的數(shù)據(jù)最后通過物理電纜進(jìn)行傳輸。

4. Alice的服務(wù)器接受到了Bob的數(shù)據(jù)包心例。

5. Alice的服務(wù)器識別存于數(shù)據(jù)頭的端口號宵凌,發(fā)現(xiàn)是80,意味著這是一個(gè)網(wǎng)頁請求契邀,于是調(diào)用網(wǎng)頁服務(wù)器相關(guān)的程序摆寄。

6. 網(wǎng)頁服務(wù)器程序接受到如下信息::

- This is a GET request

- The following file is requested: index.html

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

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

from urllib.request import urlopen

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

print(html.read())

這是一段Python3的程序微饥,請用Python3.X的版本運(yùn)行它。執(zhí)行后古戴,該網(wǎng)頁的HTML內(nèi)容會被打印出來欠橘,其實(shí)就是Chrome瀏覽器右鍵查看網(wǎng)頁源代碼可以看到的網(wǎng)頁內(nèi)容。

urllib 還是 urllib2?

Python2中用urllib2现恼,而Python3中用urllib肃续。urllib是Python的標(biāo)準(zhǔn)庫,用于網(wǎng)頁數(shù)據(jù)請求叉袍,處理Cookies始锚,甚至更改請求者的數(shù)據(jù)頭信息。因?yàn)檎緯拇a都會涉及urllib的使用喳逛,所以有空的時(shí)候可以看看Python的官方文檔:https://docs.python.org/3/library/urllib.html

BeautifulSoup的介紹和安裝

一句話來概括瞧捌,BeautifulSoup將不可能變成了可能,它將HTML的內(nèi)容組織成了Python可以識別的對象格式。因?yàn)锽eautifulSoup不是Python默認(rèn)的庫姐呐,我們要先安裝它殿怜。本書用BeautifulSoup的第四個(gè)版本。下載地址:https://pypi.python.org/pypi/beautifulsoup4曙砂⊥访眨可下載安裝包:beautifulsoup4-4.5.3.tar.gz(md5)。解壓后使用命令:"python.exe setup.py install" 進(jìn)行安裝鸠澈,這種安裝方式在Windows下也可行柱告。當(dāng)然也可以使用pip命令安裝,省去下載安裝包的過程:"pip install beautifulsoup4"款侵,但Windows下末荐,要另外裝pip工具。

BeautifulSoup初體驗(yàn)

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這個(gè)HTML文件新锈,并輸出了h1這個(gè)字段的內(nèi)容:

<h1>An Interesting Title<h1>

HTML文件的結(jié)構(gòu)

上面這個(gè)圖顯示的是HTML的常用結(jié)構(gòu)甲脏。bsObj.h1是一個(gè)快捷的訪問h1數(shù)據(jù)的方法,實(shí)際上類似這樣的訪問也是有效的:bsObj.html.body.h1妹笆,bsObj.body.h1块请,bsObj.html.h1

通過這個(gè)例子,我們應(yīng)該可以體會到BeautifulSoup的方便拳缠。第三章將對BeautifulSoup做更深入的討論墩新,例如:使用正則表達(dá)式提取網(wǎng)頁數(shù)據(jù)。

考慮腳本的穩(wěn)定性

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

考慮到某些情況下會出現(xiàn)頁面無法訪問的問題窟坐,建議加上以上出錯(cuò)判斷的代碼海渊。如果嘗試訪問的BeautifulSoup標(biāo)簽不存在,BeautifulSoup會返回None對象哲鸳。那么問題來了臣疑,訪問None對象會拋出AttributeError異常。以下是一個(gè)魯棒的獲取數(shù)據(jù)的腳本:

容錯(cuò)的腳本例子

第二章 HTML解析的進(jìn)階

第一章介紹的BeautifulSoup可以很方便地提取需要的帶標(biāo)簽的數(shù)據(jù)徙菠,但隨著標(biāo)記深度的遞增讯沈,簡單地使用對象數(shù)據(jù)不利于表達(dá)和調(diào)試,例如以下代碼就很難理解:

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

這段代碼不僅不美觀婿奔,還不夠魯棒缺狠。當(dāng)爬取的網(wǎng)站做了小幅度的更改后,這段代碼就無效了萍摊。有沒有更好的方法呢挤茄?

BeautifulSoup的另一個(gè)功能

通常,一個(gè)HTML頁面都包含有CSS樣式冰木,例如

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

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

BeautifulSoup可以通過制定class的值驮樊,過濾一些不需要的內(nèi)容。例如

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

for?name?in?nameList:

print(name.get_text())

這句代碼可以獲得class為green的span內(nèi)容片酝。其中函數(shù)get_text()可以獲得標(biāo)簽的內(nèi)容囚衔。

findAll和find的函數(shù)定義如下

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"})

這些代碼可以列出所有有關(guān)的標(biāo)簽內(nèi)容。recursive設(shè)置為true的話就執(zhí)行遞歸查詢雕沿。默認(rèn)情況該值為true练湿。

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

print(len(nameList))

以上的代碼可以統(tǒng)計(jì)"the prince"字符串出現(xiàn)的次數(shù),輸出為7审轮。(真是太強(qiáng)大了)

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

print(allText[0].get_text())

這段代碼可以根據(jù)attribute來選擇內(nèi)容肥哎,代碼的輸出是id是text的div包含的所有文本內(nèi)容。因?yàn)閏lass是Python的關(guān)鍵字疾渣,bsObj.findAll(class="green")是不允許的篡诽,可以用以下代碼替換:

bsObj.findAll(class_="green")

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

正則表達(dá)式

正則表達(dá)式在很多人眼里都是個(gè)高大上的工具,什么都不多說了榴捡,先來一個(gè)例子杈女。

aa*bbbbb(cc)*(d | )

aa*

這里的*指代任何東西

bbbbb

代表5個(gè)b,沒有其它的意義

(cc)*

代表任意個(gè)重復(fù)的c吊圾,也可以是0個(gè)

(d | )

|代表或的意思达椰,這句代表以d和空格結(jié)尾,或者僅僅以空格結(jié)尾

一些通用的規(guī)則如下项乒,例如E-mail的命名規(guī)則:

E-mail能包含的字符為:大小寫字母啰劲、數(shù)字、點(diǎn)檀何、加號或者下劃線蝇裤,并且每個(gè)E-mail都要包含@符號。這個(gè)規(guī)則用正則表達(dá)式可以這樣寫:[A-Za-z0-9\._+]+

正則表達(dá)式非常得智能频鉴,它會知道中括號中的內(nèi)容是指從A到Z的字符都可以栓辜,\.代表一個(gè)點(diǎn)(period),然后最后的+號以為著這些字符可以出現(xiàn)任意多次砚殿,但至少要出現(xiàn)一次啃憎。

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

接下來詳細(xì)介紹12個(gè)在Python中常用的正則表達(dá)式

.代表任意一個(gè)字符辛萍,如果是要查找點(diǎn)的話,就用轉(zhuǎn)義字符\.

+的作用是將前面一個(gè)字符或一個(gè)子表達(dá)式重復(fù)一遍或者多遍羡藐。

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

這里舉一個(gè)例子介紹正則表達(dá)式在實(shí)際數(shù)據(jù)抓取仆嗦,原書的代碼編譯不過辉阶,我做了一些修改。

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"])


訪問屬性

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

Lambda表達(dá)式

作者針對Lambda的描述是某個(gè)函數(shù)的參數(shù)是另一個(gè)函數(shù)。我的理解是谆甜,某個(gè)查找的條件是某個(gè)判斷函數(shù)的返回值垃僚。例如:

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

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

感覺這兩章的內(nèi)容足夠應(yīng)付基本的爬蟲應(yīng)用了谆棺,以后有額外的需求,再解讀其他幾章罕袋。^__^

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末改淑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子浴讯,更是在濱河造成了極大的恐慌朵夏,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榆纽,死亡現(xiàn)場離奇詭異仰猖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)掠河,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門亮元,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人唠摹,你說我怎么就攤上這事爆捞。” “怎么了勾拉?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵煮甥,是天一觀的道長。 經(jīng)常有香客問我藕赞,道長成肘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任斧蜕,我火速辦了婚禮双霍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘批销。我一直安慰自己洒闸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布均芽。 她就那樣靜靜地躺著丘逸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掀宋。 梳的紋絲不亂的頭發(fā)上深纲,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天仲锄,我揣著相機(jī)與錄音,去河邊找鬼湃鹊。 笑死儒喊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的涛舍。 我是一名探鬼主播澄惊,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼富雅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肛搬,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤没佑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后温赔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛤奢,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年陶贼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啤贩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拜秧,死狀恐怖痹屹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情枉氮,我是刑警寧澤志衍,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站聊替,受9級特大地震影響楼肪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惹悄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一春叫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泣港,春花似錦暂殖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惫东,卻和暖如春莉给,著一層夾襖步出監(jiān)牢的瞬間毙石,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工颓遏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留徐矩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓叁幢,卻偏偏與公主長得像滤灯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子曼玩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容