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

posted on 2020-06-19 09:28  jack_Meng  阅读(15885)  评论(0编辑  收藏  举报

导航