使用 Python 的 requests 和 Beautiful Soup 來分析網(wǎng)頁(yè)
- 學(xué)習(xí)這個(gè) Python 教程,輕松提取網(wǎng)頁(yè)的有關(guān)信息。
瀏覽網(wǎng)頁(yè)可能占了你一天中的大部分時(shí)間。然而,你總是需要手動(dòng)瀏覽,這很討厭,不是嗎?你必須打開瀏覽器,然后訪問一個(gè)網(wǎng)站,單擊按鈕,移動(dòng)鼠標(biāo)……相當(dāng)費(fèi)時(shí)費(fèi)力。如果能夠通過代碼與互聯(lián)網(wǎng)交互,豈不是更好嗎?
在 Python 的 requests 模塊的幫助下,你可以使用 Python 從互聯(lián)網(wǎng)中獲取數(shù)據(jù):
import requests
DATA = "https://opensource.com/article/22/5/document-source-code-doxygen-linux"
PAGE = requests.get(DATA)
print(PAGE.text)
在以上代碼示例中,你首先導(dǎo)入了 requests 模塊。接著,你創(chuàng)建了兩個(gè)變量:其中一個(gè)叫做 DATA,它用來保存你要下載的 URL。在之后的代碼中,你將能夠在每次運(yùn)行應(yīng)用程序時(shí)提供不同的 URL。不過,就目前而言,最簡(jiǎn)單的方法是“硬編碼”一個(gè)測(cè)試 URL,以達(dá)到演示目的。
另一個(gè)變量是 PAGE。代碼讀取了存儲(chǔ)在 DATA 中的 URL,然后把它作為參數(shù)傳入 requests.get 函數(shù),最后用變量 PAGE 來接收函數(shù)的返回值。requests 模塊及其 .get 函數(shù)的功能是:“讀取”一個(gè)互聯(lián)網(wǎng)地址(一個(gè) URL)、訪問互聯(lián)網(wǎng),并下載位于該地址的任何內(nèi)容。
當(dāng)然,其中涉及到很多步驟。幸運(yùn)的是,你不必自己弄清楚,這也正是 Python 模塊存在的原因。最后,你告訴 Python 打印 requests.get 存儲(chǔ)在 PAGE 變量的 .text 字段中的所有內(nèi)容。
Beautiful Soup
如果你運(yùn)行上面的示例代碼,你會(huì)得到示例 URL 的所有內(nèi)容,并且,它們會(huì)不加選擇地輸出到你的終端里。這是因?yàn)樵诖a中,你對(duì) requests 收集到的數(shù)據(jù)所做的唯一事情,就是打印它。然而,解析文本才是更加有趣的。
Python 可以通過其最基本的功能來“讀取”文本,但解析文本允許你搜索模式、特定單詞、HTML 標(biāo)簽等。你可以自己解析 requests 返回的文本,不過,使用專門的模塊會(huì)容易得多。針對(duì) HTML 和 XML 文本,我們有 Beautiful Soup 庫(kù)。
下面這段代碼完成了同樣的事情,只不過,它使用了 Beautiful Soup 來解析下載的文本。因?yàn)?Beautiful Soup 可以識(shí)別 HTML 元素,所以你可以使用它的一些內(nèi)置功能,讓輸出對(duì)人眼更友好。
例如,在程序的末尾,你可以使用 Beautiful Soup 的 .prettify 函數(shù)來處理文本(使其更美觀),而不是直接打印原始文本:
from bs4 import BeautifulSoup
import requests
PAGE = requests.get("https://opensource.com/article/22/5/document-source-code-doxygen-linux")
SOUP = BeautifulSoup(PAGE.text, 'html.parser')
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
# do a thing here
print(SOUP.prettify())
通過以上代碼,我們確保了每個(gè)打開的 HTML 標(biāo)簽都輸出在單獨(dú)的一行,并帶有適當(dāng)?shù)目s進(jìn),以幫助說明標(biāo)簽的繼承關(guān)系。實(shí)際上,Beautiful Soup 能夠通過更多方式來理解 HTML 標(biāo)簽,而不僅僅是將它打印出來。
你可以選擇打印某個(gè)特定標(biāo)簽,而不是打印整個(gè)頁(yè)面。例如,嘗試將打印的選擇器從 print(SOUP.prettify()) 更改為:
print(SOUP.p)
這只會(huì)打印一個(gè) <p> 標(biāo)簽。具體來說,它只打印遇到的第一個(gè) <p> 標(biāo)簽。要打印所有的 <p> 標(biāo)簽,你需要使用一個(gè)循環(huán)。
循環(huán)
使用 Beautiful Soup 的 find_all 函數(shù),你可以創(chuàng)建一個(gè) for 循環(huán),從而遍歷 SOUP 變量中包含的整個(gè)網(wǎng)頁(yè)。除了 <p> 標(biāo)簽之外,你可能也會(huì)對(duì)其他標(biāo)簽感興趣,因此最好將其構(gòu)建為自定義函數(shù),由 Python 中的 def 關(guān)鍵字(意思是 “定義”define)指定。
def loopit():
for TAG in SOUP.find_all('p'):
print(TAG)
你可以隨意更改臨時(shí)變量 TAG 的名字,例如 ITEM 或 i 或任何你喜歡的。每次循環(huán)運(yùn)行時(shí),TAG 中都會(huì)包含 find_all 函數(shù)的搜索結(jié)果。在此代碼中,它搜索的是 <p> 標(biāo)簽。
函數(shù)不會(huì)自動(dòng)執(zhí)行,除非你顯式地調(diào)用它。你可以在代碼的末尾調(diào)用這個(gè)函數(shù):
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
# do a thing here
loopit()
運(yùn)行代碼以查看所有的 <p> 標(biāo)簽和它們的內(nèi)容。
只獲取內(nèi)容
你可以通過指定只需要 “字符串string”(它是 “單詞words” 的編程術(shù)語(yǔ))來排除打印標(biāo)簽。
def loopit():
for TAG in SOUP.find_all('p'):
print(TAG.string)
當(dāng)然,一旦你有了網(wǎng)頁(yè)的文本,你就可以用標(biāo)準(zhǔn)的 Python 字符串庫(kù)進(jìn)一步解析它。例如,你可以使用 len 和 split 函數(shù)獲得單詞個(gè)數(shù):
def loopit():
for TAG in SOUP.find_all('p'):
if TAG.string is not None:
print(len(TAG.string.split()))
這將打印每個(gè)段落元素中的字符串個(gè)數(shù),省略那些沒有任何字符串的段落。要獲得字符串總數(shù),你需要用到變量和一些基本數(shù)學(xué)知識(shí):
def loopit():
NUM = 0
for TAG in SOUP.find_all('p'):
if TAG.string is not None:
NUM = NUM + len(TAG.string.split())
print("Grand total is ", NUM)
Python 作業(yè)
你可以使用 Beautiful Soup 和 Python 提取更多信息。以下是有關(guān)如何改進(jìn)你的應(yīng)用程序的一些想法:
- 接受輸入,這樣你就可以在啟動(dòng)應(yīng)用程序時(shí),指定要下載和分析的 URL。
- 統(tǒng)計(jì)頁(yè)面上圖片(<img> 標(biāo)簽)的數(shù)量。
- 統(tǒng)計(jì)另一個(gè)標(biāo)簽中的圖片(<img> 標(biāo)簽)的數(shù)量(例如,僅出現(xiàn)在<main> div 中的圖片,或僅出現(xiàn)在</p> 標(biāo)簽之后的圖片)。