BeautifulSoup模塊

Beautiful Soup是一個Python的HTML解析框架,我們可以利用它方便的處理HTML和XML文檔。Beautiful Soup有3和4兩個版本,目前3已經停止開發。所以我們當然還是學習最新的Beautiful Soup 4.如果需要詳細文檔的話可以參考Beautiful Soup中文文檔,這是難得的不是機翻的文檔。

解析文檔

獲取文檔

Beautiful Soup只是一個HTML解析庫,所以我們如果想解析網上的內容,第一件事情就是把它下載下來。對于不同的網站,可能會對請求進行過濾。糗事百科的網站就對沒有UA的請求直接拒絕掉。所以如果我們要爬這樣的網站,首先需要把請求偽裝成瀏覽器的樣子。具體網站具體分析,經過我測試,糗事百科只要設置了UA就可以爬到內容,對于其他網站,你需要測試一下才能確定什么設置能管用。

有了Request對象還不行,還需要實際發起請求才行。下面代碼的最后一句就使用了Python3的urllib庫發起了一個請求。urlopen(req)方法返回的是Reponse對象,我們調用它的read()函數獲取整個結果字符串。最后調用decode('utf-8')方法將它解碼為最終結果,如果不調用這一步,漢字等非ASCII字符就會變成\xXXX這樣的轉義字符。

import urllib.request as request

user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
headers = {'User-Agent': user_agent}
req = request.Request('http://www.qiushibaike.com/', headers=headers)

page = request.urlopen(req).read().decode('utf-8')

查詢和遍歷方法

有了文檔字符串,我們就可以開始解析文檔了。第一步是建立BeautifulSoup對象,這個對象在bs4模塊中。注意在建立對象的時候可以額外指定一個參數,作為實際的HTML解析器。解析器的值可以指定html.parser,這是內置的HTML解析器。更好的選擇是使用下面的lxml解析器,不過它需要額外安裝一下,我們使用pip install lxml就可以安裝。

import bs4

soup = bs4.BeautifulSoup(page, "lxml")

有了BeautifulSoup對象,我們就可以開始解析了。首先先來介紹一下BeautifulSoup的對象種類,常用的有標簽(bs4.element.Tag)以及文本(bs4.element.NavigableString)。還有注釋等對象,不過不太常用,所以就不介紹了。在標簽對象上,我們可以調用一些查找方法例如find_all等等,還有一些屬性返回標簽的父節點、兄弟節點、直接子節點、所有子節點等。在文本對象上,我們可以調用.string屬性獲取具體文本。

然后來說說BeautifulSoup的遍歷方法。基本所有操作都需要通過BeautifulSoup對象來使用。使用方式主要有兩種:

  • 一是直接引用屬性,就是soup.title這樣的,會返回第一個符合條件的節點;
  • 二是通過查找方法例如find_all這樣的,傳入查詢條件來查找結果。

再來說說查詢條件。查詢條件可以是:

  • 字符串,會返回對應名稱的節點;
  • 正則表達式,按照正則表達式匹配;
  • 列表,會返回所有匹配列表元素的節點;
  • 真值True,會返回所有標簽節點,不會返回字符節點;
  • 方法,我們可以編寫一個方法,按照自己的規則過濾,然后將該方法作為查詢條件。

實際例子

爬取糗事百科段子

首先打開糗事百科網站,按F12打開開發人員工具,然后在旁邊點擊分離按鈕把它變成獨立窗口,然后切到元素標簽并最大化窗口。然后點擊那個鼠標按鈕,再返回糗事百科頁面,并點擊一個段子,這樣就可以查看段子在HTML文檔的什么位置了。

HTML結構

首先分析一下HTML代碼,然后我們就可以查找所需的內容了。這里需要說明一下,查詢方法返回的是結果集,對結果集遍歷可以得到標簽或者文本對象。如果調用標簽對象的.contents,會返回一個列表,列表內是標簽、文本或注釋對象。動態語言的優勢就是使用靈活,缺點就是沒有代碼提示。雖然總共代碼沒幾行,但是還是花了我一番功夫。

divs = soup.find_all('div', class_='article block untagged mb15')
for div in divs:
    links = div.find_all('a', href=re.compile(r'/article/\d*'), class_='contentHerf')
    for link in links:
        contents = link.span.contents
        contents = [i for i in contents if not isinstance(i, bs4.element.Tag)]
        print(contents)

上面的代碼會輸出首頁的所有段子。這樣我們便實現了半個爬蟲。為什么是半個呢?因為一個完整的爬蟲可以爬取多個頁面,為了簡便這里只爬首頁,所以只能算半個爬蟲。不過如果你想爬取多個頁面,代碼稍加修改即可實現。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容