已經(jīng)確定目標內(nèi)容后,應該怎么做儡蔓?
· 尋找“打印此頁”的鏈接黍衙,或者看看網(wǎng)站有沒有 HTML 樣式更友好的移動版冕象。
· 尋找隱藏在 JavaScript 文件里的信息。
· 所需信息是否存在于其它網(wǎng)站龟梦?網(wǎng)站上顯示的數(shù)據(jù)是不是從其它網(wǎng)站上抓取的隐锭?
再來一碗 BeautifulSoup
通過屬性查找標簽
CSS 可以讓 HTML 元素呈現(xiàn)出差異化,使那些具有相同修飾元素呈現(xiàn)出不同的樣式计贰。
比如一個頁面中钦睡,小說任務對話都是紅色的,任務名稱都是綠色的躁倒。源代碼里的 span 標簽荞怒,引用了對應的 CSS 屬性,如下所示:
"<span class="red">Heavens! what a virulent attack!</span>" replied <span class="green">the prince</span>, not in the least disconcerted by this reception.
我們可以抓出整個頁面秧秉,然后創(chuàng)建一個 BeautifulSoup 對象:
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen("http://www.pythonscraping.com/pages/warandpeace.html")
bsObj = BeautifulSoup(html)
通過 BeautifulSoup 對象褐桌,我們可以用 find_all 函數(shù)抽取只包含在 <span class="green"></span> 標簽里的文字,這樣就會得到一個人物名稱的 Python 列表:
name_list = bsObj.find_all('span', {'class':'green'})
for name in name_list:
print(name.get_text())
代碼執(zhí)行后就會按照《戰(zhàn)爭與和平》中的人物出場順序顯示所有的人名象迎。這是怎么實現(xiàn)的呢荧嵌?
之前,我們調(diào)用 bsObj.tagName 只能獲取頁面中的第一個指定的標簽±剩現(xiàn)在完丽,調(diào)用 bsObj.find_all(tagName, tagAttributes) 可以獲取頁面中所有指定的標簽,不再只是第一個拇舀。
獲取人名列表之后逻族,程序遍歷列表中所有的名字,然后打印 name.get_text()骄崩,就可以把標簽中的內(nèi)容分開顯示了聘鳞。
find() 和 find_all()
這兩個函數(shù)非常類似,BeautifulSoup 文檔里兩者的定義是這樣的:
find_all(tag, attrs, recursive, text, limit, keywords)
find(tag, attrs, recursive, text, keywords)
標簽參數(shù) tag 前面已經(jīng)介紹過——你可以傳一個標簽的名稱或者多個標簽名稱組成的列表做標簽參數(shù)要拂。
屬性參數(shù) attributes 是用一個Python字典封裝一個標簽的若干屬性和對應的屬性值抠璃。例如,下面這個函數(shù)會返回 HTML 文檔里紅色與綠色兩種顏色的 span 標簽:
.find_all('span', {'class':{'green', 'red'}})
遞歸參數(shù) recursive 是一個布爾變量脱惰。你想抓取 HTML 文檔標簽結(jié)構(gòu)里多少層的信息搏嗡?如果設置為 True,find_all() 就會查找標簽參數(shù)的所有子標簽,以及子標簽的子標簽采盒。如果設置為 False旧乞,find_all() 就只查找文檔的一級標簽。(默認值為 True)
文本參數(shù) text 有點不同磅氨,它是用標簽的文本內(nèi)容去匹配尺栖,而不是用標簽的屬性。假如我們想查找前面網(wǎng)頁中包含“the prince”內(nèi)容的標簽數(shù)量烦租,我們可以把之前的 find_all() 方法換成下面的代碼:
name_list = bsObj.find_all(text='the prince')
print(len(name_list))
輸出結(jié)果為“7”延赌。
范圍限制參數(shù) limit。find 其實等價于 find_all 的limit 等于 1 時的情形叉橱。如果你只對網(wǎng)頁中獲取的前 n 項結(jié)果感興趣挫以,就可以設置它。但是前幾項的結(jié)果是按照網(wǎng)頁上的順序排序的窃祝。
還有一個關(guān)鍵詞參數(shù) keyword屡贺,可以讓你選擇哪些具有指定屬性的標簽。但是锌杀,任何關(guān)鍵詞參數(shù)能夠完成的任務甩栈,同樣可以用 attrs 解決。
通過標簽參數(shù)列表傳到 .find_all() 里獲取一列標簽糕再,其實就是一個“或”關(guān)系的過濾器量没。而關(guān)鍵詞參數(shù)可以讓你增加一個“與”關(guān)系的過濾器來簡化工作。
導航樹
獲取屬性
在網(wǎng)絡數(shù)據(jù)采集時你經(jīng)常不需要查找標簽的內(nèi)容突想,而是需要查找標簽屬性殴蹄。比如標簽 <a> 指向的URL鏈接包含在 href 屬性中,或者 <img> 標簽的圖片文件包含 src 屬性中猾担,這時獲取標簽屬性就變得非常有用了袭灯。
對于一個標簽對象,可以用下面的代碼獲取它的全部屬性:
myTag.attrs
這行代碼返回的是一個 Python 字典對象绑嘹,可以獲取和操作這些屬性稽荧。比如要獲取圖片的資源位置 src,可以用下面的代碼:
myTag.attrs['src']
lambda 表達式
BeautifulSoup 允許我們把特定函數(shù)類型當作 find_all() 函數(shù)的參數(shù)工腋。唯一的限制條件是這些函數(shù)必須把一個標簽作為參數(shù)且返回結(jié)果是布爾類型姨丈。 BeautifulSoup 用這個函數(shù)來評估它遇到的每個標簽對象,最后把評估結(jié)果為“真”的標簽保留擅腰,把其他標簽剔除蟋恬。
例如,下面的代碼就是獲取有兩個屬性的標簽:
soup.find_all(lambda tag: len(tag.attrs) == 2)