1. 程式人生 > >Python爬蟲利器:Beautiful Soup

Python爬蟲利器:Beautiful Soup

處理 previous tag 得到 navi log 簡單 文本節點 pen

Beautiful Soup 是一個可以從HTML或XML文件中提取數據的Python庫。使用它來處理HTML頁面就像JavaScript代碼操作HTML DOM樹一樣方便。官方中文文檔地址

1. 安裝

1.1 安裝 Beautiful Soup

Beautiful Soup3 目前已經停止維護,推薦使用 Beautiful Soup4,現在已經被移植到 bs4,導入的時候需要從 bs4 導入。安裝方法如下:

# 使用 pip 安裝
pip install beautifulsoup4

# 使用 easy_install 安裝
easy_install beautifulsoup4

1.2 安裝解析器 lxml

另外還需要安裝相應的解析器,lxml,html5lib 任選一個即可。

# 安裝 lxml
pip install lxml

# 安裝 html5lib
pip install html5lib

1.3 使用方法

安裝了 BeautifulSoup 以後可以導入使用。將一段文檔傳入BeautifulSoup 的構造方法,就能得到一個文檔的對象, 可以傳入一段字符串或一個文件句柄.

# 首先從 bs4 導入
from bs4 inport BeautifulSoup

# 使用解析器和html文檔可以初始化
soup = BeautifulSoup(open("index.html"), ‘lxml‘
) content = ‘<html>data</html>‘ soup = BeautifulSoup(content, ‘lxml‘)

文檔需要轉換成Unicode,並且HTML的實例都被轉換成Unicode編碼

2. BeautifulSoup中的對象

Beautiful Soup將復雜HTML文檔轉換成一個復雜的樹形結構,類似於瀏覽器中的DOM節點數,每個節點都是Python對象,所有對象可以歸納為4種: Tag , NavigableString , BeautifulSoup , Comment 。

2.1 tag 對象

tag 對象類似於一個標簽節點。與XML或HTML原生文檔中的標簽相同,如 body,div,a,span。tag 對象有很多方法和屬性。tag 對象的屬性可以像字典一樣進行增刪改查操作。

2.1.1 name 屬性

name 屬性表示 tag 的名稱。通過 .name 獲取。如果改變了tag的name,那將影響所有通過當前Beautiful Soup對象生成的HTML文檔。

2.1.2 attributes 屬性

一個tag可能有很多個屬性,使用 tag.attrs 獲取 tag 的所有節點屬性,可以對這些屬性進行增刪改查。獲取方法如下:

  • tag.attrs:獲取屬性列表
  • tag.attrs[1]:獲取屬性列表中的第2個屬性
  • tag.get(‘href‘):獲取 href 屬性
  • tag[‘href‘]:獲取 href 屬性

2.1.3 多值屬性

在HTML文檔中有典型像 class 一樣的有多個屬性值,這些多值屬性返回的值不是 string ,而是 list 。這些多值屬性的節點類型如下:

  • class
  • rel
  • rev
  • accept-charset
  • headers
  • accesskey

在XML文檔中沒有多值屬性

content = ‘<a href="index.html" class="button button-blue" data="1 2 3"></a>‘
soup = BeautifulSoup(content, ‘lxml‘)
tag = soup.a  # 獲取 a 標簽
tag.name  # 標簽名稱:a
tag.attrs  # 屬性列表:[‘href‘, ‘class‘, ‘data‘]
tag.get(‘href‘)  # 獲取href屬性:index.html
tag[‘class‘]  # 獲取class屬性為list:[button,button-blue]
tag[‘data‘]  # data屬性的值為string:1 2 3

字符串常被包含在tag內,Beautiful Soup用 NavigableString 類來包裝tag中的字符串。

  • 使用 tag.string 獲取 tag 內字符串,為NavigableString
  • 使用 unicode(tag.string) 轉換為一般Unicode字符串
  • tag 內字符串不能編輯
  • tag.string.replace_with(‘content‘) 替換 tag 內字符串
  • tag 內字符串不支持 .contents 或 .string 屬性或 find() 方法
  • 在Beautiful Soup之外使用 NavigableString 對象需要調用 unicode() 方法

2.3 BeautifulSoup 對象

BeautifulSoup 對象表示的是一個文檔的全部內容。大部分時候,可以把它當作 tag 對象。

因為 BeautifulSoup 對象並不是真正的HTML或XML的tag,BeautifulSoup 對象包含了一個值為 “[document]” 的特殊屬性 .name。

2.4 Comment 對象

Comment 對象是一個特殊類型的 NavigableString 對象,用來表示文檔的註釋部分。

markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>"
soup = BeautifulSoup(markup)
comment = soup.b.string
type(comment)  # <class ‘bs4.element.Comment‘>
print(soup.b.prettify())
# <b>
#  <!--Hey, buddy. Want to buy a used parser?-->
# </b>

3. 遍歷文檔樹

通過遍歷文檔樹,能夠從文檔中找到指定的內容。

3.1 子節點

一個 tag 可能包含多個字符串或者其他 tag,這些 tag 就是頂層的子節點,Beautiful Soup提供了許多操作和遍歷子節點的屬性。需要註意:

  • 字符串沒有子節點。
  • BeautifulSoup 對象本身一定會包含子節點

假設有以下幾種簡單的方式獲取子節點:

  • 使用tag名:獲取第一個直接子節點:soup.div.p
  • 使用contents屬性:獲取所有直接子節點列表:soup.div.contents
  • 使用children屬性: 對子節點進行循環:soup.div.children
  • 使用descendants屬性:所有tag的子孫節點進行遞歸循環:soup.div.descendants
  • 使用string屬性:獲取只有一個string子節點tag的子節點:p.string
  • 使用strings屬性:循環有多個string子節點的情況:div.strings
div_html = 
<div>
    <p>uu</p>
    <p>sa</p>
    <p>
        <a>ma</a>
        -->
    </p>
</div>‘
soup =  BeautifulSoup(div_html), ‘lxml‘)
div = soup.div # 獲取 div 節點
div.p  # <p>uu</p>

div.contents
# [<p>uu</p>, <p>sa</p>, <p><a>ma</a>--></p>]
div.contents[0] # <p>uu</p>

for child in div.children:
    print(child)
# <p>uu</p><p>sa</p><p><a>ma</a>--></p>

for child in div.descendants:
    pring(child)

3.2 父節點

每個tag或字符串都有父節點,即每個節點都被包含在tag中,通過 .parent 屬性來獲取某個元素的父節點: p.parent,通過元素的 .parents 屬性可以遞歸得到元素的所有父輩節點。

soup =  BeautifulSoup(div_html), ‘lxml‘)  # 使用3.1中定義的 div_html
div = soup.div # 獲取 div 節點
sa = div.a.string  # 第一個 a 節點的string

sa.parent  # a 節點
sa.parent.parent # div 節點
for parent in sa.parents:
    print(parent)
# a
# div
# [document]
# None

3.3 兄弟節點

兄弟節點就是具有相同父節點的同義詞節點。如3.1中定義的 div_html 中的3個p標簽互相為兄弟節點。使用下面的節點tag屬性訪問兄弟節點

  • next_sibling:當前節點的下一個兄弟節點
  • previous_sibling:當前節點的上一個兄弟節點
  • next_siblings:當前節點之後的所有兄弟節點
  • previous_siblings:當前節點之前的所有兄弟節點

4. 搜索文檔樹

搜索功能可以說是寫爬蟲過程中必用的功能,用來查找指定的節點,Beautiful Soup定義了很多搜索方法,這些搜索方法的參數和用法都很類似,查詢功能非常強大,下面主要針對 find_all 方法說明。

4.1 過濾器

過濾器是使用搜索方法過程中的匹配規則,即參數的可能取值。過濾器可以為下面幾種形式:

  • 字符串:find_all(‘div‘)
  • 列表:find_all([‘div‘, ‘span‘])
  • 正則表達式:find_all(re.compile(‘[a-z]{1,3}‘))
  • True:匹配任意非字符串子節點
  • 方法:相當於匹配的回調函數,該方法返回 True 表示匹配
content = ‘<nav><a>a_1</a><a>a_2</a>string</nav>‘
soup = BeautifulSoup(content), ‘lxml‘)
nav_node = soup.nav 
# <nav><a>a_1</a><a>a_2</a>string</nav>
nav_node.find_all(True)  # 不匹配 string
# [‘<a>a_1</a>‘, ‘<a>a_2</a>‘]
def has_class_but_no_id(tag):  # 定義匹配函數
    return tag.has_attr(‘class‘) and not tag.has_attr(‘id‘)
soup.find_all(has_class_but_no_id)  # 返回有class屬性沒有id屬性的節點

4.2 find_all() find() 方法

find_all 方法返回匹配搜索的所有節點的列表或者空,而 find 方法直接返回第一個匹配搜索的結果。詳細的定義如下:

find_all(name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
find_find(name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)

各參數含義如下:

  • name:匹配 tag 標簽名
  • attrs:匹配屬性名:find_all(href=‘index.html‘)
  • text:匹配文檔中的字符串內容
  • limit:指定匹配的結果集數量
  • recursive:默認True,False表示只搜索直接子節點

以上的參數值都可以是 4.1 中說明的任意一種過濾器值。另外還需要註意以下幾點:

  • attrs 參數為字典類型,可以是多個屬性條件組合
  • 單獨使用 class 屬性時,應該使用 class_
  • class 屬性為多值屬性,會分別搜索每個 CSS 類名
  • 按照CSS類目完全匹配時,必須順序相同
# 搜索所有 div 標簽
soup.find_all(‘div‘)
# 搜索所有具有id屬性並且id屬性值為 link1 或者 link2 的節點
soup.find_all(id=[‘link1‘, ‘link2‘])
# 搜索所有 class 屬性包含 button 的節點
soup.find_all(class_=‘button‘)
# 搜索所有匹配給定正則表達式內容的 p 標簽
soup.find_all(‘p‘, text=re.compile(‘game‘))
# 搜索具有 button 類,並且具有值為 link1 的 href 屬性的 a 標簽
soup.find_all(‘a‘, {‘classl‘: ‘button‘, ‘href‘: ‘link1‘})
# 只搜索一個直接子節點的 a 標簽
soup.find_all(‘a‘, limit=1, recursive=False)

4.3 其他方法

其他的搜索方法參數和 find_all 和 find 類似,它們成對出現,分別返回結果列表和第一個匹配的結果,只是搜索文檔的範圍不一樣,下面列舉一些常用的:

  • find_parents() 和 find_parent():只從當前節點的父節點中搜索
  • find_next_siblings() 和 find_next_sibling():只從當前節點的後面的兄弟節點搜索
  • find_previous_siblings() 和 find_previous_sibling():只從當前節點的前面的兄弟節點搜索
  • find_all_next() 和 find_next():從當前節點之後的節點搜索
  • find_all_previous() 和 find_previous():從當前節點之前的節點搜索

4.4 CSS選擇器

Beautiful Soup支持大部分的CSS選擇器,使用 select() 方法來搜索。如id選擇器,標簽選擇器,屬性選擇器設置組合選擇器。如:

  • soup.select(‘body a‘)
  • soup.select(‘#top‘)
  • soup.select(‘div > span‘)
  • soup.select(‘.button + img‘)

5. 修改文檔樹

當我們得到搜索結果以後,希望搜索結果中的某些節點不參與之後的搜索,那麽就需要把這些節點刪除掉,這就需要對文檔樹修改。修改的方法如下:

  • append():向當前tag中添加內容(string)
  • new_tag(),new_string():添加一段文本
  • insert(index, content):在 index 位置插入屬性
  • insert_before() 和 insert_after():在當前節點前後插入內容
  • clear():移除當前節點內容
  • extract();將當前tag移除文檔樹,並作為結果返回
  • decompose():將當前節點移除文檔樹並完全銷毀
  • replace_with():移除文檔樹中的某段內容,並用新tag或文本節點替代它
  • wrap():對指定的tag元素進行包裝,並返回包裝後的結果

6. 輸出

有時候,需要對查到的或者修改過的文檔樹展示或者保存。
prettify() 方法將Beautiful Soup的文檔樹格式化後以Unicode編碼輸出,每個XML/HTML標簽都獨占一行。BeautifulSoup 對象和它的tag節點都可以調用 prettify() 方法。

可以對一個 BeautifulSoup 對象或 Tag 對象使用Python的 unicode() 或 str() 方法,從而對輸出結果進行壓縮。

原文地址:http://uusama.com/467.html

Python爬蟲利器:Beautiful Soup