Beautiful Soup 是一個可以從 HTML 或 XML 文件中提取數據的 Python 庫,它提供了一些簡單的操作方式來幫助你處理文檔導航,查找,修改文檔等繁瑣的工作。因為使用簡單,所以 Beautiful Soup 會幫你節省不少的工作時間。
上一篇文章我們介紹了如何使用 Beautiful Soup 來遍歷文檔中的節點,這片文章我們繼續血學習如何使用 Beautiful Soup 指定文檔中搜索到你想要的內容。
Beautiful Soup 搜索文檔同樣為了故事的順利發展,我們繼續使用之前的 HTML 文本,下文的所有例子都是基於這段文本的。
html_doc = """<html><head><title>index</title></head><body><p><b>首頁</b></p><p>我常用的網站<a href="https://www.google.com" id="google">Google</a><a href="https://www.baidu.com" id="baidu">Baidu</a><a href="https://cn.bing.com" id="bing">Bing</a></p><div><!--這是注釋內容--></div><p>...</p><p>...</p></body>"""soup = BeautifulSoup(html_doc, "lxml")過濾器正式講解搜索文檔之前,我們有必要了解下 Beautiful Soup 的過濾器,這些過濾器在整個搜索的 API 中都有所體現,他們可以被用在 TAG 的 name 中,屬性中,字符串中或他們的混合中。聽起來有點繞是麼,看幾個例子就懂了。
1、根據 TAG 的 name 來查找標籤,下面的例子會查找文檔中的所有 b 標籤。同時要注意統一傳入 Unicode 編碼以避免 Beautiful Soup 解析編碼出錯。
# demo 1tags = soup.find_all('b')print(tags)
#輸出結果[<b>首頁</b>]2、如果傳入正則表達式作為參數,那麼 Beautiful Soup 會通過正則表達式的 match() 來匹配內容。
# demo 2import refor tag in soup.find_all(re.compile("^b")): print(tag.name)
#輸出結果bodyb3、如果傳入列表參數,那麼 Beautiful Soup 會將與列表中任一一個元素匹配的內容返回。
# demo 3for tag in soup.find_all(['a', 'b']): print(tag)
#輸出結果<b>首頁</b><a href="https://www.google.com" id="google">Google</a><a href="https://www.baidu.com" id="baidu">Baidu</a><a href="https://cn.bing.com" id="bing">Bing</a>4、True 可以匹配任何值,下面的例子是查找所有的 TAG 但不會返回字符串。
# demo 4for tag in soup.find_all(True): print(tag.name, end=', ') #輸出結果html, head, title, body, p, b, p, a, a, a, div, p, p,5、方法。我們可以定義一個方法,該方法只接受一個參數,若該方法返回 True 則表示當前元素匹配並且被找到,返回 False 意味著沒找到。下面的例子展示了查找所有同時包含 class 屬性和 id 屬性的節點。
# demo 5def has_id_class(tag): return tag.has_attr('id') and tag.has_attr('class')
tags = soup.find_all(has_id_class)for tag in tags: print(tag) #輸出結果<a href="https://www.google.com" id="google">Google</a><a href="https://www.baidu.com" id="baidu">Baidu</a><a href="https://cn.bing.com" id="bing">Bing</a>大部分情況字符串過濾器就可以滿足我們的需求,外加這個神奇的方法過濾器,我們就可以實現各種自定義需求了。
find_all() 函數該函數搜索當前節點下的所有子節點,其籤名如下find_all( name , attrs , recursive , text , **kwargs )。我們可以傳入指定 TAG 的 name 來查找節點,上面已經舉過例子了,這裡不在贅述。我們來看幾個其他的用法。
1、如果我們傳入 find_all() 函數不是搜索內置的參數名,那麼搜索是就會將該參數對應到屬性上去。下文的例子表示查找 id 為 google 的節點。
搜索指定名字的屬性時可以使用的參數值包括:字符串,正則表達式,列表,True。也就是我們上文介紹過的過濾器。
# demo 6tags = soup.find_all(id='google')print(tags[0]['href'])
for tag in soup.find_all(id=True): # 查找所有包含 id 屬性的 TAG print(tag['href'])
#輸出結果https://www.google.comhttps://www.google.comhttps://www.baidu.comhttps://cn.bing.com2、按照 CSS 類名搜索,但是鏢師 CSS 的關鍵字 class 在 Python 中是內置關鍵字,從 Beautiful Soup 4.1.1 版本開始,可以通過 class_ 參數搜索有指定 CSS 類名的 TAG:
class_ 參數同樣接受不同類型的過濾器:字符串,正則表達式,方法,True。
# demo 7tags = soup.find_all("a", class_="website")for tag in tags: print(tag['href'])
def has_seven_characters(css_class): return css_class is not None and len(css_class) == 7
for tag in soup.find_all(class_=has_seven_characters): print(tag['id'])
#輸出結果https://www.google.comhttps://www.baidu.comhttps://cn.bing.comgooglebaidubing同時,因為 CSS 可以有多個值,所以我們可以分別搜索 CSS 中的每個值。
# demo 8css_soup = BeautifulSoup('<p></p>', 'lxml')tags = css_soup.find_all("p", class_="strikeout")print(tags)
#輸出結果[<p></p>]3、不僅可以按照標籤和 CSS 來搜索整個文檔,還可以使用 text 來按照內容來搜索。同時 text 還可以配合其他屬性一起來完成搜索任務。
# demo 9tags = soup.find_all(text="Google")print("google : ", tags)
tags = soup.find_all(text=["Baidu", "Bing"])print("baidu & bing : ", tags)
tags = soup.find_all('a', text="Google")print("a[text=google] : ", tags)
#輸出結果google : ['Google']baidu & bing : ['Baidu', 'Bing']a[text=google] : [<a href="https://www.google.com" id="google">Google</a>]4、限制返回數量
有時候文檔樹過於龐大,我們不想查查找整棵樹,只想查找指定數量的節點,或者只想查找子節點,而不想查找孫子節點,指定 limit 或者 recursive 參數即可。
# demo 10tag = soup.find_all("a", limit=1)print(tag)
tags = soup.find_all("p", recursive=False)print(tags)
#輸出結果[<a href="https://www.google.com" id="google">Google</a>][]因為該對象的兒子節點沒有 p 標籤,所以返回的是空列表。
find() 函數該函數只會返回一個結果,與 find_all(some_args, limit=1) 是等價的,唯一的區別就是該函數直接返回結果,而 find_all() 函數返回包含一個結果的列表。另外 find_all() 方法沒有找到目標是返回空列表, find() 方法找不到目標時,返回 None。除此之外使用上沒有其他差別。
其他函數除了 find_all() 和 find() 外,Beautiful Soup 中還有 10 個用於搜索的 API,其中中五個用的是與 find_all() 相同的搜索參數,另外 5 個與 find() 方法的搜索參數類似,區別僅是它們搜索文檔的範圍不同。
find_parents() 和 find_parent() 用來搜索當前節點的父節點。
find_next_siblings() 和 find_next_sibling() 對在當前節點後面解析的所有兄弟節點進行迭代。
find_previous_siblings() 和 find_previous_sibling() 對在當前節點前面解析的所有兄弟節點進行迭代。
find_all_next() 和 find_next() 對當前節點之後的 TAG 和字符串進行迭代。
find_all_previous() 和 find_previous() 對當前節點之前的 TAG 和字符串進行迭代。
以上五組函數的區別僅僅是前者返回一個所有符合搜索條件的節點列表,而後者只返回第一個符合搜索條件的節點。
因為這 10 個 API 的使用和 find_all() 與 find() 大同小異,所有i這裡不在舉例,讀者可以自己探索。
CSS 選擇器在 Tag 或 BeautifulSoup 對象的 .select() 方法中傳入字符串參數即可使用 CSS 選擇器的語法找到 TAG。
1、通過某個標籤逐層查找。
# demo 11tags = soup.select("body a")for tag in tags: print(tag['href'])
#輸出結果https://www.google.comhttps://www.baidu.comhttps://cn.bing.com2、查找某個標籤下的直接子標籤
# demo 12tags = soup.select("p > a")print(tags)
tags = soup.select("p > #google")print(tags)
#輸出結果[<a href="https://www.google.com" id="google">Google</a>, <a href="https://www.baidu.com" id="baidu">Baidu</a>, <a href="https://cn.bing.com" id="bing">Bing</a>][<a href="https://www.google.com" id="google">Google</a>]3、通過 CSS 類名直接查找
# demo 13tags = soup.select(".website")for tag in tags: print(tag.string)
#輸出結果GoogleBaiduBing4、通過標籤的 id 屬性查找
# demo 14tags = soup.select("#google")print(tags)
#輸出結果[<a href="https://www.google.com" id="google">Google</a>]5、通過屬性的值來查找
# demo 15tags = soup.select('a[href="https://cn.bing.com"]')print(tags)
#輸出結果[<a href="https://cn.bing.com" id="bing">Bing</a>]Beautiful Soup 總結本章節介紹了 Beautiful Soup 關於文檔搜索的相關操作,熟練掌握這些 API 的操作可以讓我們更快更好找到我們想要定位的節點,不要看到這麼多函數嚇怕了,其實我們只需要熟練掌握 find_all() 和 find() 兩個函數即可,其餘 API 的使用都大同小異,稍加練習即可快速上手。
代碼地址示例代碼:https://github.com/JustDoPython/python-100-day/tree/master/day-066
參考內容https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#