一、BeautifulSoup解析庫
1、快速開始
html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"><b>The Dormouse's story</b></p> <p class="story">Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>, <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; and they lived at the bottom of a well.</p> <p class="story">...</p> """
from bs4 import BeautifulSoup soup = BeautifulSoup(html_doc) print(soup.prettify()) # 結構化輸出文檔 print(soup.title) # 獲取title標簽 print(soup.title.name) # 獲取title標簽名稱 print(soup.title.parent.name) print(soup.p['class'])
從文檔中找到所有<a>標簽的鏈接:
for link in soup.find_all('a'): print(link.get('href'))
從文檔中獲取所有文字內容:
print(soup.get_text())
2、標簽選擇器
#1、標簽選擇器:即直接通過標簽名字選擇,選擇速度快,如果存在多個相同的標簽則只返回第一個 #2、獲取標簽的名稱 #3、獲取標簽的屬性 #4、獲取標簽的內容 #5、嵌套選擇 #6、子節點、子孫節點 #7、父節點、祖先節點 #8、兄弟節點

#1、標簽選擇器:即直接通過標簽名字選擇,選擇速度快,如果存在多個相同的標簽則只返回第一個 html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"><b>The Dormouse's story</b></p> <p class="story">Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>, <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; and they lived at the bottom of a well.</p> <p class="story">...</p> """ from bs4 import BeautifulSoup soup=BeautifulSoup(html_doc,'lxml') print(soup.head) print(type(soup.head)) #<class 'bs4.element.Tag'> print(soup.p) #存在多個相同的標簽則只返回第一個 print(soup.a) #存在多個相同的標簽則只返回第一個 #2、獲取標簽的名稱 print(soup.p.name) #3、獲取標簽的屬性 print(soup.p.attrs) #4、獲取表的內容 print(soup.p.string) ''' 對下面的這種結構,soup.p.string 返回為None,因為里面有a <p id='list-1'> 哈哈哈哈 <a class='sss'> <span> <h1>aaaa</h1> </span> </a> <b>bbbbb</b> </p> ''' #5、嵌套選擇 print(soup.head.title.string) print(soup.body.a.string) #6、子節點、子孫節點 html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"> <b>The Dormouse's story</b> Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1"> <span>Elsie</span> </a>, <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; and they lived at the bottom of a well. </p> <p class="story">...</p> """ from bs4 import BeautifulSoup soup=BeautifulSoup(html_doc,'lxml') print(soup.p.contents) #p下所有子節點 print(soup.p.children) #得到一個迭代器,包含p下所有子節點 for i,child in enumerate(soup.p.children): print(i,child) print(soup.p.descendants) #獲取子孫節點,p下所有的標簽都會選擇出來 for i,child in enumerate(soup.p.descendants): print(i,child) #7、父節點、祖先節點 print(soup.a.parent) #獲取a標簽的父節點 print(soup.a.parents) #找到a標簽所有的祖先節點,父親的父親,父親的父親的父親... #8、兄弟節點 print(soup.a.next_siblings) #得到生成器對象 print(soup.a.previous_siblings) #得到生成器對象
3、標准選擇器
#find與findall:用法完全一樣,可根據標簽名,屬性,內容查找文檔,但是find只找第一個元素 html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"> <b>The Dormouse's story</b> Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1"> <span>Elsie</span> </a> <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; and they lived at the bottom of a well. </p> <p class="story">...</p> """ from bs4 import BeautifulSoup soup=BeautifulSoup(html_doc,'lxml') #1、按照標簽名查找 # print(soup.find_all('a')) # print(soup.find_all('a',id='link3')) # print(soup.find_all('a',id='link3',attrs={'class':"sister"})) # # print(soup.find_all('a')[0].find('span')) #嵌套查找 #2、按照屬性查找 # print(soup.p.find_all(attrs={'id':'link1'})) #等同於print(soup.find_all(id='link1')) # print(soup.p.find_all(attrs={'class':'sister'})) # # print(soup.find_all(class_='sister')) #3、按照文本內容查找 print(soup.p.find_all(text="The Dormouse's story")) # 按照完整內容匹配(是==而不是in),得到的結果也是內容 #更多:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#find
4、Css選擇器
##該模塊提供了select方法來支持css html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"> <b>The Dormouse's story</b> Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1"> <span>Elsie</span> </a> <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; <div class='panel-1'> <ul class='list' id='list-1'> <li class='element'>Foo</li> <li class='element'>Bar</li> <li class='element'>Jay</li> </ul> <ul class='list list-small' id='list-2'> <li class='element'><h1 class='yyyy'>Foo</h1></li> <li class='element xxx'>Bar</li> <li class='element'>Jay</li> </ul> </div> and they lived at the bottom of a well. </p> <p class="story">...</p> """ from bs4 import BeautifulSoup soup=BeautifulSoup(html_doc,'lxml') #1、CSS選擇器 print(soup.p.select('.sister')) print(soup.select('.sister span')) print(soup.select('#link1')) print(soup.select('#link1 span')) print(soup.select('#list-2 .element.xxx')) print(soup.select('#list-2')[0].select('.element')) #可以一直select,但其實沒必要,一條select就可以了 # 2、獲取屬性 print(soup.select('#list-2 h1')[0].attrs) # 3、獲取內容 print(soup.select('#list-2 h1')[0].get_text())
5、總結
# 總結: #1、推薦使用lxml解析庫 #2、講了三種選擇器:標簽選擇器,find與find_all,css選擇器 1、標簽選擇器篩選功能弱,但是速度快 2、建議使用find,find_all查詢匹配單個結果或者多個結果 3、如果對css選擇器非常熟悉建議使用select #3、記住常用的獲取屬性attrs和文本值get_text()的方法
二、Xpath解析庫
1、絕對路徑與相對路徑
如果"/"處在XPath表達式開頭則表示文檔根元素,(表達式中間作為分隔符用以分割每一個步進表達式)如:/messages/message/subject是一種絕對路徑表示法,它表明是從文檔根開始查找節點。假設當前節點是在第一個message節點【/messages/message[1]】,則路徑表達式subject(路徑前沒有"/")這種表示法稱為相對路徑,表明從當前節點開始查找。具體請見下面所述的"表達式上下文"。
2、表達式上下文
上下文其實表示一種環境。以明確當前XPath路徑表達式處在什么樣的環境下執行。例如同樣一個路徑表達式處在對根節點操作的環境和處在對某一個特定子節點操作的環境下執行所獲得的結果可能是完全不一樣的。也就是說XPath路徑表達式計算結果取決於它所處的上下文。
XPath上下文基本有以下幾種:

<?xml version="1.0" encoding="UTF-8"?> <!-- edited with XMLSpy v2008 rel. 2 sp2 (http://www.altova.com) by Administrator --> <?xml-stylesheet type="text/xsl" href="messages.xsl"?> <messages> <message id="1"> <sender>gukaitong@gmail.com</sender> <to>anonymous@gmail.com <group name="IT"> <address>111@gmail.com</address> <address>222@gmail.com</address> <address>aaa@gmail.com</address> <address>bbb@gmail.com</address> <address>ccc@gmail.com</address> </group> </to> <subject>This is a sample</subject> <datetime date="2008-12-11" time="12:00:00" formatted="12/11/2008 12:00AM">2008-12-11T12:00:00Z</datetime> <body> Are you interested in? <attachments> <attachment id="1"> <message id="0"> <sender>anonymous@gmail.com</sender> <to>gukaitong@gmail.com</to> <body> We strongly recommend the following books <books xmlns:amazon="http://www.amazon.com/books/schema"> <amazon:book> <name>Professional C# 2008 </name> <country>USA</country> <price>37.79</price> <year>2007</year> </amazon:book> <amazon:book> <name>Microsoft Visual C# 2008 Step by Step </name> <country>USA</country> <price>26.39 </price> <year>2008</year> </amazon:book> <amazon:book> <name>C# in Depth</name> <country>USA</country> <price>29.69 </price> <year>2006</year> </amazon:book> <amazon:book> <name>Thinking in Java</name> <country>USA</country> <price>23.69 </price> <year>2004</year> </amazon:book> </books> </body> </message> </attachment> </attachments> </body> </message> <message id="2"> <sender>333@gmail.com</sender> <to>444@gmail.com</to> <subject>No title</subject> <body/> </message> </messages>
當前節點(./):如./sender表示選擇當前節點下的sender節點集合(等同於下面所講的"特定元素",如:sender) 父節點(../):如../sender表示選擇當前節點的父節點下的sender節點集合 根元素(/):如/messages表示選擇從文檔根節點下的messages節點集合. 根節點(/*):這里的*是代表所有節點,但是根元素只有一個,所以這里表示根節點。/*的返回結果和/messages返回的結果一樣都是messages節點。 遞歸下降(//):如當前上下文是messages節點。則//sender將返回以下結果: /messages//sender : <sender>gkt1980@gmail.com</sender> <sender>111@gmail.com</sender> <sender>333@gmail.com</sender> /messages/message[1]//sender: <sender>gkt1980@gmail.com</sender> <sender>111@gmail.com</sender> 我們可以看出XPath表達式返回的結果是:從當前節點開始遞歸步進搜索當前節點下的所有子節點找到滿足條件的節點集。
3、運算符及特殊字符
運算符/特殊字符 |
說明 |
/ |
此路徑運算符出現在模式開頭時,表示應從根節點選擇。 |
// |
從當前節點開始遞歸下降,此路徑運算符出現在模式開頭時,表示應從根節點遞歸下降。 |
. |
當前上下文。 |
.. |
當前上下文節點父級。 |
* |
通配符;選擇所有元素節點與元素名無關。(不包括文本,注釋,指令等節點,如果也要包含這些節點請用node()函數) |
@ |
屬性名的前綴。 |
@* |
選擇所有屬性,與名稱無關。 |
: |
命名空間分隔符;將命名空間前綴與元素名或屬性名分隔。 |
( ) |
括號運算符(優先級最高),強制運算優先級。 |
[ ] |
應用篩選模式(即謂詞,包括"過濾表達式"和"軸(向前/向后)")。 |
[ ] |
下標運算符;用於在集合中編制索引。 |
| |
兩個節點集合的聯合,如://messages/message/to | //messages/message/cc |
- |
減法。 |
div, |
浮點除法。 |
and, or |
邏輯運算。 |
mod |
求余。 |
not() |
邏輯非 |
= |
等於 |
!= |
不等於 |
特殊比較運算符 |
< 或者 <<= 或者 <= > 或者 >>= 或者 >=需要轉義的時候必須使用轉義的形式,如在XSLT中,而在XMLDOM的scripting中不需要轉義。 |
4、常用表達式
/ |
Document Root文檔根. |
/* |
選擇文檔根下面的所有元素節點,即根節點(XML文檔只有一個根節點) |
/node() |
根元素下所有的節點(包括文本節點,注釋節點等) |
/text() |
查找文檔根節點下的所有文本節點 |
/messages/message |
messages節點下的所有message節點 |
/messages/message[1] |
messages節點下的第一個message節點 |
/messages/message[1]/self::node() |
第一個message節點(self軸表示自身,node()表示選擇所有節點) |
/messages/message[1]/node() |
第一個message節點下的所有子節點 |
/messages/message[1]/*[last()] |
第一個message節點的最后一個子節點 |
/messages/message[1]/[last()] |
Error,謂詞前必須是節點或節點集 |
/messages/message[1]/node()[last()] |
第一個message節點的最后一個子節點 |
/messages/message[1]/text() |
第一個message節點的所有子節點 |
/messages/message[1]//text() |
第一個message節點下遞歸下降查找所有的文本節點(無限深度) |
/messages/message[1] /child::node() /messages/message[1] /node() /messages/message[position()=1]/node() //message[@id=1] /node() |
第一個message節點下的所有子節點 |
//message[@id=1] //child::node() |
遞歸所有子節點(無限深度) |
//message[position()=1]/node() |
選擇id=1的message節點以及id=0的message節點 |
/messages/message[1] /parent::* |
Messages節點 |
/messages/message[1]/body/attachments/parent::node() /messages/message[1]/body/attachments/parent::* /messages/message[1]/body/attachments/.. |
attachments節點的父節點。父節點只有一個,所以node()和* 返回結果一樣。 (..也表示父節點. 表示自身節點) |
//message[@id=0]/ancestor::* |
Ancestor軸表示所有的祖輩,父,祖父等。 向上遞歸 |
//message[@id=0]/ancestor-or-self::* |
向上遞歸,包含自身 |
//message[@id=0]/ancestor::node() |
對比使用*,多一個文檔根元素(Document root) |
/messages/message[1]/descendant::node() //messages/message[1]//node() |
遞歸下降查找message節點的所有節點 |
/messages/message[1]/sender/following::* |
查找第一個message節點的sender節點后的所有同級節點,並對每一個同級節點遞歸向下查找。 |
//message[@id=1]/sender/following-sibling::* |
查找id=1的message節點的sender節點的所有后續的同級節點。 |
//message[@id=1]/datetime/@date |
查找id=1的message節點的datetime節點的date屬性 |
//message[@id=1]/datetime[@date] //message/datetime[attribute::date] |
查找id=1的message節點的所有含有date屬性的datetime節點 |
//message[datetime] |
查找所有含有datetime節點的message節點 |
//message/datetime/attribute::* //message/datetime/attribute::node() //message/datetime/@* |
返回message節點下datetime節點的所有屬性節點 |
//message/datetime[attribute::*] //message/datetime[attribute::node()] //message/datetime[@*] //message/datetime[@node()] |
選擇所有含有屬性的datetime節點 |
//attribute::* |
選擇根節點下的所有屬性節點 |
//message[@id=0]/body/preceding::node() |
順序選擇body節點所在節點前的所有同級節點。(查找順序為:先找到body節點的頂級節點(根節點),得到根節點標簽前的所有同級節點,執行完成后繼續向下一級,順序得到該節點標簽前的所有同級節點,依次類推。) 注意:查找同級節點是順序查找,而不是遞歸查找。 |
//message[@id=0]/body/preceding-sibling::node() |
順序查找body標簽前的所有同級節點。(和上例一個最大的區別是:不從最頂層開始到body節點逐層查找。我們可以理解成少了一個循環,而只查找當前節點前的同級節點) |
//message[@id=1]//*[namespace::amazon] |
查找id=1的所有message節點下的所有命名空間為amazon的節點。 |
//namespace::* |
文檔中的所有的命名空間節點。(包括默認命名空間xmlns:xml) |
//message[@id=0]//books/*[local-name()='book'] |
選擇books下的所有的book節點, 注意:由於book節點定義了命名空間<amazone:book>.若寫成//message[@id=0]//books/book則查找不出任何節點。 |
//message[@id=0]//books/*[local-name()='book' and namespace-uri()='http://www.amazon.com/books/schema'] |
選擇books下的所有的book節點,(節點名和命名空間都匹配) |
//message[@id=0]//books/*[local-name()='book'][year>2006] |
選擇year節點值>2006的book節點 |
//message[@id=0]//books/*[local-name()='book'][1]/year>2006 |
指示第一個book節點的year節點值是否大於2006. 返回xs:boolean: true |