Python爬蟲利器:Beautiful Soup的使用(二)

上一篇文章介紹了 BeautifulSoup 的安裝以及基本對(duì)象類型。

本次介紹使用 bs4 對(duì) HTML 文檔樹的遍歷。

先把本文用到的例子貼上:

str = """
<!DOCTYPE html>
<html>
<head><title>bs4 test</title></head>
<body>
    <h1>bs4 test</h1>
    <div>
        <ul>
            <li><a>PHP</a></li>
            <li><a>Python</a></li>
            <li><a>Golang</a></li>
        </ul>
    </div>
    <p><span>a</span><i>b</i><em></em></p>
</body>
</html>
"""

文檔樹的遍歷:

文檔樹的遍歷包括以下四部分:

  1. 子節(jié)點(diǎn)
  2. 父節(jié)點(diǎn)
  3. 兄弟節(jié)點(diǎn)
  4. 回退和前進(jìn)

一、子節(jié)點(diǎn)

一個(gè)標(biāo)簽可能包含多個(gè)字符串或者其他標(biāo)簽,這些標(biāo)簽都屬于子節(jié)點(diǎn)。要獲取子節(jié)點(diǎn),首先需要得到一個(gè) Tag 對(duì)象:

獲取一個(gè) Tag 對(duì)象最簡單的方式是用 bs4 對(duì)象點(diǎn)上要獲取的標(biāo)簽的名字,同時(shí)支持鏈?zhǔn)秸{(diào)用。

bs4 = BeautifulSoup(str, "lxml")
div_tag = bs4.div
ul_tag = bs4.div.ul

.contents :

tag 對(duì)象的 .contents 屬性可以將 tag 的子節(jié)點(diǎn)以列表的方式輸出,不包含孫節(jié)點(diǎn):

ul_tag.contents
# ['\n', <li><a>PHP</a></li>, '\n', <li><a>Python</a></li>, '\n', <li><a>Golang</a></li>, '\n']

字符串沒有 .contents 屬性,因?yàn)樽址疀]有子節(jié)點(diǎn)。

.children:

.children 生成器,可以對(duì) tag 的直接子節(jié)點(diǎn)進(jìn)行循環(huán):

for child in ul_tag.children:
    print(child)
# <li><a>PHP</a></li> <li><a>Python</a></li> <li><a>Golang</a></li>

.descendants:

.descendants 屬性可以對(duì)所有 tag 的子孫節(jié)點(diǎn)進(jìn)行遞歸循環(huán):

for child in ul_tag.descendants:
    print(child)

.string:

如果 tag 只有一個(gè) NavigableString 類型子節(jié)點(diǎn),那么這個(gè) tag 可以使用 .string 得到子節(jié)點(diǎn)。

title_tag = bs4.title 
print(title_tag.string)  # bs4 test

如果一個(gè) tag 僅有一個(gè)子節(jié)點(diǎn),那么這個(gè) tag 也可以使用 .string 方法,輸出結(jié)果與當(dāng)前唯一子節(jié)點(diǎn)(也就是 title 節(jié)點(diǎn))的 .string 結(jié)果相同。

head_tag = bs4.head
print(head_tag.string)  # bs4 test

如果 tag 包含了多個(gè)子節(jié)點(diǎn),tag 就無法確定 .string 方法應(yīng)該調(diào)用哪個(gè)子節(jié)點(diǎn)的內(nèi)容,所以輸出結(jié)果是 None:

print(div_tag.string) # None

.strings 和 stripped_strings:

對(duì)于上邊 tag 包含了多個(gè)子節(jié)點(diǎn)的問題,可以使用 .strings 來循環(huán)獲取:

for str in div_tag.strings:
    print(str)
# PHP   Python   Golang

.stripped_strings 可以去除多余空白內(nèi)容。

二、父節(jié)點(diǎn)

.parent:

.parent 屬性來獲取某個(gè)標(biāo)簽或字符串的父節(jié)點(diǎn),比如:

print(title_tag.parent) # <head><title>bs4 test</title></head>
h1_tag = bs4.h1
print(h1_tag.string.parent) # <h1>bs4 test</h1>

.parents:

.parents 屬性可以遞歸得到元素的所有父輩節(jié)點(diǎn)。

for p in h1_tag.parents:
    print(p.name)
# body   html   [document]

三、兄弟節(jié)點(diǎn)

首先先看一下例子中的這一行:

#<p><span>a</span><i>b</i><em>c</em></p>

p_tag = bs4.p
print(p_tag.prettify())
#<p>
# <span>
#  a
# </span>
# <i>
#  b
# </i>
# <em>
#  c
# </em>
#</p>


<span><i><em>都是<p>的子節(jié)點(diǎn),所以這三個(gè)可以被稱為兄弟節(jié)點(diǎn)。

.next_sibling 和 .previous_sibling:

通過以上兩個(gè)屬性可以查詢兄弟節(jié)點(diǎn)。

print(p_tag.i.next_sibling) # <em>c</em>
print(p_tag.i.previous_sibling) # <span>a</span>

要注意的點(diǎn):

在這里<span>沒有 previous_sibling 屬性,因?yàn)樗峭?jí)節(jié)點(diǎn)中的第一個(gè)。相反,<em>沒有 next_sibling 屬性。

字符串“a,b,c”不是兄弟節(jié)點(diǎn),因?yàn)樗鼈兊母腹?jié)點(diǎn)不同。

由于我們上邊的例子是寫的一行,在實(shí)際中 .next_sibling 和 .previous_sibling 屬性通常是字符串或空白。

如果示例是如下方式則 .next_sibling 和 .previous_sibling 獲取到的是空白。

<p>
    <span>a</span>
    <i>b</i>
    <em>c</em>
</p>

.next_siblings 和 .previous_siblings:

.next_siblings 和 .previous_siblings 屬性可以對(duì)當(dāng)前節(jié)點(diǎn)的兄弟節(jié)點(diǎn)迭代輸出。

for sibling in p_tag.span.next_siblings:
    print(repr(sibling))
#'\n'
#<i>b</i>
#'\n'
#<em>c</em>
#'\n'

for prev in p_tag.em.previous_siblings:
    print(repr(prev))
#'\n'
#<i>b</i>
#'\n'
#<span>a</span>
#'\n'

四、回退和前進(jìn)

HTML解析器把文檔字符串轉(zhuǎn)換成一連串的事件:
打開<html>標(biāo)簽 -> 打開<head>標(biāo)簽 -> 打開<title>標(biāo)簽 -> 添加一段字符串 -> 關(guān)閉<title>標(biāo)簽 ...
Beautiful Soup提供了重現(xiàn)解析器初始化過程的方法。

.next_element 和 .previous_element:

.next_element 屬性指向解析過程中下一個(gè)被解析的對(duì)象(字符串或tag)。

print(h1_tag.next_element) # bs4 test
因?yàn)檫@個(gè)結(jié)果是在<h1>標(biāo)簽被解析之后的解析內(nèi)容,所以輸出字符串。

print(h1_tag.next_element.previous_element) # <h1>bs4 test</h1>

h1_tag.next_element 輸出的是“bs4 test”字符串,因?yàn)?.previous_element 指向當(dāng)前被解析的對(duì)象的前一個(gè)解析對(duì)象,所以這里輸出<h1>bs4 test</h1>。

.next_elements 和 .previous_elements:

通過 .next_elements 和 .previous_elements 的迭代器可以向前或向后訪問文檔的解析內(nèi)容。

str2 = "<p><span>a</span><i>b</i><em>c</em></p>"
bs42 = BeautifulSoup(str2, "lxml")
for element in bs42.p.next_elements:
    print(element)
# <span>a</span>
# a
# <i>b</i>
# b
# <em>c</em>
# c
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容