python解析庫lxml的簡單使用


 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>&#13;\n    <ul>&#13;\n         <li class="item-0"><a href="link1.html">first item</a></li>&#13;\n         <li class="item-1"><a href="link2.html">second item</a></li>&#13;\n         <li class="item-inactive"><a href="link3.html">third item</a></li>&#13;\n         <li class="item-1"><a href="link4.html">fourth item</a></li>&#13;\n         <li class="item-0"><a href="link5.html">fifth item</a>&#13;\n     </li></ul>&#13;\n </div>&#13;\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)屬性匹配

在選取的時候,我們還可以用@符號進行屬性過濾。比如,這里如果要選取classitem-1li節點,可以這樣實現:

復制代碼
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安裝可查看上一篇文章)

Element類

1、節點操作

2、屬性操作

3、文本操作

4、文件解析與輸出

5、ElementPath

6、案例(尤其最后的一篇代碼) 


 

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方法,參數即節點名稱。

  1.  
    >>> root = etree.Element( 'root')
  2.  
    >>> print(root)
  3.  
    <Element root at 0x2da0708>

2、獲取節點名稱
使用tag屬性,獲取節點的名稱。

  1.  
    >>> print(root.tag)
  2.  
    root

3、輸出XML內容
使用tostring方法輸出XML內容,參數為Element對象。

  1.  
    >>> print(etree.tostring(root))
  2.  
    b'<root><child1/><child2/><child3/></root>'

4、添加子節點
使用SubElement方法創建子節點,第一個參數為父節點(Element對象),第二個參數為子節點名稱。

  1.  
    >>> child1 = etree.SubElement(root, 'child1')
  2.  
    >>> child2 = etree.SubElement(root, 'child2')
  3.  
    >>> child3 = etree.SubElement(root, 'child3')

5、刪除子節點
使用remove方法刪除指定節點,參數為Element對象。clear方法清空所有節點。

  1.  
    >>> root.remove(child1) # 刪除指定子節點
  2.  
    >>> print(etree.tostring(root))
  3.  
    b'<root><child2/><child3/></root>'
  4.  
    >>> root.clear() # 清除所有子節點
  5.  
    >>> print(etree.tostring(root))
  6.  
    b'<root/>'

6、以列表的方式操作子節點
可以將Element對象的子節點視為列表進行各種操作:

  1.  
    >>> child = root[ 0] # 下標訪問
  2.  
    >>> print(child.tag)
  3.  
    child1
  4.  
     
  5.  
    >>> print(len(root)) # 子節點數量
  6.  
    3
  7.  
     
  8.  
    >>> root.index(child2) # 獲取索引號
  9.  
    1
  10.  
     
  11.  
    >>> for child in root: # 遍歷
  12.  
    ... print(child.tag)
  13.  
    child1
  14.  
    child2
  15.  
    child3
  16.  
     
  17.  
    >>> root.insert( 0, etree.Element( 'child0')) # 插入
  18.  
    >>> start = root[: 1] # 切片
  19.  
    >>> end = root[ -1:]
  20.  
     
  21.  
    >>> print(start[ 0].tag)
  22.  
    child0
  23.  
    >>> print(end[ 0].tag)
  24.  
    child3
  25.  
     
  26.  
    >>> root.append( etree.Element( 'child4') ) # 尾部添加
  27.  
    >>> print(etree.tostring(root))
  28.  
    b'<root><child0/><child1/><child2/><child3/><child4/></root>'

7、獲取父節點
使用getparent方法可以獲取父節點。

  1.  
    >>> print(child1.getparent().tag)
  2.  
    root

2、屬性操作

屬性是以key-value的方式存儲的,就像字典一樣。

1、創建屬性

可以在創建Element對象時同步創建屬性,第二個參數即為屬性名和屬性值:

  1.  
    >>> root = etree.Element( 'root', interesting= 'totally')
  2.  
    >>> print(etree.tostring(root))
  3.  
    b'<root interesting="totally"/>'
  4.  
    也可以使用set方法給已有的Element對象添加屬性,兩個參數分別為屬性名和屬性值:
  5.  
     
  6.  
    >>> root.set( 'hello', 'Huhu')
  7.  
    >>> print(etree.tostring(root))
  8.  
    b'<root interesting="totally" hello="Huhu"/>'

2、獲取屬性

屬性是以key-value的方式存儲的,就像字典一樣。直接看例子

  1.  
    # get方法獲得某一個屬性值
  2.  
    >>> print(root.get( 'interesting'))
  3.  
    totally
  4.  
     
  5.  
    # keys方法獲取所有的屬性名
  6.  
    >>> sorted(root.keys())
  7.  
    [ 'hello', 'interesting']
  8.  
     
  9.  
    # items方法獲取所有的鍵值對
  10.  
    >>> for name, value in sorted(root.items()):
  11.  
    ... print( '%s = %r' % (name, value))
  12.  
    hello = 'Huhu'
  13.  
    interesting = 'totally'

也可以用attrib屬性一次拿到所有的屬性及屬性值存於字典中:

  1.  
    >>> attributes = root.attrib
  2.  
    >>> print(attributes)
  3.  
    { 'interesting': 'totally', 'hello': 'Huhu'}
  4.  
     
  5.  
    >>> attributes[ 'good'] = 'Bye' # 字典的修改影響節點
  6.  
    >>> print(root.get( 'good'))
  7.  
    Bye

3、文本操作

標簽及標簽的屬性操作介紹完了,最后就剩下標簽內的文本了。可以使用text和tail屬性、或XPath的方式來訪問文本內容。 

1、text和tail屬性

一般情況,可以用Element的text屬性訪問標簽的文本。

  1.  
    >>> root = etree.Element( 'root')
  2.  
    >>> root.text = 'Hello, World!'
  3.  
    >>> print(root.text)
  4.  
    Hello, World!
  5.  
    >>> print(etree.tostring(root))
  6.  
    b'<root>Hello, World!</root>'```
  7.  
     
  8.  
    XML的標簽一般是成對出現的,有開有關,但像HTML則可能出現單一的標簽,如下面這段代碼中的`<br/>`
  9.  
     
  10.  
    `<html><body>Text<br/>Tail</body></html>`
  11.  
     
  12.  
    Element類提供了tail屬性支持單一標簽的文本獲取。
  13.  
    ```python
  14.  
    >>> html = etree.Element( 'html')
  15.  
    >>> body = etree.SubElement(html, 'body')
  16.  
    >>> body.text = 'Text'
  17.  
    >>> print(etree.tostring(html))
  18.  
    b'<html><body>Text</body></html>'
  19.  
     
  20.  
    >>> br = etree.SubElement(body, 'br')
  21.  
    >>> print(etree.tostring(html))
  22.  
    b'<html><body>Text<br/></body></html>'
  23.  
     
  24.  
    # tail僅在該標簽后面追加文本
  25.  
    >>> br.tail = 'Tail'
  26.  
    >>> print(etree.tostring(br))
  27.  
    b'<br/>Tail'
  28.  
     
  29.  
    >>> print(etree.tostring(html))
  30.  
    b'<html><body>Text<br/>Tail</body></html>'
  31.  
     
  32.  
    # tostring方法增加method參數,過濾單一標簽,輸出全部文本
  33.  
    >>> print(etree.tostring(html, method= 'text'))
  34.  
    b'TextTail'

2、XPath方式

  1.  
    # 方式一:過濾單一標簽,返回文本
  2.  
    >>> print(html.xpath( 'string()'))
  3.  
    TextTail
  4.  
     
  5.  
    # 方式二:返回列表,以單一標簽為分隔
  6.  
    >>> print(html.xpath( '//text()'))
  7.  
    [ 'Text', 'Tail']

方法二獲得的列表,每個元素都會帶上它所屬節點及文本類型信息,如下:

  1.  
    >>> texts = html.xpath( '//text()'))
  2.  
     
  3.  
    >>> print(texts[ 0])
  4.  
    Text
  5.  
    # 所屬節點
  6.  
    >>> parent = texts[ 0].getparent()
  7.  
    >>> print(parent.tag)
  8.  
    body
  9.  
     
  10.  
    >>> print(texts[ 1], texts[ 1].getparent().tag)
  11.  
    Tail br
  12.  
     
  13.  
    # 文本類型:是普通文本還是tail文本
  14.  
    >>> print(texts[ 0].is_text)
  15.  
    True
  16.  
    >>> print(texts[ 1].is_text)
  17.  
    False
  18.  
    >>> print(texts[ 1].is_tail)
  19.  
    True

4、文件解析與輸出

這部分講述如何將XML文件解析為Element對象,以及如何將Element對象輸出為XML文件。

1. 文件解析

文件解析常用的有fromstring、XML和HTML三個方法。接受的參數都是字符串。

  1.  
    >>> xml_data = '<root>data</root>'
  2.  
     
  3.  
    # fromstring方法
  4.  
    >>> root1 = etree.fromstring(xml_data)
  5.  
    >>> print(root1.tag)
  6.  
    root
  7.  
    >>> print(etree.tostring(root1))
  8.  
    b'<root>data</root>'
  9.  
     
  10.  
    # XML方法,與fromstring方法基本一樣
  11.  
    >>> root2 = etree.XML(xml_data)
  12.  
    >>> print(root2.tag)
  13.  
    root
  14.  
    >>> print(etree.tostring(root2))
  15.  
    b'<root>data</root>'
  16.  
     
  17.  
    # HTML方法,如果沒有<html>和<body>標簽,會自動補上
  18.  
    >>> root3 = etree.HTML(xml_data)
  19.  
    >>> print(root3.tag)
  20.  
    html
  21.  
    >>> print(etree.tostring(root3))
  22.  
    b'<html><body><root>data</root></body></html>'

2. 輸出

輸出其實就是前面一直在用的tostring方法了,這里補充xml_declaration和encoding兩個參數,前者是XML聲明,后者是指定編碼。

  1.  
    >>> root = etree.XML( '<root><a><b/></a></root>')
  2.  
     
  3.  
    >>> print(etree.tostring(root))
  4.  
    b'<root><a><b/></a></root>'
  5.  
     
  6.  
    # XML聲明
  7.  
    >>> print(etree.tostring(root, xml_declaration= True))
  8.  
    b"<?xml version='1.0' encoding='ASCII'?>\n<root><a><b/></a></root>"
  9.  
     
  10.  
    # 指定編碼
  11.  
    >>> print(etree.tostring(root, encoding= 'iso-8859-1'))
  12.  
    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():返回匹配到的第一個元素

  1.  
    >>> root = etree.XML( "<root><a x='123'>aText<b/><c/><b/></a></root>")
  2.  
     
  3.  
    # 查找第一個b標簽
  4.  
    >>> print(root.find( 'b'))
  5.  
    None
  6.  
    >>> print(root.find( 'a').tag)
  7.  
    a
  8.  
     
  9.  
    # 查找所有b標簽,返回Element對象組成的列表
  10.  
    >>> [ b.tag for b in root.findall( './/b') ]
  11.  
    [ 'b', 'b']
  12.  
     
  13.  
    # 根據屬性查詢
  14.  
    >>> print(root.findall( './/a[@x]')[ 0].tag)
  15.  
    a
  16.  
    >>> print(root.findall( './/a[@y]'))
  17.  
    []

以上內容大多來自此原文:Python lxml教程-SKYue

6、案例(尤其最后的一篇代碼) 

  • 解析HTML,案例1.py
  • 文件讀取,案例2.html, 案例2.py
  • etree和XPath的配合使用, 案例3.py

案例1.py

  1.  
    '''
  2.  
    安裝lxml
  3.  
    '''
  4.  
    from lxml import etree
  5.  
     
  6.  
    '''
  7.  
    用lxml來解析HTML代碼
  8.  
    '''
  9.  
     
  10.  
    text = '''
  11.  
    <div>
  12.  
    <ul>
  13.  
    <li class="item-0"> <a href="0.html"> first item </a></li>
  14.  
    <li class="item-1"> <a href="1.html"> first item </a></li>
  15.  
    <li class="item-2"> <a href="2.html"> first item </a></li>
  16.  
    <li class="item-3"> <a href="3.html"> first item </a></li>
  17.  
    <li class="item-4"> <a href="4.html"> first item </a></li>
  18.  
    <li class="item-5"> <a href="5.html"> first item </a>
  19.  
    </ul>
  20.  
    </div>
  21.  
    '''
  22.  
     
  23.  
    # 利用etree.HTML把字符串解析成HTML文檔
  24.  
    html = etree.HTML(text)
  25.  
    s = etree.tostring(html)
  26.  
    print(s)

案例2.html

  1.  
    <?xml version="1.0" encoding="utf-8"?>
  2.  
     
  3.  
    <bookstore>
  4.  
    <book category="cooking">
  5.  
    <title lang="en">Everyday Italian </title>
  6.  
    <author>Gidada De </author>
  7.  
    <year>2018 </year>
  8.  
    <price>23 </price>
  9.  
    </book>
  10.  
     
  11.  
    <book category="education">
  12.  
    <title lang="en">Python is Python </title>
  13.  
    <author>Food War </author>
  14.  
    <year>2008 </year>
  15.  
    <price>83 </price>
  16.  
    </book>
  17.  
     
  18.  
    <book category="sport">
  19.  
    <title lang="en">Running </title>
  20.  
    <author>Klaus Kuka </author>
  21.  
    <year>2010 </year>
  22.  
    <price>43 </price>
  23.  
    </book>
  24.  
     
  25.  
    </bookstore>

案例2.py 

  1.  
    from lxml import etree
  2.  
     
  3.  
    # 只能讀取xml格式內容,html報錯
  4.  
    html = etree.parse( "./v30.html")
  5.  
     
  6.  
    rst = etree.tostring(html, pretty_print= True)
  7.  
    print(rst)

案例3.py 

  1.  
    from lxml import etree
  2.  
     
  3.  
    # 只能讀取xml格式內容,html報錯
  4.  
    html = etree.parse( "./v30.html")
  5.  
    print(type(html))
  6.  
     
  7.  
    rst = html.xpath( '//book')
  8.  
    print(type(rst))
  9.  
    print(rst)
  10.  
     
  11.  
    # xpath的意識是,查找帶有category屬性值為sport的book元素
  12.  
    rst = html.xpath( '//book[@category="sport"]')
  13.  
    print(type(rst))
  14.  
    print(rst)
  15.  
     
  16.  
    # xpath的意識是,查找帶有category屬性值為sport的book元素下的year元素
  17.  
    rst = html.xpath( '//book[@category="sport"]/year')
  18.  
    rst = rst[ 0]
  19.  
    print(type(rst))
  20.  
    print(rst.tag)
  21.  
    print(rst.text)

 

目前有很多xml,html文檔的parser,如標准庫的xml.etree , beautifulsoup  ,  還有lxml. 都用下來感覺lxml不錯,速度也還行,就他了.

圍繞三個問題:

  • 問題1:有一個XML文件,如何解析
  • 問題2:解析后,如果查找、定位某個標簽
  • 問題3:定位后如何操作標簽,比如訪問屬性、文本內容等

這些操作應該算是比較基礎的,還可以自己在網上查找相關教程,官網更詳細一點,進階xpath語法,要在以后操作xml文件和html文件用上.

  1.  
    #!/usr/bin/python
  2.  
    # coding=utf-8
  3.  
    # __author__='dahu'
  4.  
    #
  5.  
    '''
  6.  
    Element是XML處理的核心類,
  7.  
    Element對象可以直觀的理解為XML的節點,大部分XML節點的處理都是圍繞該類進行的。
  8.  
    這部分包括三個內容:節點的操作、節點屬性的操作、節點內文本的操作。
  9.  
    '''
  10.  
    from lxml import etree
  11.  
     
  12.  
    # 1.創建element
  13.  
    root = etree.Element( 'root')
  14.  
    print root, root.tag
  15.  
     
  16.  
    # 2.添加子節點
  17.  
    child1 = etree.SubElement(root, 'child1')
  18.  
    child2 = etree.SubElement(root, 'child2')
  19.  
     
  20.  
    # 3.刪除子節點
  21.  
    # root.remove(child2)
  22.  
     
  23.  
    # 4.刪除所有子節點
  24.  
    # root.clear()
  25.  
     
  26.  
    # 5.以列表的方式操作子節點
  27.  
    print(len(root))
  28.  
    print root.index(child1) # 索引號
  29.  
    root.insert( 0, etree.Element( 'child3')) # 按位置插入
  30.  
    root.append(etree.Element( 'child4')) # 尾部添加
  31.  
     
  32.  
    # 6.獲取父節點
  33.  
    print(child1.getparent().tag)
  34.  
    # print root[0].getparent().tag #用列表獲取子節點,再獲取父節點
  35.  
    '''以上都是節點操作'''
  36.  
     
  37.  
    # 7.創建屬性
  38.  
    # root.set('hello', 'dahu') #set(屬性名,屬性值)
  39.  
    # root.set('hi', 'qing')
  40.  
     
  41.  
    # 8.獲取屬性
  42.  
    # print(root.get('hello')) #get方法
  43.  
    # print root.keys(),root.values(),root.items() #參考字典的操作
  44.  
    # print root.attrib #直接拿到屬性存放的字典,節點的attrib,就是該節點的屬性
  45.  
    '''以上是屬性的操作'''
  46.  
     
  47.  
    # 9.text和tail屬性
  48.  
    # root.text = 'Hello, World!'
  49.  
    # print root.text
  50.  
     
  51.  
    # 10.test,tail和text的結合
  52.  
    html = etree.Element( 'html')
  53.  
    html.text = 'html.text'
  54.  
    body = etree.SubElement(html, 'body')
  55.  
    body.text = 'wo ai ni'
  56.  
    child = etree.SubElement(body, 'child')
  57.  
    child.text= 'child.text' #一般情況下,如果一個節點的text沒有內容,就只有</>符號,如果有內容,才會<>,</>都有
  58.  
    child.tail = 'tails' # tail是在標簽后面追加文本
  59.  
    print(etree.tostring(html))
  60.  
    # print(etree.tostring(html, method='text')) # 只輸出text和tail這種文本文檔,輸出的內容連在一起,不實用
  61.  
     
  62.  
    #11.Xpath方式
  63.  
    # print(html.xpath('string()')) #這個和上面的方法一樣,只返回文本的text和tail
  64.  
    print(html.xpath( '//text()')) #這個比較好,按各個文本值存放在列表里面
  65.  
    tt=html.xpath( '//text()')
  66.  
    print tt[ 0].getparent().tag #這個可以,首先我可以找到存放每個節點的text的列表,然后我再根據text找相應的節點
  67.  
    # for i in tt:
  68.  
    # print i,i.getparent().tag,'\t',
  69.  
     
  70.  
    #12.判斷文本類型
  71.  
    print tt[ 0].is_text,tt[ -1].is_tail #判斷是普通text文本,還是tail文本
  72.  
    '''以上都是文本的操作'''
  73.  
     
  74.  
    #13.字符串解析,fromstring方式
  75.  
    xml_data = '<html>html.text<body>wo ai ni<child>child.text</child>tails</body></html>'
  76.  
    root1=etree.fromstring(xml_data) #fromstring,字面意思,直接來源字符串
  77.  
    # print root1.tag
  78.  
    # print etree.tostring(root1)
  79.  
     
  80.  
    #14.xml方式
  81.  
    root2 = etree.XML(xml_data) #和fromstring基本一樣,
  82.  
    print etree.tostring(root2)
  83.  
     
  84.  
    #15.文件類型解析
  85.  
    tree =etree.parse( 'text') #文件解析成元素樹
  86.  
    root3 = tree.getroot() #獲取元素樹的根節點
  87.  
    print etree.tostring(root3,pretty_print= True)
  88.  
     
  89.  
    parser= etree.XMLParser(remove_blank_text= True) #去除xml文件里的空行
  90.  
    root = etree.XML( "<root> <a/> <b> </b> </root>",parser)
  91.  
    print etree.tostring(root)
  92.  
     
  93.  
    #16.html方式
  94.  
    xml_data1= '<root>data</root>'
  95.  
    root4 = etree.HTML(xml_data1)
  96.  
    print(etree.tostring(root4)) #HTML方法,如果沒有<html>和<body>標簽,會自動補上
  97.  
    #注意,如果是需要補全的html格式:這樣處理哦
  98.  
    with open( "quotes-1.html", 'r') as f:
  99.  
    a=H.document_fromstring(f.read().decode( "utf-8"))
  100.  
     
  101.  
    for i in a.xpath( '//div[@class="quote"]/span[@class="text"]/text()'):
  102.  
    print i
  103.  
     
  104.  
    #17.輸出內容,輸出xml格式
  105.  
    print etree.tostring(root)
  106.  
    print(etree.tostring(root, xml_declaration= True,pretty_print= True,encoding= 'utf-8')) #指定xml聲明和編碼
  107.  
    '''以上是文件IO操作'''
  108.  
     
  109.  
    #18.findall方法
  110.  
    root = etree.XML( "<root><a x='123'>aText<b/><c/><b/></a></root>")
  111.  
    print(root.findall( 'a')[ 0].text) #findall操作返回列表
  112.  
    print(root.find( './/a').text) #find操作就相當與找到了這個元素節點,返回匹配到的第一個元素
  113.  
    print(root.find( 'a').text)
  114.  
    print [ b.text for b in root.findall( './/a') ] #配合列表解析,相當帥氣!
  115.  
    print(root.findall( './/a[@x]')[ 0].tag) #根據屬性查詢
  116.  
    '''以上是搜索和定位操作'''
  117.  
    print(etree.iselement(root))
  118.  
    print root[ 0] is root[ 1].getprevious() #子節點之間的順序
  119.  
    print root[ 1] is root[ 0].getnext()
  120.  
    '''其他技能'''
  121.  
    # 遍歷元素數
  122.  
    root = etree.Element( "root")
  123.  
    etree.SubElement(root, "child").text = "Child 1"
  124.  
    etree.SubElement(root, "child").text = "Child 2"
  125.  
    etree.SubElement(root, "another").text = "Child 3"
  126.  
    etree.SubElement(root[ 0], "childson").text = "son 1"
  127.  
    # for i in root.iter(): #深度遍歷
  128.  
    # for i in root.iter('child'): #只迭代目標值
  129.  
    # print i.tag,i.text
  130.  
    # print etree.tostring(root,pretty_print=True)

 

 

 

出處:https://blog.csdn.net/ydw_ydw/article/details/82227699


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM