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 相关文章推荐
使用graphics.py实现2048小游戏
Mar 10 Python
python实现在字符串中查找子字符串的方法
Jul 11 Python
python中利用xml.dom模块解析xml的方法教程
May 24 Python
python爬虫爬取淘宝商品信息(selenum+phontomjs)
Feb 24 Python
python多线程并发让两个LED同时亮的方法
Feb 18 Python
解决pycharm下os.system执行命令返回有中文乱码的问题
Jul 07 Python
python:按行读入,排序然后输出的方法
Jul 20 Python
python 检查数据中是否有缺失值,删除缺失值的方式
Dec 02 Python
pytorch常见的Tensor类型详解
Jan 15 Python
python Shapely使用指南详解
Feb 18 Python
Python csv文件记录流程代码解析
Jul 16 Python
Elasticsearch 聚合查询和排序
Apr 19 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
Zend studio for eclipse中使php可以调用mysql相关函数的设置方法
2008/10/13 PHP
php_xmlhttp 乱码问题解决方法
2009/08/07 PHP
DW中链接mysql数据库时,建立字符集中文出现乱码的解决方法
2010/03/27 PHP
网页上facebook分享功能具体实现
2014/01/26 PHP
利用PHP获取网站访客的所在地位置
2017/01/18 PHP
PHP大文件分片上传的实现方法
2018/10/28 PHP
表单的焦点顺序tabindex和对应enter键提交
2013/01/04 Javascript
重构Javascript代码示例(重构前后对比)
2013/01/23 Javascript
JS控制图片等比例缩放的示例代码
2013/12/24 Javascript
Node.js实现简单聊天服务器
2014/06/20 Javascript
基于jQuery实现二级下拉菜单效果
2016/02/01 Javascript
易被忽视的js事件问题总结
2016/05/14 Javascript
jQuery仿京东商城楼梯式导航定位菜单
2016/07/25 Javascript
js方法数据验证的简单实例
2016/09/17 Javascript
javascript循环链表之约瑟夫环的实现方法
2017/01/16 Javascript
canvas 绘制圆形时钟
2017/02/22 Javascript
利用vue+elementUI实现部分引入组件的方法详解
2017/11/22 Javascript
vuex进阶知识点巩固
2018/05/20 Javascript
JavaScript中reduce()的5个基本用法示例
2020/07/19 Javascript
Element Dialog对话框的使用示例
2020/07/26 Javascript
pycharm 使用心得(九)解决No Python interpreter selected的问题
2014/06/06 Python
Python3实现生成随机密码的方法
2014/08/23 Python
python实现自动更换ip的方法
2015/05/05 Python
TF-IDF算法解析与Python实现方法详解
2017/11/16 Python
Python实现生成随机日期字符串的方法示例
2017/12/25 Python
python爬取网页转换为PDF文件
2018/06/07 Python
scrapy-redis源码分析之发送POST请求详解
2019/05/15 Python
详解基于python的全局与局部序列比对的实现(DNA)
2020/10/07 Python
希腊品牌鞋类销售网站:epapoutsia.gr
2020/03/18 全球购物
大学本科毕业生求职信范文
2013/12/18 职场文书
趣味运动会策划方案
2014/06/02 职场文书
领导班子三严三实对照检查材料
2014/09/25 职场文书
2014个人年度工作总结
2014/12/15 职场文书
2019最新版火锅店的创业计划书 !
2019/07/12 职场文书
【DOTA2】当街暴打?PSG LGD vs VG - DPC 2022 WINTER TOUR CN
2022/04/02 DOTA
mysql数据库隔离级别详解
2022/06/16 MySQL