lxml是python的一個解析庫,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高
XPath,全稱XML Path Language,即XML路徑語言,它是一門在XML文檔中查找信息的語言,它最初是用來搜尋XML文檔的,但是它同樣適用於HTML文檔的搜索
XPath的選擇功能十分強大,它提供了非常簡明的路徑選擇表達式,另外,它還提供了超過100個內建函數,用於字符串、數值、時間的匹配以及節點、序列的處理等,幾乎所有我們想要定位的節點,都可以用XPath來選擇
XPath於1999年11月16日成為W3C標准,它被設計為供XSLT、XPointer以及其他XML解析軟件使用,更多的文檔可以訪問其官方網站:https://www.w3.org/TR/xpath/
1、python庫lxml的安裝
windows系統下的安裝:
#pip安裝 pip3 install lxml #wheel安裝 #下載對應系統版本的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml pip3 install lxml-4.2.1-cp36-cp36m-win_amd64.whl
linux下安裝:
yum install -y epel-release libxslt-devel libxml2-devel openssl-devel
pip3 install lxml
驗證安裝:
$python3 >>>import lxml
2、XPath常用規則
表達式 | 描述 |
nodename | 選取此節點的所有子節點 |
/ | 從當前節點選取直接子節點 |
// | 從當前節點選取子孫節點 |
. | 選取當前節點 |
.. | 選取當前節點的父節點 |
@ | 選取屬性 |
* | 通配符,選擇所有元素節點與元素名 |
@* | 選取所有屬性 |
[@attrib] | 選取具有給定屬性的所有元素 |
[@attrib='value'] | 選取給定屬性具有給定值的所有元素 |
[tag] | 選取所有具有指定元素的直接子節點 |
[tag='text'] | 選取所有具有指定元素並且文本內容是text節點 |
(1)讀取文本解析節點
from lxml import etree text=''' <div> <ul> <li class="item-0"><a href="link1.html">第一個</a></li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0"><a href="link5.html">a屬性</a> </ul> </div> ''' html=etree.HTML(text) #初始化生成一個XPath解析對象 result=etree.tostring(html,encoding='utf-8') #解析對象輸出代碼 print(type(html)) print(type(result)) print(result.decode('utf-8')) #etree會修復HTML文本節點 <class 'lxml.etree._Element'> <class 'bytes'> <html><body><div> <ul> <li class="item-0"><a href="link1.html">第一個</a></li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0"><a href="link5.html">a屬性</a> </li></ul> </div> </body></html>
(2)讀取HTML文件進行解析
from lxml import etree html=etree.parse('test.html',etree.HTMLParser()) #指定解析器HTMLParser會根據文件修復HTML文件中缺失的如聲明信息 result=etree.tostring(html) #解析成字節 #result=etree.tostringlist(html) #解析成列表 print(type(html)) print(type(result)) print(result) # <class 'lxml.etree._ElementTree'> <class 'bytes'> b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">\n<html><body><div> \n <ul> \n <li class="item-0"><a href="link1.html">first item</a></li> \n <li class="item-1"><a href="link2.html">second item</a></li> \n <li class="item-inactive"><a href="link3.html">third item</a></li> \n <li class="item-1"><a href="link4.html">fourth item</a></li> \n <li class="item-0"><a href="link5.html">fifth item</a> \n </li></ul> \n </div> \n</body></html>'
(3)獲取所有節點
返回一個列表每個元素都是Element類型,所有節點都包含在其中
from lxml import etree html=etree.parse('test',etree.HTMLParser()) result=html.xpath('//*') #//代表獲取子孫節點,*代表獲取所有 print(type(html)) print(type(result)) print(result) # <class 'lxml.etree._ElementTree'> <class 'list'> [<Element html at 0x754b210048>, <Element body at 0x754b210108>, <Element div at 0x754b210148>, <Element ul at 0x754b210188>, <Element li at 0x754b2101c8>, <Element a at 0x754b210248>, <Element li at 0x754b210288>, <Element a at 0x754b2102c8>, <Element li at 0x754b210308>, <Element a at 0x754b210208>, <Element li at 0x754b210348>, <Element a at 0x754b210388>, <Element li at 0x754b2103c8>, <Element a at 0x754b210408>]
如要獲取li節點,可以使用//后面加上節點名稱,然后調用xpath()方法
html.xpath('//li') #獲取所有子孫節點的li節點
(4)獲取子節點
通過/或者//即可查找元素的子節點或者子孫節點,如果想選擇li節點的所有直接a節點,可以這樣使用
result=html.xpath('//li/a') #通過追加/a選擇所有li節點的所有直接a節點,因為//li用於選中所有li節點,/a用於選中li節點的所有直接子節點a
(5)獲取父節點
我們知道通過連續的/或者//可以查找子節點或子孫節點,那么要查找父節點可以使用..來實現也可以使用parent::來獲取父節點
from lxml import etree from lxml.etree import HTMLParser text=''' <div> <ul> <li class="item-0"><a href="link1.html">第一個</a></li> <li class="item-1"><a href="link2.html">second item</a></li> </ul> </div> ''' html=etree.HTML(text,etree.HTMLParser()) result=html.xpath('//a[@href="link2.html"]/../@class') result1=html.xpath('//a[@href="link2.html"]/parent::*/@class') print(result) print(result1) # ['item-1'] ['item-1']
(6)屬性匹配
在選取的時候,我們還可以用@
符號進行屬性過濾。比如,這里如果要選取class
為item-1
的li
節點,可以這樣實現:
from lxml import etree from lxml.etree import HTMLParser text=''' <div> <ul> <li class="item-0"><a href="link1.html">第一個</a></li> <li class="item-1"><a href="link2.html">second item</a></li> </ul> </div> ''' html=etree.HTML(text,etree.HTMLParser()) result=html.xpath('//li[@class="item-1"]') print(result)
(7)文本獲取
我們用XPath中的text()方法獲取節點中的文本
from lxml import etree text=''' <div> <ul> <li class="item-0"><a href="link1.html">第一個</a></li> <li class="item-1"><a href="link2.html">second item</a></li> </ul> </div> ''' html=etree.HTML(text,etree.HTMLParser()) result=html.xpath('//li[@class="item-1"]/a/text()') #獲取a節點下的內容 result1=html.xpath('//li[@class="item-1"]//text()') #獲取li下所有子孫節點的內容 print(result) print(result1)
(8)屬性獲取
使用@符號即可獲取節點的屬性,如下:獲取所有li節點下所有a節點的href屬性
result=html.xpath('//li/a/@href') #獲取a的href屬性 result=html.xpath('//li//@href') #獲取所有li子孫節點的href屬性
(9)屬性多值匹配
如果某個屬性的值有多個時,我們可以使用contains()函數來獲取
from lxml import etree text1=''' <div> <ul> <li class="aaa item-0"><a href="link1.html">第一個</a></li> <li class="bbb item-1"><a href="link2.html">second item</a></li> </ul> </div> ''' html=etree.HTML(text1,etree.HTMLParser()) result=html.xpath('//li[@class="aaa"]/a/text()') result1=html.xpath('//li[contains(@class,"aaa")]/a/text()') print(result) print(result1) #通過第一種方法沒有取到值,通過contains()就能精確匹配到節點了 [] ['第一個']
(10)多屬性匹配
另外我們還可能遇到一種情況,那就是根據多個屬性確定一個節點,這時就需要同時匹配多個屬性,此時可用運用and運算符來連接使用:
from lxml import etree text1=''' <div> <ul> <li class="aaa" name="item"><a href="link1.html">第一個</a></li> <li class="aaa" name="fore"><a href="link2.html">second item</a></li> </ul> </div> ''' html=etree.HTML(text1,etree.HTMLParser()) result=html.xpath('//li[@class="aaa" and @name="fore"]/a/text()') result1=html.xpath('//li[contains(@class,"aaa") and @name="fore"]/a/text()') print(result) print(result1) # ['second item'] ['second item']
(11)XPath中的運算符
運算符 | 描述 | 實例 | 返回值 |
or |
或 | age=19 or age=20 | 如果age等於19或者等於20則返回true反正返回false |
and | 與 | age>19 and age<21 | 如果age等於20則返回true,否則返回false |
mod | 取余 | 5 mod 2 | 1 |
| | 取兩個節點的集合 | //book | //cd | 返回所有擁有book和cd元素的節點集合 |
+ | 加 | 6+4 | 10 |
- | 減 | 6-4 | 2 |
* | 乘 | 6*4 | 24 |
div | 除法 | 8 div 4 | 2 |
= | 等於 | age=19 | true |
!= | 不等於 | age!=19 | true |
< | 小於 | age<19 | true |
<= | 小於或等於 | age<=19 | true |
> | 大於 | age>19 | true |
>= | 大於或等於 | age>=19 | true |
此表參考來源:http://www.w3school.com.cn/xpath/xpath_operators.asp
(12)按序選擇
有時候,我們在選擇的時候某些屬性可能同時匹配多個節點,但我們只想要其中的某個節點,如第二個節點或者最后一個節點,這時可以利用中括號引入索引的方法獲取特定次序的節點:
from lxml import etree text1=''' <div> <ul> <li class="aaa" name="item"><a href="link1.html">第一個</a></li> <li class="aaa" name="item"><a href="link1.html">第二個</a></li> <li class="aaa" name="item"><a href="link1.html">第三個</a></li> <li class="aaa" name="item"><a href="link1.html">第四個</a></li> </ul> </div> ''' html=etree.HTML(text1,etree.HTMLParser()) result=html.xpath('//li[contains(@class,"aaa")]/a/text()') #獲取所有li節點下a節點的內容 result1=html.xpath('//li[1][contains(@class,"aaa")]/a/text()') #獲取第一個 result2=html.xpath('//li[last()][contains(@class,"aaa")]/a/text()') #獲取最后一個 result3=html.xpath('//li[position()>2 and position()<4][contains(@class,"aaa")]/a/text()') #獲取第一個 result4=html.xpath('//li[last()-2][contains(@class,"aaa")]/a/text()') #獲取倒數第三個 print(result) print(result1) print(result2) print(result3) print(result4) # ['第一個', '第二個', '第三個', '第四個'] ['第一個'] ['第四個'] ['第三個'] ['第二個']
這里使用了last()、position()函數,在XPath中,提供了100多個函數,包括存取、數值、字符串、邏輯、節點、序列等處理功能,它們的具體作用可參考:http://www.w3school.com.cn/xpath/xpath_functions.asp
(13)節點軸選擇
XPath提供了很多節點選擇方法,包括獲取子元素、兄弟元素、父元素、祖先元素等,示例如下:
from lxml import etree text1=''' <div> <ul> <li class="aaa" name="item"><a href="link1.html">第一個</a></li> <li class="aaa" name="item"><a href="link1.html">第二個</a></li> <li class="aaa" name="item"><a href="link1.html">第三個</a></li> <li class="aaa" name="item"><a href="link1.html">第四個</a></li> </ul> </div> ''' html=etree.HTML(text1,etree.HTMLParser()) result=html.xpath('//li[1]/ancestor::*') #獲取所有祖先節點 result1=html.xpath('//li[1]/ancestor::div') #獲取div祖先節點 result2=html.xpath('//li[1]/attribute::*') #獲取所有屬性值 result3=html.xpath('//li[1]/child::*') #獲取所有直接子節點 result4=html.xpath('//li[1]/descendant::a') #獲取所有子孫節點的a節點 result5=html.xpath('//li[1]/following::*') #獲取當前子節之后的所有節點 result6=html.xpath('//li[1]/following-sibling::*') #獲取當前節點的所有同級節點 # [<Element html at 0x3ca6b960c8>, <Element body at 0x3ca6b96088>, <Element div at 0x3ca6b96188>, <Element ul at 0x3ca6b961c8>] [<Element div at 0x3ca6b96188>] ['aaa', 'item'] [<Element a at 0x3ca6b96248>] [<Element a at 0x3ca6b96248>] [<Element li at 0x3ca6b96308>, <Element a at 0x3ca6b96348>, <Element li at 0x3ca6b96388>, <Element a at 0x3ca6b963c8>, <Element li at 0x3ca6b96408>, <Element a at 0x3ca6b96488>] [<Element li at 0x3ca6b96308>, <Element li at 0x3ca6b96388>, <Element li at 0x3ca6b96408>]
以上使用的是XPath軸的用法,更多軸的用法可參考:http://www.w3school.com.cn/xpath/xpath_axes.asp
(14)案例應用:抓取TIOBE指數前20名排行開發語言
#!/usr/bin/env python #coding:utf-8 import requests from requests.exceptions import RequestException from lxml import etree from lxml.etree import ParseError import json def one_to_page(html): headers={ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36' } try: response=requests.get(html,headers=headers) body=response.text #獲取網頁內容 except RequestException as e: print('request is error!',e) try: html=etree.HTML(body,etree.HTMLParser()) #解析HTML文本內容 result=html.xpath('//table[contains(@class,"table-top20")]/tbody/tr//text()') #獲取列表數據 pos = 0 for i in range(20): if i == 0: yield result[i:5] else: yield result[pos:pos+5] #返回排名生成器數據 pos+=5 except ParseError as e: print(e.position) def write_file(data): #將數據重新組合成字典寫入文件並輸出 for i in data: sul={ '2018年6月排行':i[0], '2017年6排行':i[1], '開發語言':i[2], '評級':i[3], '變化率':i[4] } with open('test.txt','a',encoding='utf-8') as f: f.write(json.dumps(sul,ensure_ascii=False) + '\n') #必須格式化數據 f.close() print(sul) return None def main(): url='https://www.tiobe.com/tiobe-index/' data=one_to_page(url) revaule=write_file(data) if revaule == None: print('ok') if __name__ == '__main__': main() # {'2018年6月排行': '1', '2017年6排行': '1', '開發語言': 'Java', '評級': '15.368%', '變化率': '+0.88%'} {'2018年6月排行': '2', '2017年6排行': '2', '開發語言': 'C', '評級': '14.936%', '變化率': '+8.09%'} {'2018年6月排行': '3', '2017年6排行': '3', '開發語言': 'C++', '評級': '8.337%', '變化率': '+2.61%'} {'2018年6月排行': '4', '2017年6排行': '4', '開發語言': 'Python', '評級': '5.761%', '變化率': '+1.43%'} {'2018年6月排行': '5', '2017年6排行': '5', '開發語言': 'C#', '評級': '4.314%', '變化率': '+0.78%'} {'2018年6月排行': '6', '2017年6排行': '6', '開發語言': 'Visual Basic .NET', '評級': '3.762%', '變化率': '+0.65%'} {'2018年6月排行': '7', '2017年6排行': '8', '開發語言': 'PHP', '評級': '2.881%', '變化率': '+0.11%'} {'2018年6月排行': '8', '2017年6排行': '7', '開發語言': 'JavaScript', '評級': '2.495%', '變化率': '-0.53%'} {'2018年6月排行': '9', '2017年6排行': '-', '開發語言': 'SQL', '評級': '2.339%', '變化率': '+2.34%'} {'2018年6月排行': '10', '2017年6排行': '14', '開發語言': 'R', '評級': '1.452%', '變化率': '-0.70%'} {'2018年6月排行': '11', '2017年6排行': '11', '開發語言': 'Ruby', '評級': '1.253%', '變化率': '-0.97%'} {'2018年6月排行': '12', '2017年6排行': '18', '開發語言': 'Objective-C', '評級': '1.181%', '變化率': '-0.78%'} {'2018年6月排行': '13', '2017年6排行': '16', '開發語言': 'Visual Basic', '評級': '1.154%', '變化率': '-0.86%'} {'2018年6月排行': '14', '2017年6排行': '9', '開發語言': 'Perl', '評級': '1.147%', '變化率': '-1.16%'} {'2018年6月排行': '15', '2017年6排行': '12', '開發語言': 'Swift', '評級': '1.145%', '變化率': '-1.06%'} {'2018年6月排行': '16', '2017年6排行': '10', '開發語言': 'Assembly language', '評級': '0.915%', '變化率': '-1.34%'} {'2018年6月排行': '17', '2017年6排行': '17', '開發語言': 'MATLAB', '評級': '0.894%', '變化率': '-1.10%'} {'2018年6月排行': '18', '2017年6排行': '15', '開發語言': 'Go', '評級': '0.879%', '變化率': '-1.17%'} {'2018年6月排行': '19', '2017年6排行': '13', '開發語言': 'Delphi/Object Pascal', '評級': '0.875%', '變化率': '-1.28%'} {'2018年6月排行': '20', '2017年6排行': '20', '開發語言': 'PL/SQL', '評級': '0.848%', '變化率': '-0.72%'}
XPath的更多用法參考:http://www.w3school.com.cn/xpath/index.asp
python lxml庫的更多用法參考:http://lxml.de/
出處:https://www.cnblogs.com/zhangxinqi/p/9210211.html
==========================================================================================
Python-- lxml用法
目錄
lxml庫(lxml安裝可查看上一篇文章)
- python的HTML/XML的解析器
- 官方文檔: http://lxml.de/index.html
- 功能:
- 解析HTML
- 文件讀取
- etree和XPath的配合使用
圍繞三個問題:
問題1:有一個XML文件,如何解析
問題2:解析后,如果查找、定位某個標簽
問題3:定位后如何操作標簽,比如訪問屬性、文本內容等
導入模塊,該庫常用的XML處理功能都在lxml.etree中
from lxml import etree
Element類
Element是XML處理的核心類,Element對象可以直觀的理解為XML的節點,大部分XML節點的處理都是圍繞該類進行的。這部分包括三個內容:節點的操作、節點屬性的操作、節點內文本的操作。
1、節點操作
1、創建Element對象
使用Element
方法,參數即節點名稱。
-
'root')root = etree.Element(
-
-
<Element root at 0x2da0708>
2、獲取節點名稱
使用tag
屬性,獲取節點的名稱。
-
-
root
3、輸出XML內容
使用tostring
方法輸出XML內容,參數為Element對象。
-
-
b'<root><child1/><child2/><child3/></root>'
4、添加子節點
使用SubElement
方法創建子節點,第一個參數為父節點(Element對象),第二個參數為子節點名稱。
-
'child1')child1 = etree.SubElement(root,
-
'child2')child2 = etree.SubElement(root,
-
'child3')child3 = etree.SubElement(root,
5、刪除子節點
使用remove
方法刪除指定節點,參數為Element對象。clear方法清空所有節點。
-
# 刪除指定子節點root.remove(child1)
-
-
b'<root><child2/><child3/></root>'
-
# 清除所有子節點root.clear()
-
-
b'<root/>'
6、以列表的方式操作子節點
可以將Element對象的子節點視為列表進行各種操作:
-
0] # 下標訪問child = root[
-
-
child1
-
-
# 子節點數量print(len(root))
-
3
-
-
# 獲取索引號root.index(child2)
-
1
-
-
for child in root: # 遍歷
-
-
child1
-
child2
-
child3
-
-
0, etree.Element( 'child0')) # 插入root.insert(
-
1] # 切片start = root[:
-
-1:]end = root[
-
-
0].tag)print(start[
-
child0
-
0].tag)print(end[
-
child3
-
-
'child4') ) # 尾部添加root.append( etree.Element(
-
-
b'<root><child0/><child1/><child2/><child3/><child4/></root>'
7、獲取父節點
使用getparent
方法可以獲取父節點。
-
-
root
2、屬性操作
屬性是以key-value的方式存儲的,就像字典一樣。
1、創建屬性
可以在創建Element對象時同步創建屬性,第二個參數即為屬性名和屬性值:
-
'root', interesting= 'totally')root = etree.Element(
-
-
b'<root interesting="totally"/>'
-
也可以使用set方法給已有的Element對象添加屬性,兩個參數分別為屬性名和屬性值:
-
-
'hello', 'Huhu')root.set(
-
-
b'<root interesting="totally" hello="Huhu"/>'
2、獲取屬性
屬性是以key-value的方式存儲的,就像字典一樣。直接看例子
-
# get方法獲得某一個屬性值
-
'interesting'))print(root.get(
-
totally
-
-
# keys方法獲取所有的屬性名
-
-
[ 'hello', 'interesting']
-
-
# items方法獲取所有的鍵值對
-
for name, value in sorted(root.items()):
-
'%s = %r' % (name, value))print(
-
hello = 'Huhu'
-
interesting = 'totally'
也可以用attrib屬性一次拿到所有的屬性及屬性值存於字典中:
-
-
-
{ 'interesting': 'totally', 'hello': 'Huhu'}
-
-
'good'] = 'Bye' # 字典的修改影響節點attributes[
-
'good'))print(root.get(
-
Bye
3、文本操作
標簽及標簽的屬性操作介紹完了,最后就剩下標簽內的文本了。可以使用text和tail屬性、或XPath的方式來訪問文本內容。
1、text和tail屬性
一般情況,可以用Element的text屬性訪問標簽的文本。
-
'root')root = etree.Element(
-
'Hello, World!'root.text =
-
-
Hello, World!
-
-
b'<root>Hello, World!</root>'```
-
-
XML的標簽一般是成對出現的,有開有關,但像HTML則可能出現單一的標簽,如下面這段代碼中的`<br/>`
-
-
`<html><body>Text<br/>Tail</body></html>`
-
-
Element類提供了tail屬性支持單一標簽的文本獲取。
-
```python
-
'html')html = etree.Element(
-
'body')body = etree.SubElement(html,
-
'Text'body.text =
-
-
b'<html><body>Text</body></html>'
-
-
'br')br = etree.SubElement(body,
-
-
b'<html><body>Text<br/></body></html>'
-
-
# tail僅在該標簽后面追加文本
-
'Tail'br.tail =
-
-
b'<br/>Tail'
-
-
-
b'<html><body>Text<br/>Tail</body></html>'
-
-
# tostring方法增加method參數,過濾單一標簽,輸出全部文本
-
'text'))print(etree.tostring(html, method=
-
b'TextTail'
2、XPath方式
-
# 方式一:過濾單一標簽,返回文本
-
'string()'))print(html.xpath(
-
TextTail
-
-
# 方式二:返回列表,以單一標簽為分隔
-
'//text()'))print(html.xpath(
-
[ 'Text', 'Tail']
方法二獲得的列表,每個元素都會帶上它所屬節點及文本類型信息,如下:
-
'//text()'))texts = html.xpath(
-
-
0])print(texts[
-
Text
-
# 所屬節點
-
0].getparent()parent = texts[
-
-
body
-
-
1], texts[ 1].getparent().tag)print(texts[
-
Tail br
-
-
# 文本類型:是普通文本還是tail文本
-
0].is_text)print(texts[
-
True
-
1].is_text)print(texts[
-
False
-
1].is_tail)print(texts[
-
True
4、文件解析與輸出
這部分講述如何將XML文件解析為Element對象,以及如何將Element對象輸出為XML文件。
1. 文件解析
文件解析常用的有fromstring、XML和HTML三個方法。接受的參數都是字符串。
-
'<root>data</root>'xml_data =
-
-
# fromstring方法
-
-
-
root
-
-
b'<root>data</root>'
-
-
# XML方法,與fromstring方法基本一樣
-
-
-
root
-
-
b'<root>data</root>'
-
-
# HTML方法,如果沒有<html>和<body>標簽,會自動補上
-
-
-
html
-
-
b'<html><body><root>data</root></body></html>'
2. 輸出
輸出其實就是前面一直在用的tostring方法了,這里補充xml_declaration和encoding兩個參數,前者是XML聲明,后者是指定編碼。
-
'<root><a><b/></a></root>')root = etree.XML(
-
-
-
b'<root><a><b/></a></root>'
-
-
# XML聲明
-
True))print(etree.tostring(root, xml_declaration=
-
b"<?xml version='1.0' encoding='ASCII'?>\n<root><a><b/></a></root>"
-
-
# 指定編碼
-
'iso-8859-1'))print(etree.tostring(root, encoding=
-
b"<?xml version='1.0' encoding='iso-8859-1'?>\n<root><a><b/></a></root>"
5、ElementPath
講ElementPath前,需要引入ElementTree類,一個ElementTree對象可理解為一個完整的XML樹,每個節點都是一個Element對象。而ElementPath則相當於XML中的XPath。用於搜索和定位Element元素。
這里介紹兩個常用方法,可以滿足大部分搜索、查詢需求,它們的參數都是XPath語句(關於XPath的學習可以查看我之前的一片文章):
findall():返回所有匹配的元素,返回列表
find():返回匹配到的第一個元素
-
"<root><a x='123'>aText<b/><c/><b/></a></root>")root = etree.XML(
-
-
# 查找第一個b標簽
-
'b'))print(root.find(
-
None
-
'a').tag)print(root.find(
-
a
-
-
# 查找所有b標簽,返回Element對象組成的列表
-
for b in root.findall( './/b') ][ b.tag
-
[ 'b', 'b']
-
-
# 根據屬性查詢
-
'.//a[@x]')[ 0].tag)print(root.findall(
-
a
-
'.//a[@y]'))print(root.findall(
-
[]
以上內容大多來自此原文:Python lxml教程-SKYue
6、案例(尤其最后的一篇代碼)
- 解析HTML,案例1.py
- 文件讀取,案例2.html, 案例2.py
- etree和XPath的配合使用, 案例3.py
案例1.py
-
'''
-
安裝lxml
-
'''
-
from lxml import etree
-
-
'''
-
用lxml來解析HTML代碼
-
'''
-
-
text = '''
-
<div>
-
<ul>
-
<li class="item-0"> <a href="0.html"> first item </a></li>
-
<li class="item-1"> <a href="1.html"> first item </a></li>
-
<li class="item-2"> <a href="2.html"> first item </a></li>
-
<li class="item-3"> <a href="3.html"> first item </a></li>
-
<li class="item-4"> <a href="4.html"> first item </a></li>
-
<li class="item-5"> <a href="5.html"> first item </a>
-
</ul>
-
</div>
-
'''
-
-
# 利用etree.HTML把字符串解析成HTML文檔
-
html = etree.HTML(text)
-
s = etree.tostring(html)
-
print(s)
案例2.html
-
"1.0" encoding="utf-8" xml version=
-
-
<bookstore>
-
<book category="cooking">
-
<title lang="en">Everyday Italian </title>
-
<author>Gidada De </author>
-
<year>2018 </year>
-
<price>23 </price>
-
</book>
-
-
<book category="education">
-
<title lang="en">Python is Python </title>
-
<author>Food War </author>
-
<year>2008 </year>
-
<price>83 </price>
-
</book>
-
-
<book category="sport">
-
<title lang="en">Running </title>
-
<author>Klaus Kuka </author>
-
<year>2010 </year>
-
<price>43 </price>
-
</book>
-
-
</bookstore>
案例2.py
-
from lxml import etree
-
-
# 只能讀取xml格式內容,html報錯
-
html = etree.parse( "./v30.html")
-
-
rst = etree.tostring(html, pretty_print= True)
-
print(rst)
案例3.py
-
from lxml import etree
-
-
# 只能讀取xml格式內容,html報錯
-
html = etree.parse( "./v30.html")
-
print(type(html))
-
-
rst = html.xpath( '//book')
-
print(type(rst))
-
print(rst)
-
-
# xpath的意識是,查找帶有category屬性值為sport的book元素
-
rst = html.xpath( '//book[@category="sport"]')
-
print(type(rst))
-
print(rst)
-
-
# xpath的意識是,查找帶有category屬性值為sport的book元素下的year元素
-
rst = html.xpath( '//book[@category="sport"]/year')
-
rst = rst[ 0]
-
print(type(rst))
-
print(rst.tag)
-
print(rst.text)
目前有很多xml,html文檔的parser,如標准庫的xml.etree , beautifulsoup , 還有lxml. 都用下來感覺lxml不錯,速度也還行,就他了.
圍繞三個問題:
- 問題1:有一個XML文件,如何解析
- 問題2:解析后,如果查找、定位某個標簽
- 問題3:定位后如何操作標簽,比如訪問屬性、文本內容等
這些操作應該算是比較基礎的,還可以自己在網上查找相關教程,官網更詳細一點,進階xpath語法,要在以后操作xml文件和html文件用上.
-
#!/usr/bin/python
-
# coding=utf-8
-
# __author__='dahu'
-
#
-
'''
-
Element是XML處理的核心類,
-
Element對象可以直觀的理解為XML的節點,大部分XML節點的處理都是圍繞該類進行的。
-
這部分包括三個內容:節點的操作、節點屬性的操作、節點內文本的操作。
-
'''
-
from lxml import etree
-
-
# 1.創建element
-
root = etree.Element( 'root')
-
print root, root.tag
-
-
# 2.添加子節點
-
child1 = etree.SubElement(root, 'child1')
-
child2 = etree.SubElement(root, 'child2')
-
-
# 3.刪除子節點
-
# root.remove(child2)
-
-
# 4.刪除所有子節點
-
# root.clear()
-
-
# 5.以列表的方式操作子節點
-
print(len(root))
-
print root.index(child1) # 索引號
-
root.insert( 0, etree.Element( 'child3')) # 按位置插入
-
root.append(etree.Element( 'child4')) # 尾部添加
-
-
# 6.獲取父節點
-
print(child1.getparent().tag)
-
# print root[0].getparent().tag #用列表獲取子節點,再獲取父節點
-
'''以上都是節點操作'''
-
-
# 7.創建屬性
-
# root.set('hello', 'dahu') #set(屬性名,屬性值)
-
# root.set('hi', 'qing')
-
-
# 8.獲取屬性
-
# print(root.get('hello')) #get方法
-
# print root.keys(),root.values(),root.items() #參考字典的操作
-
# print root.attrib #直接拿到屬性存放的字典,節點的attrib,就是該節點的屬性
-
'''以上是屬性的操作'''
-
-
# 9.text和tail屬性
-
# root.text = 'Hello, World!'
-
# print root.text
-
-
# 10.test,tail和text的結合
-
html = etree.Element( 'html')
-
html.text = 'html.text'
-
body = etree.SubElement(html, 'body')
-
body.text = 'wo ai ni'
-
child = etree.SubElement(body, 'child')
-
child.text= 'child.text' #一般情況下,如果一個節點的text沒有內容,就只有</>符號,如果有內容,才會<>,</>都有
-
child.tail = 'tails' # tail是在標簽后面追加文本
-
print(etree.tostring(html))
-
# print(etree.tostring(html, method='text')) # 只輸出text和tail這種文本文檔,輸出的內容連在一起,不實用
-
-
#11.Xpath方式
-
# print(html.xpath('string()')) #這個和上面的方法一樣,只返回文本的text和tail
-
print(html.xpath( '//text()')) #這個比較好,按各個文本值存放在列表里面
-
tt=html.xpath( '//text()')
-
print tt[ 0].getparent().tag #這個可以,首先我可以找到存放每個節點的text的列表,然后我再根據text找相應的節點
-
# for i in tt:
-
# print i,i.getparent().tag,'\t',
-
-
#12.判斷文本類型
-
print tt[ 0].is_text,tt[ -1].is_tail #判斷是普通text文本,還是tail文本
-
'''以上都是文本的操作'''
-
-
#13.字符串解析,fromstring方式
-
xml_data = '<html>html.text<body>wo ai ni<child>child.text</child>tails</body></html>'
-
root1=etree.fromstring(xml_data) #fromstring,字面意思,直接來源字符串
-
# print root1.tag
-
# print etree.tostring(root1)
-
-
#14.xml方式
-
root2 = etree.XML(xml_data) #和fromstring基本一樣,
-
print etree.tostring(root2)
-
-
#15.文件類型解析
-
tree =etree.parse( 'text') #文件解析成元素樹
-
root3 = tree.getroot() #獲取元素樹的根節點
-
print etree.tostring(root3,pretty_print= True)
-
-
parser= etree.XMLParser(remove_blank_text= True) #去除xml文件里的空行
-
root = etree.XML( "<root> <a/> <b> </b> </root>",parser)
-
print etree.tostring(root)
-
-
#16.html方式
-
xml_data1= '<root>data</root>'
-
root4 = etree.HTML(xml_data1)
-
print(etree.tostring(root4)) #HTML方法,如果沒有<html>和<body>標簽,會自動補上
-
#注意,如果是需要補全的html格式:這樣處理哦
-
with open( "quotes-1.html", 'r') as f:
-
a=H.document_fromstring(f.read().decode( "utf-8"))
-
-
for i in a.xpath( '//div[@class="quote"]/span[@class="text"]/text()'):
-
print i
-
-
#17.輸出內容,輸出xml格式
-
print etree.tostring(root)
-
print(etree.tostring(root, xml_declaration= True,pretty_print= True,encoding= 'utf-8')) #指定xml聲明和編碼
-
'''以上是文件IO操作'''
-
-
#18.findall方法
-
root = etree.XML( "<root><a x='123'>aText<b/><c/><b/></a></root>")
-
print(root.findall( 'a')[ 0].text) #findall操作返回列表
-
print(root.find( './/a').text) #find操作就相當與找到了這個元素節點,返回匹配到的第一個元素
-
print(root.find( 'a').text)
-
print [ b.text for b in root.findall( './/a') ] #配合列表解析,相當帥氣!
-
print(root.findall( './/a[@x]')[ 0].tag) #根據屬性查詢
-
'''以上是搜索和定位操作'''
-
print(etree.iselement(root))
-
print root[ 0] is root[ 1].getprevious() #子節點之間的順序
-
print root[ 1] is root[ 0].getnext()
-
'''其他技能'''
-
# 遍歷元素數
-
root = etree.Element( "root")
-
etree.SubElement(root, "child").text = "Child 1"
-
etree.SubElement(root, "child").text = "Child 2"
-
etree.SubElement(root, "another").text = "Child 3"
-
etree.SubElement(root[ 0], "childson").text = "son 1"
-
# for i in root.iter(): #深度遍歷
-
# for i in root.iter('child'): #只迭代目標值
-
# print i.tag,i.text
-
# print etree.tostring(root,pretty_print=True)