上一篇文章介紹了 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、子節點對象
一個標籤可能包含多個字符串或者其餘標籤,這些標籤都屬於子節點。要獲取子節點,首先須要獲得一個 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 乾貨哦!