天天看點

Python每日一庫之Beautiful Soup

作者:喜歡編碼的社畜
Python每日一庫之Beautiful Soup

Beautiful Soup4是一個 Python 庫,用于從 HTML 和 XML 檔案中提取資料。它是一個工具箱,通過解析文檔為使用者提供需要抓取的資料,Beautiful Soup自動将輸入文檔轉換為Unicode編碼,輸出文檔轉換為utf-8編碼。你不需要考慮編碼方式,除非文檔沒有指定一個編碼方式,這時,Beautiful Soup就不能自動識别編碼方式了。

BeautifulSoup安裝

使用pip來安裝BeautifulSoup

pip install bs4            

另外要安裝解析器,下清單格列出一些常用的解析器。

Python每日一庫之Beautiful Soup
解析器 使用方法 優點 缺點
Python内置解析器html BeautifulSoup(doc,"html.parser") Python内置的标準庫,執行速度中等,文檔容錯能力強 中文文檔容錯能力差
lxml HTML解析器 BeautifulSoup(doc,"lxml") 執行速度快,文檔容錯能力強 需要依賴C語言的庫
lxml XML解析器 BeautifulSoup(doc,"xml") 執行速度快,唯一支援XML的解析器 需要依賴C語言的庫
html5lib BeautifulSoup(doc,"html5lib") 以浏覽器的方式解析文檔可以生成HTML5格式的文檔 速度慢

使用BeautifulSoup及四大對象

建立BeautifulSoup對象

from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"

content = requests.get(url).content
soup = BeautifulSoup(content)

print(soup.prettify())  // 格式化輸出

print(soup.get_text()) // 擷取網頁所有的文字内容
           

BeautifulSoup四大對象

Beautiful Soup 将複雜 HTML 文檔轉換成一個複雜的樹形結構,每個節點都是 Python 對象,所有對象可以歸納為 4 種。

  • Tag:HTML中的标簽,簡單來說就是html标簽。
  • NavigableString:簡單來說就是标簽裡面的内容,它的類型是一個NavigableString,翻譯過來叫可以周遊的字元串。
  • BeautifulSoup:BeautifulSoup對象表示的是一個文檔的全部内容,大部分時候,可以把它當作Tag對象,是一個特殊的Tag,我們可以分别擷取它的類型、名稱、以及屬性
  • Comment:一個特殊類型的NavigableString對象,其實輸出的内容不包括注釋符号

Tag對象示例

from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"

content = requests.get(url).content
soup = BeautifulSoup(content)

print(soup.title)
print(soup.a)
print(soup.p)
           

運作輸出如下圖所示,但是發現好像這個網頁不止一個a标簽跟p标簽,是因為它查找的是在所有内容中的第一個符合要求的标簽,要是想得到所有符合要求的标簽,後面會介紹find_all函數。

Python每日一庫之Beautiful Soup

在Tag對象中有兩個重要的屬性,name和attrs。

from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.a.attrs)           

運作輸出如下圖所示,name輸出的是标簽的本身,attrs輸出的是一個字典的類型,如果我們需要得到某個标簽的某個屬性可以使用字典一些方法去擷取比如get方法,print(soup.p.get("class"))或者直接使用print(soup.p["class"])

Python每日一庫之Beautiful Soup

NavigableString代碼示例

from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.a.string)           

運作輸出如下圖,可以NavigableString類型的string方法輕松擷取到了标簽裡面的内容。

Python每日一庫之Beautiful Soup

BeautifulSoup代碼示例

from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"

content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.name)
print(soup.attrs)
           

運作輸出如下圖所示

Python每日一庫之Beautiful Soup

Comment代碼示例

from bs4 import BeautifulSoup
htmlText = '#<a class="sister" href="http://example.com/elsie" id="link1"><!-- Comment --></a>'
soup = BeautifulSoup(htmlText)
print(soup.a.string)           

運作輸出如下,a 标簽裡的内容實際上是注釋,但是如果利用 .string方法來輸出它的内容,發現它已經把注釋符号去掉了,是以這可能會給帶來不必要的麻煩。

文檔樹周遊

  • 直接子節點

tag裡面的content屬性可以将tag的子節點以清單的形式傳回。通過周遊content.傳回的清單來擷取每一個子節點或者直接使用tag的children方法來擷取。

from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.head.contents)
for child in soup.head.contents:
    print(child)

for child in soup.head.children:
    print(child)           

運作輸出結果如下圖所示

Python每日一庫之Beautiful Soup
  • 所有子孫節點

tag裡面的.descendants 屬性可以對所有tag的子孫節點進行遞歸循環,和 children類似,我們也需要周遊擷取其中的内容。

from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
for child in soup.descendants:
     print(child)           

運作結果輸出如下圖所示

Python每日一庫之Beautiful Soup
  • 節點内容

使用.string方法來擷取内容,如果一個标簽裡面沒有标簽了,那麼 .string 就會傳回标簽裡面的内容。如果标簽裡面隻有唯一的一個标簽了,那麼 .string 也會傳回最裡面的内容,如果标簽裡面沒有内容則傳回None

from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.a.string)
print(soup.title.string)           

運作結果輸出如下圖所示

Python每日一庫之Beautiful Soup
  • 多個内容

使用strippend_strings 屬性來擷取多個内容還可以出除多餘的空白字元,需要使用周遊來擷取,

from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
for child in soup.stripped_strings:
    print(child)           

運作結果輸出如下圖所示

Python每日一庫之Beautiful Soup
  • 父節點

通過元素的 .parents 屬性可以遞歸得到元素的所有父輩節點

from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"html.parser")
parentObject = soup.head.title

for parent in parentObject.parent:
    print(parent.name)           

運作結果輸出如下圖所示

Python每日一庫之Beautiful Soup

還有一些節點就不舉例,跟其它擷取節點一樣也是需要周遊,而且使用的場景不同,兄弟節點使用.next_siblings或者.previous_sibling方法,前後節點使用.next_element或者.previous_element方法。

搜尋文檔樹

find_all(name,attrs,recursive,text,**kwargs),find_all()方法用于搜尋目前tag的所有tag子節點,并判斷是否符合過濾條件。

name 參數

name參數可以查找所有名字為name的tag,字元串對象會被自動忽略掉

  • 傳字元串

最簡單的過濾器是字元串,在搜尋方法中傳入一個字元串參數,beautifulsoup會查找與字元串完整比對的内容,下面的例子用于查找文檔中的所有a标簽

from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")

print(soup.find_all("a"))
           
  • 傳正規表達式

如果傳入正規表達式作為參數,beautiful soup會通過正規表達式的match()來比對内容,下面例子中找出所有以b開頭的标簽,這表示b開頭标簽都應該被找到,如果都正規表達式不熟悉的可以看我之前寫關于正則表示式的文章:https://www.toutiao.com/article/7140941215431819783/?log_from=4bb8705803d45_1663051238064

from bs4 import BeautifulSoup
import requests
import re
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")

for tag in soup.find_all(re.compile('^b')):
    print(tag.name)
           

運作結果如下圖所示

Python每日一庫之Beautiful Soup
  • 傳清單

如果傳入清單參數,Beautiful Soup會将與清單中任一進制素比對的内容傳回.下面代碼找到文檔中所有标簽和标簽

from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")

print(soup.find_all(["a", "p"]))           

運作結果如下圖所示

Python每日一庫之Beautiful Soup
  • 傳True

true 可以比對任何值,下面代碼查找到所有的tag,但是不會傳回字元串節點

from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")

for tag in soup.find_all(True):
    print(tag.name)
           

運作結果如下圖所示

Python每日一庫之Beautiful Soup
  • 傳函數

如果沒有合适過濾器,那麼還可以定義一個函數,函數隻接受一個元素參數 [4] ,如果這個方法傳回 True 表示目前元素比對并且被找到,如果不是則傳回 False

from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")


def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

print(soup.find_all(has_class_but_no_id))           

輸出結果如下圖所示

Python每日一庫之Beautiful Soup
  • keyword 參數

注意:如果一個指定名字的參數不是搜尋内置的參數名,搜尋時會把該參數當作指定名字tag的屬性來搜尋,如果包含一個名字為id的參數,Beautifulsoup會搜尋每個tag的'id'值

import re

from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.find_all(id='lg'))
print(soup.find_all(href=re.compile("hao123")))           

運作結果如下圖所示

Python每日一庫之Beautiful Soup
  1. find(name , attrs , recursive , text , **kwargs ), 它與 find_all() 方法唯一的差別是 find_all() 方法的傳回結果是值包含一個元素的清單,而 find() 方法直接傳回結果。
Python每日一庫之Beautiful Soup

CSS選擇器

在使用BeautifulSoup中常用的有5中css選擇器方法,用到的方法是 soup.select(),傳回類型是清單

  • 通過标簽名查找
from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select("title"))
           

運作結果如下圖所示

Python每日一庫之Beautiful Soup
  • 通過CSS類名查找
from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select(".mnav"))           

運作結果如下圖所示

Python每日一庫之Beautiful Soup
  • 通過ID來查找
from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select("#lg"))           

運作結果如下圖所示

Python每日一庫之Beautiful Soup
  • 組合查找

組合查找有點類似前端CSS選擇器中的組合選擇器,組合查找還可以使用子代選擇器。

from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select('div #lg'))

print(soup.select('div > a'))           

運作結果如下圖所示

Python每日一庫之Beautiful Soup
  • 通過CSS屬性查找

使用屬性需要用中括号括起來,注意屬性和标簽屬于同一節點,是以中間不能加空格,否則會無法比對到。

from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select('a[class="mnav"]'))
           
Python每日一庫之Beautiful Soup

不同節點使用屬性查找

from bs4 import BeautifulSoup
import requests

url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select('span input[class="bg s_btn"]'))
           

運作結果如下圖所示

Python每日一庫之Beautiful Soup

修改文檔樹

Beautiful Soup的強項是文檔樹的搜尋,但同時也可以友善的修改文檔樹

  • 修改tag的名稱和屬性
from bs4 import BeautifulSoup
import requests

soup = BeautifulSoup('<b class="boldest">Extremely bold</b>',"lxml")

tag = soup.b

tag.name = "newtag"
tag['class'] = 'newclass'
tag['id'] = 1
print(tag)

del tag['class']
print(tag)           

運作結果如下圖所示

Python每日一庫之Beautiful Soup
  • 修改标簽内容

給tag的 .string 屬性指派,就相當于用目前的内容替代了原來的内容,如果目前的tag包含了其它tag,那麼給它的 .string 屬性指派會覆寫掉原有的所有内容包括子tag

from bs4 import BeautifulSoup
import requests

markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
soup = BeautifulSoup(markup,"lxml")

tag = soup.a
tag.string = "New link text."
print(tag)           

運作結果如下圖所示

Python每日一庫之Beautiful Soup
  • 在tag中添加内容

Tag.append() 方法可以在tag中添加内容

from bs4 import BeautifulSoup
import requests

soup = BeautifulSoup("<a>Foo</a>","lxml")
soup.a.append("Bar")
print(soup)
print(soup.a.contents)           

運作結果如下圖所示

Python每日一庫之Beautiful Soup

總結

本篇内容比較多,把 Beautiful Soup 的方法進行了大部分整理和總結,但是還不夠完整隻是列出一些常用的,如果需要完整的可以檢視Beautiful Soup 官網的文檔,希望對大家有幫助,掌握了 Beautiful Soup,一定會給你在資料爬取帶來友善,下一期我将分享Python pands庫,如果對我的文章感興趣可以關注我,如果有想了解的Python庫也可以在評論留言,我将采納你們的意見寫一篇文章來分享給大家。