Python之lxml模块的使用:

  • 1. 认识lxml
  • 2. lxml中基本使用
  • 2.1 安装并导入lxml模块
  • 2.2 节点操作:
  • 2.3 属性操作:
  • 2.4 文本操作
  • 2.5 xml文件解析与序列化
  • 2.6 lxml命名空间的处理
  • 3. 使用lxml解析xml案例
  • 4. 使用lxml生成一个xml文件案例:
  • 5. 补充:xPath语法
  • 5.1 节点选择语法:
  • 5.2 节点修饰语法:
  • 5.3 谷歌浏览器xpath helper插件的安装和使用


1. 认识lxml

lxml是一款高性能的Python XML库,主要用来解析及生成xml和html文件(解析、序列化、转换)。其天生支持Xpath1.0、XSLT1.0、定制元素类,甚至 python 风格的数据绑定接口。lxml基于Cpython实现,其底层是libxml2和libxslt两个C语言库。因此具有较高的性能。

官方文档:https://lxml.de/

2. lxml中基本使用

在lxml中,lxml.etree模块是最常用的HTML、XML文档解析模块。其中lxml.etree.Element是处理xml的一个核心类,Element对象可以直观的理解为是XML中的节点。使用Element类,可以实现对XML节点、节点属性、节点内文本的操作。

2.1 安装并导入lxml模块

pip install lxml
from lxml import etree

2.2 节点操作:

1.创建节点(创建Element对象):

root = etrre.Element('root')
print(root)

2.获取节点名称

print(root.tag)

3.添加子节点

添加子节点的方法有三种:1、直接使用SubElement方法添加子节点。2、创建一个Element对象,使用append的方法将该对象追加到父节点中。3、创建一个Element对象,使用insert方法,将对象添加到父节点指定位置。

  • 使用SubElement方法添加:
child_sub = etree.SubElement(root, 'child_sub')
  • 使用append方法添加:
child_append = etree.Element('child_append')
root.append(child_append)
  • 使用insert方法添加:
child_insert = etree.Element('child_insert')
root.insert(0, child_append)  #  第一个参数为添加的位置,第二个参数为添加的Element对象

4.删除子节点:

可以使用remove()方法来删除指定子节点。使用clear()方法来清空所有子节点

root.remove(child_sub)  # 删除名字为child_sub节点
root.clear()  # 清空root的所有子节点

5.访问节点

在Element对象中,访问节点的方法有多种。

  • 可以通过列表的方式来方位节点。
  • 可以通过getparent()等方法来访问节点。
  • 可以通过xpath语法来定位指定节点(暂不介绍)

(1) 通过列表的方式来访问节点:

child_sub = root[0]  # 通过下标来访问子节点
child_sub = root[0: 1][0]  # 通过切片的方式来访问节点
for c in root:  # 通过遍历来获取所有节点
    print(c.tag)
    
c_append_index = root.index(child_append)  # 获取节点的索引
print(len(root))  # 获取子节点的数量

(2) 通过方法来访问节点:

  • 获取父节点:getarent()
  • 获取所有子节点:getchildren()
  • 获取根节点:getroot()
  • findall():返回所有匹配的元素,返回列表
  • find():返回匹配到的第一个元素
print(child_sub.getparent().tag)  # 查询父节点
print(root.getchildren())  # 查询所有子节点
print(root.getroot())  # 获取根节点
print(root.find('b'))  # 查询第一个b标签
print(root.findall('.//b'))  # 查询所有b标签

2.3 属性操作:

在Element中,节点的属性是以字典的形式存储的。

创建属性:

创建属性的方式有两种。1、在创建节点的时候创建属性。2、使用set()方法创建属性。

root = etree.Element('root', language='中文')  # 创建节点时创建属性

root.set('hello', 'python')  # 使用set方法为root节点添加属性

获取属性:

print(root.get('language'))  # 使用get方法获取属性
print(root['language'])
print(root.keys())
print(root.values())
print(root.items())

修改属性:

root['language'] = 'English'

2.4 文本操作

在lxml中访问xml文本的方式有多种,可以使用text、tail属性的方式访问文本。也可以使用xpath语法访问文本。这里只介绍使用text和tail获取和设置文本的属性的方法。xpath后面会具体介绍。

text和tail属性 的区别:

xml中标签一般是成对出现的。但在HTML中则可能会出现单标签,如<html><body>text<br/>tail</body></html>

  • text属性用于成对便签的读取和设置
  • tail属性用于单一标签的读取和设置
html = etree.Element('html')
body = etree.SubElement(html, 'body')
body.text = 'text'  # 给body标签内写入text文本内容

br = etree.SubElement('body', 'br')
br.tail = 'tail'  # 在br标签中写入tail文本内容

2.5 xml文件解析与序列化

1. xml文件解析的方法:
xml文件解析的方法有多种,常用的有fromstring、XML、HTML、parse。其中XML和HTML的参数既可以是字符串、也可以是二进制的字节码。

  • fromstring、XML、parse:返回的是一个Element对象,是一个节点。主要用于解析文档碎片。
  • parse(): 返回值是一个ElementTree类型的对象,完整的xml树结构。parse主要用来解析完整的文档,而不是Element对象。
  • 参数:
    打开的文件或文件类型对象(建议以二进制形式打开
    文件名或字符串
    HTTP或者FTP的url。
    注意:从文件名或者url解析通常比从文件对象解析要快
xml_data = '<root>data</root>'

 # fromstring
root_str = etree.formstring(xml_data)
print(root_str.tag)

 # XML
root_xml = etree.XML(xml_data)
print(root_xml.tag)

 # HTML,如果没有<html>和<body>标签,则会自动补上
 root_html = etree.HTML(xml_data)
 print(root_html.tag)
 
 # parse中的参数应该是一个完整的xml或html,同样返回值是一个ElementTree类型的对象,完整的xml树结构。parse主要用来解析完整的文档。
tree =etree.parse('text')   #文件解析成元素树
root = tree.getroot()      #获取元素树的根节点
print etree.tostring(root, pretty_print=True)

2. xml文件序列化的方法:

我们在生成一个xml文件是有两种方式:1、将Element对象转换成一个xml字符串,然后将其写入到文件中。2、使用ElementTreee对象中的write()方法直接将xml写入文件。

root = '<root>data</root>'

# 将Element对象转换成xml字符串写入文件
root_str = element.tostring(root, pretty_print=True, xml_declartion=True, encoding='utf-8')
with open('text.xml', 'w', encoding='utf-8') as f:
    f.write(root_str)


# 将节点(Element对象)转为ElementTree对象。
tree = etree.ElementTree(root)
tree.write('text.xml', pretty_print=True, xml_declartion=True, encoding='utf-8')

参数含义:

  • 第一个参数是xml保持的路径(包括文件名)
  • pretty_print:是否格式化xml(美化)
  • xml_declaration:是否写入xml声明,就是xml中开头第一行文字。
  • encoding:编码格式

补充:ElementTree对象可理解为一个完整的XML树,每个节点都是一个Element对象。而ElementPath则相当于XML中的XPath。用于搜索和定位Element元素。


2.6 lxml命名空间的处理

什么是命名空间?https://www.w3school.com.cn/xml/xml_namespaces.asp

带有命名空间的xml解析处理:

from lxml import etree

str_xml = """
<A xmlns="http://This/is/a/namespace">
    <B>dataB1</B>
    <B>dataB2</B>
    <B><C>datac</C></B>
</A>
"""

xml = etree.fromstring(str_xml)  # 解析字符串
ns = xml.nsmap  # 获取命名空间
print(ns)
print(ns[None])

>>> {None: 'http://This/is/a/namespace'}
>>> http://This/is/a/namespace

ns = xml.nsmap[None]  # 获取命名空间xmlns

# 1. 使用findall方法查找指定节点。
for item in xml.findall(f'{ns}b')
	print(item.text)
    
# 2. 使用xpath语法加命名空间查找指定节点
ns = {'x':root.nsmap[None]}  # 获取命名空间
b = root.xpath("//x:B", namespaces=ns)
print(b)

C = root.xapth("//x:B/X:C", namespaces=ns)
print(c)

注意:当xml携带有命名空间(xmlns)的时候,在查找节点时,每一级节点都需要加上命名空间。如果不携带命名空间,是无法查询到该节点的。
除此之外处理命名空间还有一个非常shao的方法,即将所有的命名空间都替换为空,将其当成普通的节点进行处理。


3. 使用lxml解析xml案例

(1)导入lxml 的 etree 库

from lxml import etree

(2)利用etree.HTML,将html字符串(bytes类型或str类型)转化为Element对象,Element对象具有xpath的方法,返回结果的列表

html = etree.HTML(text)
ret_list = html.xpath("xpath语法规则字符串")

(3)xpath方法返回列表的三种情况

  • 返回空列表:根据xpath语法规则字符串,没有定位到任何元素
  • 返回由字符串构成的列表:xpath字符串规则匹配的一定是文本内容或某属性的值
  • 返回由Element对象构成的列表:xpath规则字符串匹配的是标签,列表中的Element对象可以继续进行xpath
from lxml import etree
text = '''
<div>
  <ul>
    <li class="item-1">
      <a href="link1.html">first item</a>
    </li>
    <li class="item-1">
      <a href="link2.html">second item</a>
    </li>
    <li class="item-inactive">
      <a href="link3.html">third item</a>
    </li>
    <li class="item-1">
      <a href="link4.html">fourth item</a>
    </li>
    <li class="item-0">
      a href="link5.html">fifth item</a>
  </ul>
</div>
'''

html = etree.HTML(text)  # 也可以使用XML和fromstring方法

# 获取所有的class属性为item-1的href属性
href_list = html.xpath('//li[@class="item-1"]/a/@href')
# 获取所有的class属性为item-1的text内容
text_list = html.xpath('//li[@class="item-1"]/a/text()')

# 组装成字典
for href, title in zip(href_list, title_list):
    print({f'{href}': f'{title}'})

注意:lxml.etree.HTML(html_str)方法可以自动补全不完整的标签。



4. 使用lxml生成一个xml文件案例:

from lxml import etree

# 创建element对象
root = etree.Element('root')
print(root.tag)

# 添加子节点
child_sub = etree.SubElement(root, 'child_sub')

child = etree.Element('child')
child_append = root.append(child)  # 通过append向root节点里面追加子节点
child_insert = root.insert(0, child)  # 通过insert向root节点开始的位置添加子节点

# 3.删除子节点
# root.remove(child2)

# 4.删除所有子节点
# root.clear()

# 5.以列表的方式操作子节点
print(len(root))
print root.index(child)  # 索引号

# 6.生成xml字符串写入xml文件
# 将Element对象转换成xml字符串写入文件
root_str = etree.tostring(root, pretty_print=True, xml_declaration=True, encoding='utf-8')
with open('text.xml', 'wb') as f:
    f.write(root_str)

# 将节点(Element对象)转为ElementTree对象。
tree = etree.ElementTree(root)
tree.write('text.xml', pretty_print=True, xml_declartion=True, encoding='utf-8')


5. 补充:xPath语法

  • XPath (XML Path Language) 是一门在 HTML\XML 文档中查找信息的语言,可用来在 HTML\XML文档中对元素和属性进行遍历
  • W3School官方文档:http://www.w3school.com.cn/xpath/index.asp
  • 提取xml、html中的数据一般需要将lxml模块和xpath语法配合使用

5.1 节点选择语法:

xpath定位节点以及提取属性或文本内容的语法:

表达式

描述

nodename

选中该元素。

/

从根节点选取、或者是元素和元素间的过渡。

//

从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。

.

选取当前节点。

..

选取当前节点的父节点。

@

选取属性。

text()

选取文本。

注意:@符号出现在末尾时,用来提取属性;出现在[]中时是用来匹配属性

选取未知节点的语法:
可以通过通配符来选取未知的html、xml的元素

通配符

描述

*

匹配任何元素节点。

node()

匹配任何类型的节点。

  • 全部的标签://*
  • 全部的属性://node()

5.2 节点修饰语法:

可以根据标签的属性值、下标等来获取特定的节点

路径表达式

结果

//title[@lang=“eng”]

选择lang属性值为eng的所有title元素

/bookstore/book[1]

选取属于 bookstore 子元素的第一个 book 元素。

/bookstore/book[last()]

选取属于 bookstore 子元素的最后一个 book 元素。

/bookstore/book[last()-1]

选取属于 bookstore 子元素的倒数第二个 book 元素。

/bookstore/book[position()>1]

选择bookstore下面的book元素,从第二个开始选择

//book/title[text()=‘Harry Potter’]

选择所有book下的title元素,仅仅选择文本为Harry Potter的title元素

//book/title[contains(text(), “Harry”)]

选择所有book下的文本包含Harry的title元素

/bookstore/book[price>35.00]/title

选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

关于xpath的下标:

  • 在xpath中,第一个元素的位置是1
  • 最后一个元素的位置是last()
  • 倒数第二个是last()-1

5.3 谷歌浏览器xpath helper插件的安装和使用

要想利用lxml模块提取数据,需要我们掌握xpath语法规则。接下来我们就来了解一下xpath helper插件,它可以帮助我们练习xpath语法。

xpath helper插件的安装:

1.下载Chrome插件 XPath Helper:

2.把文件的后缀名crx改为rar,然后解压到同名文件夹中

3.把解压后的文件夹拖入到已经开启开发者模式的chrome浏览器扩展程序界面

4.重启浏览器后,访问url之后在页面中点击xpath图标,就可以使用了

5.如果是linux或macOS操作系统,无需操作上述的步骤2,直接将crx文件拖入已经开启开发者模式的chrome浏览器扩展程序界面