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

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

本次介紹使用 bs4 對 HTML 文檔樹的遍歷。spa

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

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>
"""

文檔樹的遍歷:xml

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

  1. 子節點
  2. 父節點
  3. 兄弟節點
  4. 回退和前進

1、子節點對象

一個標籤可能包含多個字符串或者其餘標籤,這些標籤都屬於子節點。要獲取子節點,首先須要獲得一個 Tag 對象:遞歸

獲取一個 Tag 對象最簡單的方式是用 bs4 對象點上要獲取的標籤的名字,同時支持鏈式調用。事件

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

.contents :ip

tag 對象的 .contents 屬性能夠將 tag 的子節點以列表的方式輸出,不包含孫節點:element

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

字符串沒有 .contents 屬性,由於字符串沒有子節點。

.children:

.children 生成器,能夠對 tag 的直接子節點進行循環:

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 屬性能夠對全部 tag 的子孫節點進行遞歸循環:

for child in ul_tag.descendants:
    print(child)

.string:

若是 tag 只有一個 NavigableString 類型子節點,那麼這個 tag 可使用 .string 獲得子節點。

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

若是一個 tag 僅有一個子節點,那麼這個 tag 也可使用 .string 方法,輸出結果與當前惟一子節點(也就是 title 節點)的 .string 結果相同。

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

若是 tag 包含了多個子節點,tag 就沒法肯定 .string 方法應該調用哪一個子節點的內容,因此輸出結果是 None:

print(div_tag.string) # None

.strings 和 stripped_strings:

對於上邊 tag 包含了多個子節點的問題,可使用 .strings 來循環獲取:

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

.stripped_strings 能夠去除多餘空白內容。

2、父節點

.parent:

.parent 屬性來獲取某個標籤或字符串的父節點,好比:

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 屬性能夠遞歸獲得元素的全部父輩節點。

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

3、兄弟節點

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

#<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>的子節點,因此這三個能夠被稱爲兄弟節點。

.next_sibling 和 .previous_sibling:

經過以上兩個屬性能夠查詢兄弟節點。

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

要注意的點:

在這裏<span>沒有 previous_sibling 屬性,由於它是同級節點中的第一個。相反,<em>沒有 next_sibling 屬性。

字符串「a,b,c」不是兄弟節點,由於它們的父節點不一樣。

因爲咱們上邊的例子是寫的一行,在實際中 .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 屬性能夠對當前節點的兄弟節點迭代輸出。

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'

4、回退和前進

HTML解析器把文檔字符串轉換成一連串的事件:
打開<html>標籤 -> 打開<head>標籤 -> 打開<title>標籤 -> 添加一段字符串 -> 關閉<title>標籤 ...
Beautiful Soup提供了重現解析器初始化過程的方法。

.next_element 和 .previous_element:

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

print(h1_tag.next_element) # bs4 test
由於這個結果是在<h1>標籤被解析以後的解析內容,因此輸出字符串。

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

h1_tag.next_element 輸出的是「bs4 test」字符串,由於 .previous_element 指向當前被解析的對象的前一個解析對象,因此這裏輸出<h1>bs4 test</h1>。

.next_elements 和 .previous_elements:

經過 .next_elements 和 .previous_elements 的迭代器能夠向前或向後訪問文檔的解析內容。

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

以上就是本文總結的使用 Beautiful Soup 對文檔遍歷的相關內容。

有問題歡迎指出。關注我解鎖更多 Python 乾貨哦!

相關文章
相關標籤/搜索