python3解析库lxml的安装与基本使用


Posted in Python onJune 27, 2018

前言

在爬虫的学习中,我们爬取网页信息之后就是对信息项匹配,这个时候一般是使用正则。但是在使用中发现正则写的不好的时候不能精确匹配(这其实是自己的问题!)所以就找啊找。想到了可以通过标签来进行精确匹配岂不是比正则要快。所以找到了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>
\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 如果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 true
小于或等于 age 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/

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
Python中的二叉树查找算法模块使用指南
Jul 04 Python
Python 性能优化技巧总结
Nov 01 Python
详解如何利用Cython为Python代码加速
Jan 27 Python
Python利用Django如何写restful api接口详解
Jun 08 Python
python版本单链表实现代码
Sep 28 Python
Python求两个圆的交点坐标或三个圆的交点坐标方法
Nov 07 Python
python 实现在tkinter中动态显示label图片的方法
Jun 13 Python
Pytorch中index_select() 函数的实现理解
Nov 19 Python
pytorch实现mnist分类的示例讲解
Jan 10 Python
python实现人工蜂群算法
Sep 18 Python
python中的垃圾回收(GC)机制
Sep 21 Python
Python中Cookies导出某站用户数据的方法
May 17 Python
详解Python安装scrapy的正确姿势
Jun 26 #Python
python3解析库BeautifulSoup4的安装配置与基本用法
Jun 26 #Python
Python 修改列表中的元素方法
Jun 26 #Python
Python 找到列表中满足某些条件的元素方法
Jun 26 #Python
python3解析库pyquery的深入讲解
Jun 26 #Python
Python将list中的string批量转化成int/float的方法
Jun 26 #Python
python中从str中提取元素到list以及将list转换为str的方法
Jun 26 #Python
You might like
PHP simple_html_dom.php+正则 采集文章代码
2009/12/24 PHP
php中++i 与 i++ 的区别
2012/08/08 PHP
apache和php之间协同工作的配置经验分享
2013/04/08 PHP
在html文件中也可以执行php语句的方法
2015/04/09 PHP
php 删除指定文件夹的实例讲解
2017/07/25 PHP
PHP重置数组为连续数字索引的几种方式总结
2018/03/12 PHP
基于Laravel(5.4版本)的基本增删改查操作方法
2019/10/11 PHP
PHP copy函数使用案例代码解析
2020/09/01 PHP
js变量以及其作用域详解
2020/07/18 Javascript
如何使用Javascript正则表达式来格式化XML内容
2013/07/04 Javascript
jquery任意位置浮动固定层插件用法实例
2015/05/29 Javascript
JavaScript常用数组算法小结
2016/02/13 Javascript
jQuery实现手机自定义弹出输入框
2016/06/13 Javascript
JavaScript 对象详细整理总结
2016/09/29 Javascript
提高JavaScript执行效率的23个实用技巧
2017/03/01 Javascript
微信小程序学习之数据处理详解
2017/07/05 Javascript
用js屏蔽被http劫持的浮动广告实现方法
2017/08/10 Javascript
javascript变量提升和闭包理解
2018/03/12 Javascript
详解html-webpack-plugin插件(用法总结)
2018/09/12 Javascript
[02:30]DOTA2英雄基础教程 暗影恶魔
2013/12/17 DOTA
[46:47]2014 DOTA2国际邀请赛中国区预选赛 DT VS HGT
2014/05/22 DOTA
[04:16]完美世界DOTA2联赛PWL S2 集锦第一期
2020/11/23 DOTA
详解Python中内置的NotImplemented类型的用法
2015/03/31 Python
Python pymysql模块安装并操作过程解析
2020/10/13 Python
Microsoft新加坡官方网站:购买微软最新软件和技术产品
2016/10/28 全球购物
联想法国官方网站:Lenovo法国
2018/10/18 全球购物
德国电子产品购物网站:TechInTheBasket德国
2018/12/07 全球购物
什么是GWT的Entry Point
2013/08/16 面试题
大学生毕业自我鉴定范文
2013/09/19 职场文书
自主招生自荐信格式
2013/12/03 职场文书
历史专业个人求职信分享
2013/12/20 职场文书
关于感谢信的范文
2015/01/23 职场文书
课文《燕子》教学反思
2016/02/17 职场文书
sql注入教程之类型以及提交注入
2021/08/02 MySQL
python_tkinter事件类型详情
2022/03/20 Python
HTML 里 img 元素的 src 和 srcset 属性的区别详解
2023/05/21 HTML / CSS