xpath基本知識
XPath語法:使用路徑表達式來選取XML或HTML文檔中的節點或節點集
路徑表達式
nodename:表示選取此節點的所有子節點
/ : 表示從根節點選取
// :選擇任意位置的某個節點。
. :選取當前節點
.. :選取當前節點的父節點
@ :選取屬性
謂語實例
實現效果 路勁表達式
選取屬於classroom子元素的第一個student元素 /classroom/student[1]
選取屬於classroom子元素的最后一個student元素 /classroom/student[last()]
選取屬於classroom子元素的倒數第二個stduent元素 /classroom/stduent[last()-1]
選取最前面的兩個屬於classroom元素的子元素的student元素 /classroom/stduent[position()<3]
選取所有擁有名為lang的屬性的name元素 //name[@lang]
選取所有name元素,且這些元素擁有值為eng的lang屬性 //name[@lang='en']
選取classroom元素的所有student元素,且其中的age元素的值須大於20 .classroom.stduent[age>20]
選取classroom元素中的student元素的所有name元素,且其中的age元素的值須大於20 /classroom/stduent[age>20]/name
通配符“*”與“|”操作
實現效果 路徑表達式
選取classroom元素的所有子元素 /classroom/*
選取文檔中的所有元素 //*
選取所有帶有屬性的name元素 //name[@*]
選取stduent元素的所有name和age元素 //stduent/name | //stduent/age
選取屬於classroom元素的student元素的所有name元素,以及文檔中所有的age元素 /classroom/stduent/name | //age
XPath軸 步的語法為 軸名稱:節點測試[謂語]
軸名稱 含義
child 選取當前節點的所有子節點
parent 選取當前節點的父節點
ancestor 選取當前節點的所有先輩(父、祖父等)
ancestor-or-self 選取當前節點的所有先輩以及當前節點本身
descendant 選取當前節點的所有后代節點
descendant-or-self 選取當前節點的所有后代節點以及當前節點本身
preceding 選取文檔中當前節點的開始標記之前的所有節點
following 選取文檔中當前節點的結束標記之后的所有節點
preceding-sibling 選取當前節點之前的所有同級節點
following-sibling 選取當前節點之后的所用同級節點
self 選取當前節點
attribute 選取當前節點的所有屬性
namespace 選取當前節點的所有命名空間
XPath軸示例分析
實現效果 路徑表達式
選取當前classroom節點中子元素的teacher節點 /classroom/child::teacher
選取所有id節點的父節點 //id/parent::*
選取所有以classid為子節點的祖先節點 //classid/ancestor::*
選取classroom節點下的所有后代節點 /classroom/descendant::*
選取所有以student為父節點的id元素 //student/descendant::id
選取所有classid元素的祖先節點及本身 //classid/ancestor-or-self::*
選擇/classroom/student本身及其所有后代元素 /classroom/student/descendant-or-self::*
選取/classroom/teacher之前的所有同級節點,結果就是選所有的student節點 /classroom/teacher/preceding-sibling::*
選取/classroom中第二個stduent之后的所有同級節點 /classroom/student[2]/following-sibling::*
選取/classroom/teacher節點所有之前的節點(除其祖先外),不僅僅是student節點,還有里面的子節點 /classroom/teacher/preceding::*
選取/classroom中第二個student之后的所有節點,結果就是選擇了teacher節點及其子節點 /classroom/student[2]/following::*
選取student節點,單獨使用沒有什么意思 //stduent/self::*
選取/classroom/teacher/name節點下的所有屬性 /classroom/teacher/name/attribute::*
XPath運算符示例分析
含義 實例
選取classroom元素的所有student元素 /classroom/student[age=19+1] /classroom/stduent[age=5*4] /classroom/student[age=21-1]
且其中的age元素的值須等於20 /classroom/student[age=40div2]
類似可以選取 大於、小於、不等於等操作
or 運算實例 /classroom/stduent[age<20 or age>25] .................age小於20或者大於25
and 運算實例 /classroom/stduent[age>20 and age<25] ..................age在20 到25 之間
mod 計算除法的余數
實例代碼

from lxml import etree contentStream = open(r'xpathText.xml', 'rb') content = contentStream.read().decode('utf-8') root = etree.XML(content) print(content) print('-------') em = root.xpath('/classroom/student[2]/following::*') print(em[0].xpath('./name/text()'))#獲取name標簽中文本的內容 print(em[0].xpath('./name/@lang')) #獲取name標簽中屬性名為lang的屬性值
BeautifulSoup基礎知識
創建BeautifulSoup對象的兩種方式 1.通過字符串創建 soup=BeautifulSoup(htl_str,'lxml') 其中'lxml'表示指定的解析方式
2.通過文件創建 soup=BeautifulSoup(open('index.html'))
對象種類 四種 Tag、NavigableString、BeautifulSoup 、Comment
1)Tag
在html中每個標簽及其里面的內容就是一個Tag對象,如何抽取Tag呢?
soup.title抽取title soup.a 抽取a 利用soup+標記名查找的是再內容中第一個符合要求的標記
Tag中有兩個最重要的屬性:name和attributes.每個Tag都有自己的名字,通過.name來獲取
修改Tag的name,修改完成后將影響所有通過當前Beautiful Soup對象生成的HTML文檔
html_str = """<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>
</body>
</html>"""
soup = BeautifulSoup(html_str, 'lxml') # soup = BeautifulSoup(open(r'index.html','rb'),'lxml') print(soup.prettify()) #以格式化的形式輸出文檔的內容 print(soup.name) print(soup.title.name)#輸出title的名稱 soup.title.name = 'mytitle' #修改title的名稱為mytitle print(soup.title) #title已經修改輸出None print(soup.mytitle)#輸出mytitle Tag
輸出結果
整個文檔的內容 [document] title None <mytitle>The Dormouse's story</mytitle>
獲取Tag屬性?<p class="title"><b>The Dormouse's story</b></p>Tag p中有一個屬性class值為title,獲取代碼如下:
Tag屬性值的修改類似於上述標簽名的修改 soup.p['class']='myclass' 就把屬性值title改為了myclass
# 獲取Tag中的屬性 和字典類似 print(soup.p['class']) print(soup.p.get('class'))
輸出結果
['title'] ['title']
用於獲取Tag所有屬性的方法 print(soup.p.attrs)以字典的行書獲取指定Tag的所有屬性:屬性值字典
輸出格式如下
{'class': ['title']}
2)NavigableString 當已經得到了標記的內容,要想獲取標記的內部文字怎么辦呢?需要用到.string。
print(soup.b.string)#輸出Tag對象b的內容 print(type(soup.b.string))#輸出Tage對象b的內容的類型 其實就是NavigableString類型
輸出結果
The Dormouse's story <class 'bs4.element.NavigableString'>
3)Beautiful Soup
Beautiful Soup對象表示的是一個文檔的全部內容。大部分時候,可以把它當作Tag對象,是一個特殊的人Tag,實例如下
print(type(soup.name)) print(soup.name) print(soup.attrs)
輸出結果
<class 'str'> [document] {}
4) Comment 文檔的注釋部分 ,示例如下
print(soup.a.string) print(type(soup.a.string))
輸出結果
Elsie <class 'bs4.element.Comment'>
遍歷文檔
1)子節點
Tag中的.contents和.children是非常重要的,都是輸出直接子節點,Tag的contents屬性可以將Tag子節點以列表的方式輸出:
print(soup.html.contents) print(soup.html.contents[1])#如果soup.html.contents[1].string會直接輸出文檔里的內容,具體解釋看下面
輸出結果
['\n', <head><mytitle>The Dormouse's story</mytitle></head>, '\n', <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 class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a> <a class="sister" href="http://example.com/lacie" id="link2"> <!--Lacie --> </a> and <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>; and they lived at the bottom of a well. </p><p class="story">……</p> </body>, '\n'] <head><mytitle>The Dormouse's story</mytitle></head>
Tag中children,其實.children返回的是一個生成器,可以對Tag的子節點進行循環
for child in soup.html.children: # 孩子結點遞歸循環 print(child)
輸出結果:對於輸出換行時,他要空兩行,因為print自帶換行
<head><mytitle>The Dormouse's story</mytitle></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 class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a> <a class="sister" href="http://example.com/lacie" id="link2"> <!--Lacie --> </a> and <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>; and they lived at the bottom of a well. </p><p class="story">……</p> </body>
.descendants屬性可以對所有tag的子孫節點進行遞歸循環:head中只有一個直接2節點title,但title也包含一個子節點:字符串'The Dormouse's story',
在這種情況下,字符串也屬於<head>標記的子孫節點,
for child in soup.head.descendants: # 子孫節點遞歸循環 print(child)
輸出結果
<mytitle>The Dormouse's story</mytitle> The Dormouse's story
如何獲取標記的內容呢???這就涉及.string、.strings、stripped_strings三個屬性
.string這個屬性很有特點:如果一個標記里面沒有標記了,那么.string就會返回標記里面的內容。如果標記里面只有唯一
的一個標記了,那么.string也會返回最里面的內容。如果tag包含多個子節點,tag就無法確定,string方法應該調用哪個子節點的內容,.string的輸出結果是None
print(soup.head.string) print(soup.mytitle.string) print(soup.html.string)
輸出結果
The Dormouse's story The Dormouse's story None
.strings屬性主要應用於tag中包含多個字符串的情況,可以進行循環遍歷
for stri in soup.strings: print(repr(stri))
輸出結果
'\n' "The Dormouse's story" '\n' '\n' "The Dormouse's story" 'Once upon a time there were three little sisters; and their names were\n ' '\n' '\n' '\n' '\n and\n ' 'Tillie' ';\n and they lived at the bottom of a well.\n' '……' '\n' '\n'
.stripped_strings屬性可以去掉輸出字符串中包含的空格或換行,示例如下
for stri in soup.stripped_strings: print(repr(stri))
輸出結果
"The Dormouse's story" "The Dormouse's story" 'Once upon a time there were three little sisters; and their names were' 'and' 'Tillie' ';\n and they lived at the bottom of a well.' '……'
2)父節點
每個Tag或者字符串都有父節點:被包含在某個Tag中。通過.parent可以獲取某個元素的父節點
print soup.mytitle.parent 輸出<head><title>........</title></head>
通過元素的.parents屬性可以遞歸得到元素所有父輩節點,使用.parents方法遍歷了<a>標記到根節點的所有節點
print(soup.a) for parent in soup.a.parents: if parent is None: print(parent) else: print(parent.name)
輸出結果
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a> p body html [document]
3)兄弟節點:可以理解為和本節點出在同一級上的節點,.next_sibling屬性可以獲取該節點的下一個兄弟節點,.previous_sibling則與之相反,
如果節點不存在,則返回None
可以通過.next_siblings和.previous_siblings來迭代所有的兄弟節點
4)前后節點
前后節點需要使用.next_element、previous_element這兩個屬性,他針對所有節點,不分層次,例如<head><title>The Dormouse‘s story</title></head>
中的下一個節點是title
如果想遍歷所有的前節點或者后節點,通過.next_elements和previous_elements的迭代器就可以向前或向后訪問文檔的解析內容
for elem in soup.html.next_elements: #有點像深度優先遍歷 print(repr(elem))
輸出結果
'\n' <head><mytitle>The Dormouse's story</mytitle></head> <mytitle>The Dormouse's story</mytitle> "The Dormouse's story" '\n' <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 class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a> <a class="sister" href="http://example.com/lacie" id="link2"> <!--Lacie --> </a> and <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>; and they lived at the bottom of a well. </p><p class="story">……</p> </body> '\n' <p class="title"><b>The Dormouse's story</b></p> <b>The Dormouse's story</b> "The Dormouse's story" <p class="story">Once upon a time there were three little sisters; and their names were <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a> <a class="sister" href="http://example.com/lacie" id="link2"> <!--Lacie --> </a> and <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>; and they lived at the bottom of a well. </p> 'Once upon a time there were three little sisters; and their names were\n ' <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a> ' Elsie ' '\n' <a class="sister" href="http://example.com/lacie" id="link2"> <!--Lacie --> </a> '\n' 'Lacie ' '\n' '\n and\n ' <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a> 'Tillie' ';\n and they lived at the bottom of a well.\n' <p class="story">……</p> '……' '\n' '\n'
搜索文檔
只介紹find_all()方法,其它方法類似
函數原型
find_all(name,attrs,recursive,text,**kwargs)
1)name參數
name參數可以查找所有名字為name的標記,字符對象會被自動忽略掉。name參數取值可以是字符串、正則表達式、列表、True和方法
字符串案例 用於查找文檔中所有的<b>標記 ,返回值為列表:
print(soup.find_all('b')) #輸出結果 [<b>The Dormouse's story</b>]
傳入正則表達式作為參數,會通過正則表達式的match()來匹配內容。下面列出所有以b開頭的標記,這表示<body>和<b>標記
for tag in soup.find_all(re.compile('^b')): print(tag.name) #輸出結果 body b
傳入列表
print(soup.find_all(['a','b']))//找到文檔中所有的<a>標記和<b>標記
傳入True,True可以匹配任何值,會查找所有的tag ,但不會返回字符串節點
for tag in soup.find_all(True): print(tag.name) #輸出結果 html head mytitle body p b p a a a p
如果沒有合適過濾器,那么還可以定義一個方法,方法只接受一個元素參數Tag節點,如果這個方法返回?True表示當前元素匹配並且被找到
,如果不是則返回False,比如過濾包含class屬性,也包含id屬性的元素
def hasClass_Id(tag): return tag.has_attr('class') and tag.has_attr('id') print(soup.find_all(hasClass_Id)) #輸出結果 [<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2"> <!--Lacie --> </a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
2)kwargs參數
kwargs參數就是python中的keyword參數 ,如果一個指定名字的參數不是搜索內置的參數名,搜索時會把該參數當作指定名字Tag的屬性來搜索
。搜索指定名字的屬性時可以使用的參數值包括字符串、正則表達式、列表、True
傳入字符串 print(soup.find_all(id='link2')) 會搜索每個tag的id屬性
傳入正則表達式 print(soup.find_all(href=re.compile('elsie')))搜索href屬性中含有‘elsie’的tag
True print(soup.find_all(id=True)) 文檔樹中查找所有包含id屬性的Tag,無論id的值是什么:
如果想用 class過濾·,但class是python的關鍵字,需要在class后main加個下划線:
soup.find_all('a',class_='sister')
有些tag屬性在搜索中不能使用,比如HTML5中的data-*屬性 可以通過find_all()方法的attrs參數定義一個字典參數來搜索包含特殊屬性的tag
,
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>', 'lxml') print(data_soup.find_all(attrs={"data-foo": "value"})) # data_soup.find_all(data - foo = 'value') #報錯 特殊屬性不能這樣處理 #輸出結果 [<div data-foo="value">foo!</div>]
3)text參數
通過text參數可以搜索文檔中的字符串內容。與name參數的可選值一樣,text參數接受字符、正則表達式、列表、True
print soup.find_all(text=["Tillie", "Elsie", "Lacie"])
print soup.find_all(text=re.compile("Dormouse"))輸出結果
[u'Elsie', u'Lacie', u'Tillie']
[u"The Dormouse's story", u"The Dormouse's story"]
4)limit參數
find_all()方法返回全部的搜索結構,如果文檔樹很大那么搜索會很慢2.如果我們不需要全部結果,可以使用limit參數限制返回結果的數量
soup.find_all('a',limit=2)值返回兩條結果
5)recursive參數
調用tag的find_all()方法是,Beautiful Soup會檢索當前tag的所有子孫節點,如果只想檢索tag的直接子節點,可以使用參數
recusive=False
print(soup.find_all('mytitle')) print(soup.find_all('mytitle', recursive=False)) #輸出結果 [<mytitle>The Dormouse's story</mytitle>] []