python中lxml模块的使用,Python 中 xpath 语法 与 lxml 库解析 HTML/XML 和 CSS Selector
lxml是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高
xml的安装,导入,使用
1.lxml的安装
pip install lxml
2.导入lxml 的 etree 库
from lxml import etree
# etree全称:ElementTree 元素树
step3: selector = etree.HTML(网页源代码)
step4: selector.xpath(一段神奇的符号)
3.利用etree.HTML,将字符串转化为Element对象,Element对象具有xpath的方法,返回结果的列表,能够接受bytes类型的数据和str类型的数据。
from lxml import etree
html = etree.HTML(response.text)
ret_list = html.xpath("xpath字符串")
也可以这样使用:
from lxml import etree
htmlDiv = etree.HTML(response.content.decode())
hrefs = htmlDiv.xpath("//h4//a/@href")
4.把转化后的element对象转化为字符串,返回bytes类型,etree.tostring(element)
假设我们现有如下的html字符换,尝试对他进行操作:
<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> # 注意,此处缺少一个 </li> 闭合标签
</ul> </div>
5 etree.tostring
handeled_html_str = etree.tostring(html).decode()
print(handeled_html_str)
可以发现,lxml确实能够把确实的标签补充完成,但是请注意lxml是人写的,很多时候由于网页不够规范,或者是lxml的bug。
即使参考url地址对应的响应去提取数据,任然获取不到,这个时候我们需要使用etree.tostring的方法,观察etree到底把html转化成了什么样子,即根据转化后的html字符串去进行数据的提取。
————————————————
版权声明:本文为CSDN博主「埃菲尔没有塔尖」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38819889/article/details/103551896
python3 lxml用法
python3.5 lxml用法
问题1:有一个XML文件,如何解析
问题2:解析后,如果查找、定位某个标签
问题3:定位后如何操作标签,比如访问属性、文本内容等
开始之前,首先是导入模块,该库常用的XML处理功能都在lxml.etree中
导入模块:from lxml import etree
Element类
Element是XML处理的核心类,Element对象可以直观的理解为XML的节点,大部分XML节点的处理都是围绕该类进行的。
这部分包括三个内容:节点的操作、节点属性的操作、节点内文本的操作。
节点操作
1、创建Element对象
直接使用Element方法,参数即节点名称。
root = etree.Element(‘root’)
print(root)
2、获取节点名称
使用tag属性,获取节点的名称。
print(root.tag)
root
3、输出XML内容
使用tostring方法输出XML内容(后文还会有补充介绍),参数为Element对象。
print(etree.tostring(root))
b’’
4、添加子节点
使用SubElement方法创建子节点,第一个参数为父节点(Element对象),第二个参数为子节点名称。
child1 = etree.SubElement(root, ‘child1’)
child2 = etree.SubElement(root, ‘child2’)
child3 = etree.SubElement(root, ‘child3’)
5、删除子节点
使用remove方法删除指定节点,参数为Element对象。clear方法清空所有节点。
root.remove(child1) # 删除指定子节点
print(etree.tostring(root))
b’’
root.clear() # 清除所有子节点
print(etree.tostring(root))
b’’
6、以列表的方式操作子节点
可以将Element对象的子节点视为列表进行各种操作:
child = root[0] # 下标访问
print(child.tag)
child1
print(len(root)) # 子节点数量
3
root.index(child2) # 获取索引号
1
for child in root: # 遍历
… print(child.tag)
child1
child2
child3
root.insert(0, etree.Element(‘child0’)) # 插入
start = root[:1] # 切片
end = root[-1:]
print(start[0].tag)
child0
print(end[0].tag)
child3
root.append( etree.Element(‘child4’) ) # 尾部添加
print(etree.tostring(root))
b’’
其实前面讲到的删除子节点的两个方法remove和clear也和列表相似。
7、获取父节点
使用getparent方法可以获取父节点。
print(child1.getparent().tag)
root
属性操作
属性是以key-value的方式存储的,就像字典一样。
1、创建属性
可以在创建Element对象时同步创建属性,第二个参数即为属性名和属性值:
root = etree.Element(‘root’, interesting=’totally’)
print(etree.tostring(root))
b’’
也可以使用set方法给已有的Element对象添加属性,两个参数分别为属性名和属性值:
root.set(‘hello’, ‘Huhu’)
print(etree.tostring(root))
b’’
2、获取属性
属性是以key-value的方式存储的,就像字典一样。直接看例子
get方法获得某一个属性值
print(root.get(‘interesting’))
totally
keys方法获取所有的属性名
sorted(root.keys())
[‘hello’, ‘interesting’]
items方法获取所有的键值对
for name, value in sorted(root.items()):
… print(‘%s = %r’ % (name, value))
hello = ‘Huhu’
interesting = ‘totally’
也可以用attrib属性一次拿到所有的属性及属性值存于字典中:
attributes = root.attrib
print(attributes)
{‘interesting’: ‘totally’, ‘hello’: ‘Huhu’}
attributes[‘good’] = ‘Bye’ # 字典的修改影响节点
print(root.get(‘good’))
Bye
文本操作
标签及标签的属性操作介绍完了,最后就剩下标签内的文本了。
可以使用text和tail属性、或XPath的方式来访问文本内容。
1、text 和 tail 属性
一般情况,可以用Element的text属性访问标签的文本。
root = etree.Element(‘root’)
root.text = ‘Hello, World!’
print(root.text)
Hello, World!
print(etree.tostring(root))
b’Hello, World!’
Element类提供了tail属性支持单一标签的文本获取。
html = etree.Element(‘html’)
body = etree.SubElement(html, ‘body’)
body.text = ‘Text’
print(etree.tostring(html))
b’Text’
br = etree.SubElement(body, ‘br’)
print(etree.tostring(html))
b’Text’
tail仅在该标签后面追加文本
br.tail = ‘Tail’
print(etree.tostring(br))
b’
Tail’
print(etree.tostring(html))
b’Text
Tail’
tostring方法增加method参数,过滤单一标签,输出全部文本
print(etree.tostring(html, method=’text’))
b’TextTail’
2、XPath方式
方式一:过滤单一标签,返回文本
print(html.xpath(‘string()’))
TextTail
方式二:返回列表,以单一标签为分隔
print(html.xpath(‘//text()’))
[‘Text’, ‘Tail’]
方法二获得的列表,每个元素都会带上它所属节点及文本类型信息,如下:
texts = html.xpath(‘//text()’))
print(texts[0])
Text
所属节点
parent = texts[0].getparent()
print(parent.tag)
body
print(texts[1], texts[1].getparent().tag)
Tail br
文本类型:是普通文本还是tail文本
print(texts[0].is_text)
True
print(texts[1].is_text)
False
print(texts[1].is_tail)
True
文件 解析 与 输出
回答问题1。
这部分讲述如何将XML文件解析为Element对象,以及如何将Element对象输出为XML文件。
1、文件解析
文件解析常用的有fromstring、XML 和 HTML 三个方法。接受的参数都是字符串。
xml_data = ‘data’
fromstring方法
root1 = etree.fromstring(xml_data)
print(root1.tag)
root
print(etree.tostring(root1))
b’data’
XML方法,与fromstring方法基本一样
root2 = etree.XML(xml_data)
print(root2.tag)
root
print(etree.tostring(root2))
b’data’
HTML方法,如果没有和标签,会自动补上
root3 = etree.HTML(xml_data)
print(root3.tag)
html
print(etree.tostring(root3))
b’data’
2、输出
输出其实就是前面一直在用的tostring方法了,这里补充xml_declaration和encoding两个参数,前者是XML声明,后者是指定编码。
root = etree.XML(‘‘)
print(etree.tostring(root))
b’’
XML声明
print(etree.tostring(root, xml_declaration=True))
b”
指定编码
print(etree.tostring(root, encoding=’iso-8859-1’))
b”
查找第一个b标签
print(root.find(‘b’))
None
print(root.find(‘a’).tag)
a
查找所有b标签,返回Element对象组成的列表
[ b.tag for b in root.findall(‘.//b’) ]
[‘b’, ‘b’]
根据属性查询
print(root.findall(‘.//a[@x]’)[0].tag)
a
print(root.findall(‘.//a[@y]’))
[]
————————————————
版权声明:本文为CSDN博主「「已注销」」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lyshark_lyshark/article/details/125846734
lxml属性操作
属性是以key-value的方式存储的,就像字典一样。
1、创建属性
可以在创建Element对象时同步创建属性,第二个参数即为属性名和属性值:
root = etree.Element(‘root’, interesting=’totally’)
print(etree.tostring(root))
b’’
也可以使用set方法给已有的Element对象添加属性,两个参数分别为属性名和属性值:
root.set(‘hello’, ‘Huhu’)
print(etree.tostring(root))
b’’
2、获取属性
属性是以key-value的方式存储的,就像字典一样。直接看例子
get方法获得某一个属性值
print(root.get(‘interesting’))
totally
keys方法获取所有的属性名
sorted(root.keys())
[‘hello’, ‘interesting’]
items方法获取所有的键值对
for name, value in sorted(root.items()):
… print(‘%s = %r’ % (name, value))
hello = ‘Huhu’
interesting = ‘totally’
也可以用attrib属性一次拿到所有的属性及属性值存于字典中:
attributes = root.attrib
print(attributes)
{‘interesting’: ‘totally’, ‘hello’: ‘Huhu’}
attributes[‘good’] = ‘Bye’ # 字典的修改影响节点
print(root.get(‘good’))
Bye
lxml 文本操作
标签及标签的属性操作介绍完了,最后就剩下标签内的文本了。
可以使用text和tail属性、或XPath的方式来访问文本内容。
1、text 和 tail 属性
一般情况,可以用Element的text属性访问标签的文本。
root = etree.Element(‘root’)
root.text = ‘Hello, World!’
print(root.text)
Hello, World!
print(etree.tostring(root))
b’Hello, World!’
Element类提供了tail属性支持单一标签的文本获取。
html = etree.Element(‘html’)
body = etree.SubElement(html, ‘body’)
body.text = ‘Text’
print(etree.tostring(html))
b’Text’
br = etree.SubElement(body, ‘br’)
print(etree.tostring(html))
b’Text’
tail仅在该标签后面追加文本
br.tail = ‘Tail’
print(etree.tostring(br))
b’
Tail’
print(etree.tostring(html))
b’Text
Tail’
tostring方法增加method参数,过滤单一标签,输出全部文本
print(etree.tostring(html, method=’text’))
b’TextTail’
2、XPath方式
方式一:过滤单一标签,返回文本
print(html.xpath(‘string()’))
TextTail
方式二:返回列表,以单一标签为分隔
print(html.xpath(‘//text()’))
[‘Text’, ‘Tail’]
方法二获得的列表,每个元素都会带上它所属节点及文本类型信息,如下:
texts = html.xpath(‘//text()’))
print(texts[0])
Text
所属节点
parent = texts[0].getparent()
print(parent.tag)
body
print(texts[1], texts[1].getparent().tag)
Tail br
文本类型:是普通文本还是tail文本
print(texts[0].is_text)
True
print(texts[1].is_text)
False
print(texts[1].is_tail)
True
文件 解析 与 输出
回答问题1。
这部分讲述如何将XML文件解析为Element对象,以及如何将Element对象输出为XML文件。
1、文件解析
文件解析常用的有fromstring、XML 和 HTML 三个方法。接受的参数都是字符串。
xml_data = ‘data’
fromstring方法
root1 = etree.fromstring(xml_data)
print(root1.tag)
root
print(etree.tostring(root1))
b’data’
XML方法,与fromstring方法基本一样
root2 = etree.XML(xml_data)
print(root2.tag)
root
print(etree.tostring(root2))
b’data’
HTML方法,如果没有和标签,会自动补上
root3 = etree.HTML(xml_data)
print(root3.tag)
html
print(etree.tostring(root3))
b’data’
2、输出
输出其实就是前面一直在用的tostring方法了,这里补充xml_declaration和encoding两个参数,前者是XML声明,后者是指定编码。
root = etree.XML(‘‘)
print(etree.tostring(root))
b’’
指定编码
print(etree.tostring(root, encoding=’iso-8859-1’))
b”
查找第一个b标签
print(root.find(‘b’))
None
print(root.find(‘a’).tag)
a
查找所有b标签,返回Element对象组成的列表
[ b.tag for b in root.findall(‘.//b’) ]
[‘b’, ‘b’]
根据属性查询
print(root.findall(‘.//a[@x]’)[0].tag)
a
print(root.findall(‘.//a[@y]’))
[]
XPath语法
XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。XPath 是 W3C XSLT 标准的主要元素,并且 XQuery 和 XPointer 都构建于 XPath 表达之上。
在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。
根节点在xpath中可以用 “//” 来啊表示
XPath 常用规则
表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
* 通配符,选择所有元素节点与元素名
@* 选取所有属性
[@attrib] 选取具有给定属性的所有元素
[@attrib='value'] 选取给定属性具有给定值的所有元素
[tag] 选取所有具有指定元素的直接子节点
[tag='text'] 选取所有具有指定元素并且文本内容是text节点
参考文献
The lxml.etree Tutorial :https://lxml.de/tutorial.html
python3 解析 xml:https://www.cnblogs.com/deadwood-2016/p/8116863.html
微软文档: XPath 语法 和 XPath 函数
W3school Xpath 教程:http://www.w3school.com.cn/xpath/
Xpath 菜鸟教程:http://www.runoob.com/xpath/xpath-tutorial.html
简书:Xpath高级用法:https://www.jianshu.com/p/1575db75670f
30个示例手把手教你学会Xpath高级用法:https://www.sohu.com/a/211716225_236714
了解XPath常用术语和表达式解析 十分钟轻松入门:http://www.bazhuayu.com/blog/2014091
Python lxml语法
XPath语法
XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。XPath 是 W3C XSLT 标准的主要元素,并且 XQuery 和 XPointer 都构建于 XPath 表达之上。
在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。
根节点在xpath中可以用 “//” 来啊表示
XPath 常用规则
表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
* 通配符,选择所有元素节点与元素名
@* 选取所有属性
[@attrib] 选取具有给定属性的所有元素
[@attrib='value'] 选取给定属性具有给定值的所有元素
[tag] 选取所有具有指定元素的直接子节点
[tag='text'] 选取所有具有指定元素并且文本内容是text节点
读取 文本 解析节点 ( etree 会修复 HTML 文本节点 )
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author :
# @File : test.py
# @Software : PyCharm
# @description : XXX
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'))
'''
执行结果:
<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>
'''
读取 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)
节点关系
(1)父(Parent)
每个元素以及属性都有一个父。在下面的例子中,book 元素是 title、author、year 以及 price 元素的父:
<book>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
(2)子(Children)
元素节点可有零个、一个或多个子。在下面的例子中,title、author、year 以及 price 元素都是 book 元素的子:
<book>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
(3)同胞(Sibling)
拥有相同的父的节点。在下面的例子中,title、author、year 以及 price 元素都是同胞:
<book>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
(4)先辈(Ancestor)
某节点的父、父的父,等等。在下面的例子中,title 元素的先辈是 book 元素和 bookstore 元素:
<bookstore>
<book>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>
(5)后代(Descendant)
某个节点的子,子的子,等等。在下面的例子中,bookstore 的后代是 book、title、author、year 以及 price 元素:
<bookstore>
<book>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>
选取节点
XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
下面列出了最有用的路径表达式:
表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
* 通配符,选择所有元素节点与元素名
@* 选取所有属性
[@attrib] 选取具有给定属性的所有元素
[@attrib='value'] 选取给定属性具有给定值的所有元素
[tag] 选取所有具有指定元素的直接子节点
[tag='text'] 选取所有具有指定元素并且文本内容是text节点
实例
在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:
路径表达式 结果
bookstore 选取 bookstore 元素的所有子节点。
/bookstore 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性。
谓语(Predicates)
谓语用来查找某个特定的节点或者包含某个指定的值的节点。谓语被嵌在方括号中。
实例
在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:
路径表达式 结果
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang=’eng’] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。
选取未知节点
XPath 通配符可用来选取未知的 XML 元素。
通配符 描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。
实例
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 结果
/bookstore/* 选取 bookstore 元素的所有子元素。
//* 选取文档中的所有元素。
//title[@*] 选取所有带有属性的 title 元素。
选取若干路径
通过在路径表达式中使用“|”运算符,您可以选取若干个路径。
实例
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 结果
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
//title | //price 选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。
XPath 运算符
下面列出了可用在 XPath 表达式中的运算符:( 此表参考来源:http://www.w3school.com.cn/xpath/xpath_operators.asp)
运算符 描述 实例 返回值
| 计算两个节点集 //book | //cd 返回所有拥有 book 和 cd 元素的节点集
+ 加法 6 + 4 10
– 减法 6 – 4 2
* 乘法 6 * 4 24
div 除法 8 div 4 2
= 等于 price=9.80 如果 price 是 9.80,则返回 true。如果 price 是 9.90,则返回 false。
!= 不等于 price!=9.80 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。
< 小于 price<9.80 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。
<= 小于或等于 price<=9.80 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。
> 大于 price>9.80 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。
>= 大于或等于 price>=9.80 如果 price 是 9.90,则返回 true。如果 price 是 9.70,则返回 false。
or 或 price=9.80 or price=9.70 如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 false。
and 与 price>9.00 and price<9.90 如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 false。
mod 计算除法的余数 5 mod 2 1
XPath 函数的高级使用示例:
1.使用 contains() 和 and
//div[starts-with(@id,'res')]//table[1]//tr//td[2]//a//span[contains(.,'_Test') and contains(.,'KPI')]
//div[contains(@id,'in')] ,表示选择id中包含有’in’的div节点
2.text():
由于一个节点的文本值不属于属性,比如“<a class=”baidu“ href=”http://www.baidu.com“>baidu</a>”,
所以,用text()函数来匹配节点://a[text()='baidu']
//span[@id='idHeaderTitleCell' and contains(text(),'QuickStart')]
3.last():
前面已介绍
4. 使用starts-with()
//div[starts-with(@id,'in')] ,表示选择以’in’开头的id属性的div节点
//div[starts-with(@id,'res')]//table//tr//td[2]//table//tr//td//a//span[contains(.,'Developer Tutorial')]
5.not()函数,表示否定。not()函数通常与返回值为true or false的函数组合起来用,
比如contains(),starts-with()等,但有一种特别情况请注意一下:
我们要匹配出input节点含有id属性的,写法为://input[@id],
如果我们要匹配出input节点不含用id属性的,则为://input[not(@id)]
//input[@name=‘identity’ and not(contains(@class,‘a’))] ,表示匹配出name为identity并且class的值中不包含a的input节点。
6.使用descendant
//div[starts-with(@id,'res')]//table[1]//tr//td[2]//a//span[contains(.,'QuickStart')]/../../../descendant::img
7.使用ancestor
//div[starts-with(@id,'res')]//table[1]//tr//td[2]//a//span[contains(.,'QuickStart')]/ancestor::div[starts-with(@id,'res')]//table[2]//descendant::a[2]
示例代码:
# -*- coding: utf-8 -*-
# @Author :
# @File : douban_api.py
# @Software: PyCharm
# @description : XXX
import re
import json
import datetime
import requests
from lxml import etree
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class DBSpider(object):
def __init__(self):
self.custom_headers = {
'Host': 'movie.douban.com',
'Connection': 'keep-alive',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,'
'image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
},
# self.proxies = {
# 'http': '127.0.0.1:8080',
# 'https': '127.0.0.1:8080'
# }
self.s = requests.session()
self.s.verify = False
self.s.headers = self.custom_headers
# self.s.proxies = self.proxies
def __del__(self):
pass
def api_artists_info(self, artists_id=None):
ret_val = None
if artists_id:
url = f'https://movie.douban.com/celebrity/{artists_id}/'
try:
r = self.s.get(url)
if 200 == r.status_code:
response = etree.HTML(r.text)
artists_name = response.xpath('//h1/text()')
artists_name = artists_name[0] if len(artists_name) else ''
chinese_name = re.findall(r'([\u4e00-\u9fa5·]+)', artists_name)
chinese_name = chinese_name[0] if len(chinese_name) else ''
english_name = artists_name.replace(chinese_name, '').strip()
pic = response.xpath('//div[@id="headline"]//div[@class="pic"]//img/@src')
pic = pic[0] if len(pic) else ''
sex = response.xpath('//div[@class="info"]//span[contains(text(), "性别")]/../text()')
sex = ''.join(sex).replace('\n', '').replace(':', '').strip() if len(sex) else ''
constellation = response.xpath('//div[@class="info"]//span[contains(text(), "星座")]/../text()')
constellation = ''.join(constellation).replace('\n', '').replace(':', '').strip() if len(constellation) else ''
birthday = response.xpath('//div[@class="info"]//span[contains(text(), "日期")]/../text()')
birthday = ''.join(birthday).replace('\n', '').replace(':', '').strip() if len(birthday) else ''
place = response.xpath('//div[@class="info"]//span[contains(text(), "出生地")]/../text()')
place = ''.join(place).replace('\n', '').replace(':', '').strip() if len(place) else ''
occupation = response.xpath('//div[@class="info"]//span[contains(text(), "职业")]/../text()')
occupation = ''.join(occupation).replace('\n', '').replace(':', '').strip() if len(occupation) else ''
desc = ''.join([x for x in response.xpath('//span[@class="all hidden"]/text()')])
artists_info = dict(
artistsId=artists_id,
homePage=f'https://movie.douban.com/celebrity/{artists_id}',
sex=sex,
constellation=constellation,
chineseName=chinese_name,
foreignName=english_name,
posterAddre=pic,
# posterAddreOSS=Images.imgages_data(pic, 'movie/douban'),
birthDate=birthday,
birthAddre=place,
desc=desc,
occupation=occupation,
showCount='',
fetchTime=str(datetime.datetime.now()),
)
# print(json.dumps(artists_info, ensure_ascii=False, indent=4))
ret_val = artists_info
else:
print(f'status code : {r.status_code}')
except BaseException as e:
print(e)
return ret_val
def test(self):
pass
if __name__ == '__main__':
douban = DBSpider()
# temp_uid = '1044707'
# temp_uid = '1386515'
# temp_uid = '1052358'
temp_uid = '1052357'
user_info = douban.api_artists_info(temp_uid)
print(json.dumps(user_info, ensure_ascii=False, indent=4))
pass
Xpath 高级用法
scrapy实战2,使用内置的xpath,re 和 css 提取值:https://www.cnblogs.com/regit/p/9629263.html
span 标签 class 属性包含 selectable 字符串://span[contains(@class, 'selectable')]
匹配猫眼 座位数//div[@class='seats-wrapper']/div/span[contains(@class,'seat') and not(contains(@class,'empty'))]
等价于//div[@class='seats-wrapper']/div//span[not(contains(//span[contains(@class, 'seat')]/@class, 'empty'))]
./@data-val
//div[contains(@class, "show-list") and @data-index="{0}"]
.//div[@class="show-date"]//span[contains(@class, "date-item")]/text()
.//div[contains(@class, "plist-container")][1]//tbody//tr xpath 中下标是从 1 开始的
substring-before(substring-after(//script[contains(text(), '/apps/feedlist')]/text(), 'html":"'), '"})')
//div[text()="hello"]/p/text()
//a[@class="movie-name"][1]/text()
string(//a[@class="movie-name"][1])
1. 获取父节点属性
首先选中 href 属性为 link4.html的a节点,然后再获取其父节点,然后再获取其class属性result1 = response.xpath('//a[@href="link4.html"]/../@class')
我们也可以通过parent::来获取父节点result2 = response.xpath('//a[@href="link4.html"]/parent::*/@class')
注意: //a表示html中的所有a节点,他们的href属性有多个,这里[]的作用是属性匹配,找到a的href属性为link4.html的节点
2. 获取节点内部文本
获取class为item-1的li节点文本, result3 = response.xpath('//li[@class="item-0"]/a/text()')
返回结果为:['first item', 'fifth item']
3. 属性获取
获取所有li节点下的所有a节点的href属性result4 = response.xpath('//li/a/@href')
返回结果为:['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
4. 按序选择result = response.xpath('//li[1]/a/text()') #选取第一个li节点 result = response.xpath('//li[last()]/a/text()') #选取最后一个li节点 result = response.xpath('//li[position()<3]/a/text()') #选取位置小于3的li节点,也就是1和2的节点 result = response.xpath('//li[last()-2]/a/text()') #选取倒数第三个节点
5. 节点轴选择
1)返回第一个li节点的所有祖先节点,包括html,body,div和ul
result = response.xpath('//li[1]/ancestor::*')
2)返回第一个li节点的<div>祖先节点
result = response.xpath('//li[1]/ancestor::div')
3)返回第一个li节点的所有属性值
result = response.xpath('//li[1]/attribute::*')
4)首先返回第一个li节点的所有子节点,然后加上限定条件,选组href属性为link1.html的a节点
result = response.xpath('//li[1]/child::a[@href="link1.html"]')
5)返回第一个li节点的所有子孙节点,然后加上只要span节点的条件
result = response.xpath('//li[1]/descendant::span')
6)following轴可获得当前节点之后的所有节点,虽然使用了*匹配,但是又加了索引选择,所以只获取第2个后续节点,也就是第2个<li>节点中的<a>节点
result = response.xpath('//li[1]/following::*[2]')
7)following-sibling可获取当前节点之后的所有同级节点,也就是后面所有的<li>节点
result = response.xpath('//li[1]/following-sibling::*')
6. 属性多值匹配
<li class="li li-first"><a href="link.html">first item</a></li>
result5 = response.xpath('//li[@class="li"]/a/text()')
返回值为空,因为这里HTML文本中li节点为class属性有2个值li和li-first,如果还用之前的属性匹配就不行了,需要用contain()函数
正确方法如下
result5 = response.xpath('//li[contains(@class, "li")]/a/text()')
contains()方法中,第一个参数为属性名,第二个参数传入属性值,只要此属性名包含所传入的属性值就可完成匹配
7. 多属性匹配,这里说一下不用框架的时候,xpath的常规用法
有时候我们需要多个属性来确定一个节点,那么就需要同时匹配多个属性,可用and来连接
from lxml import etree
text = '''
<li class = "li li-first" name="item"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result6 = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
print(result)
这里的li节点有class和name两个属性,需要用and操作符相连,然后置于中括号内进行条件筛选
xpath 学习笔记
1.依靠自己的属性,文本定位
//td[text()='Data Import']
//div[contains(@class,'cux-rightArrowIcon-on')]
//a[text()='马上注册']
//input[@type='radio' and @value='1'] 多条件
//span[@name='bruce'][text()='bruce1'][1] 多条件
//span[@id='bruce1' or text()='bruce2'] 找出多个
//span[text()='bruce1' and text()='bruce2'] 找出多个
2.依靠父节点定位
//div[@class='x-grid-col-name x-grid-cell-inner']/div
//div[@id='dynamicGridTestInstanceformclearuxformdiv']/div
//div[@id='test']/input
3.依靠子节点定位
//div[div[@id='navigation']]
//div[div[@name='listType']]
//div[p[@name='testname']]
4.混合型
//div[div[@name='listType']]//img
//td[a//font[contains(text(),'seleleium2从零开始 视屏')]]//input[@type='checkbox']
5.进阶部分
//input[@id='123']/following-sibling::input 找下一个兄弟节点
//input[@id='123']/preceding-sibling::span 上一个兄弟节点
//input[starts-with(@id,'123')] 以什么开头
//span[not(contains(text(),'xpath'))] 不包含xpath字段的span
6.索引
//div/input[2]
//div[@id='position']/span[3]
//div[@id='position']/span[position()=3]
//div[@id='position']/span[position()>3]
//div[@id='position']/span[position()<3]
//div[@id='position']/span[last()]
//div[@id='position']/span[last()-1]
7.substring 截取判断
<div data-for="result" id="swfEveryCookieWrap"></div>
//*[substring(@id,4,5)='Every']/@id 截取该属性 定位3,取长度5的字符
//*[substring(@id,4)='EveryCookieWrap'] 截取该属性从定位3 到最后的字符
//*[substring-before(@id,'C')='swfEvery']/@id 属性 'C'之前的字符匹配
//*[substring-after(@id,'C')='ookieWrap']/@id 属性'C之后的字符匹配
8.通配符*
//span[@*='bruce']
//*[@name='bruce']
9.轴
//div[span[text()='+++current node']]/parent::div 找父节点
//div[span[text()='+++current node']]/ancestor::div 找祖先节点
10.孙子节点
//div[span[text()='current note']]/descendant::div/span[text()='123']
//div[span[text()='current note']]//div/span[text()='123'] 两个表达的意思一样
11.following pre
https://www.baidu.com/s?wd=xpath
//span[@class="fk fk_cur"]/../following::a 往下的所有a
//span[@class="fk fk_cur"]/../preceding::a[1] 往上的所有a
xpath提取多个标签下的text
在写爬虫的时候,经常会使用xpath进行数据的提取,对于如下的代码:
<div id="test1">大家好!</div>
使用xpath提取是非常方便的。假设网页的源代码在selector中:
data = selector.xpath('//div[@id="test1"]/text()').extract()[0]
就可以把“大家好!”提取到data变量中去。
然而如果遇到下面这段代码呢?
<div id="test2">美女,<font color=red>你的微信是多少?</font><div>
如果使用:
data = selector.xpath('//div[@id="test2"]/text()').extract()[0]
只能提取到“美女,”;
如果使用:
data = selector.xpath('//div[@id="test2"]/font/text()').extract()[0]
又只能提取到“你的微信是多少?”
可是我本意是想把“美女,你的微信是多少?”这一整个句子提取出来。
<div id="test3">我左青龙,<span id="tiger">右白虎,
<ul>上朱雀,<li>下玄武。</li></ul>老牛在当中,</span>龙头在胸口。
<div>
而且内部的标签还不固定,如果我有一百段这样类似的html代码,
又如何使用xpath表达式,以最快最方便的方式提取出来?
使用xpath的string(.)
以第三段代码为例:
data = selector.xpath('//div[@id="test3"]')
info = data.xpath('string(.)').extract()[0]
这样,就可以把“我左青龙,右白虎,上朱雀,下玄武。老牛在当中,龙头在胸口”整个句子提取出来,
赋值给info变量。
示例 XML 文档
<?xml version="1.0" encoding="utf8"?>
<bookstore>
<book>
<title lang="eng">Harry Potter</title>
<price>29.99</price>
</book>
<book>
<title lang="eng">Learning XML</title>
<price>39.95</price>
</book>
</bookstore>
选取节点
以下为基本路径的表达方式,记住 XPath 的路径表达式都是基于某个节点之上的,例如最初的当前节点一般是根节点,这与 Linux 下路径切换原理是一样的。
表达式 描述
nodename 选取已匹配节点下名为 nodename 的子元素节点。
/ 如果以 / 开头,表示从根节点作为选取起点。
// 在已匹配节点后代中选取节点,不考虑目标节点的位置。
. 选取当前节点。
.. 选取当前节点的父元素节点。
@ 选取属性。
>>> from lxml import etree
>>> xml = """<?xml version="1.0" encoding="utf8"?>
<bookstore>
<book>
<title lang="eng">Harry Potter</title>
<price>29.99</price>
</book>
<book>
<title lang="eng">Learning XML</title>
<price>39.95</price>
</book>
</bookstore>"""
# 得到根节点
>>> root = etree.fromstring(xml)
>>> print root
<Element bookstore at 0x2c9cc88>
# 选取所有book子元素
>>> root.xpath('book')
[<Element book at 0x2d88878>, <Element book at 0x2d888c8>]
# 选取根节点bookstore
>>> root.xpath('/bookstore')
[<Element bookstore at 0x2c9cc88>]
# 选取所有book子元素的title子元素
>>> root.xpath('book/title')
[<Element title at 0x2d88878>, <Element title at 0x2d888c8>]
# 以根节点为始祖,选取其后代中的title元素
>>> root.xpath('//title')
[<Element title at 0x2d88878>, <Element title at 0x2d888c8>]
# 以book子元素为始祖,选取后代中的price元素
>>> root.xpath('book//price')
[<Element price at 0x2ca20a8>, <Element price at 0x2d88738>]
# 以根节点为始祖,选取其后代中的lang属性值
>>> root.xpath('//@lang')
['eng', 'eng']
预判(Predicates)
预判是用来查找某个特定的节点或者符合某种条件的节点,预判表达式位于方括号中。
# 选取bookstore的第一个book子元素
>>> root.xpath('/bookstore/book[1]')
[<Element book at 0x2ca20a8>]
# 选取bookstore的最后一个book子元素
>>> root.xpath('/bookstore/book[last()]')
[<Element book at 0x2d88878>]
# 选取bookstore的倒数第二个book子元素
>>> root.xpath('/bookstore/book[last()-1]')
[<Element book at 0x2ca20a8>]
# 选取bookstore的前两个book子元素
>>> root.xpath('/bookstore/book[position()<3]')
[<Element book at 0x2ca20a8>, <Element book at 0x2d88878>]
# 以根节点为始祖,选取其后代中含有lang属性的title元素
>>> root.xpath('//title[@lang]')
[<Element title at 0x2d888c8>, <Element title at 0x2d88738>]
# 以根节点为始祖,选取其后代中含有lang属性并且值为eng的title元素
>>> root.xpath("//title[@lang='eng']")
[<Element title at 0x2d888c8>, <Element title at 0x2d88738>]
# 选取bookstore子元素book,条件是book的price子元素要大于35
>>> root.xpath("/bookstore/book[price>35.00]")
[<Element book at 0x2ca20a8>]
# 选取bookstore子元素book的子元素title,条件是book的price子元素要大于35
>>> root.xpath("/bookstore/book[price>35.00]/title")
[<Element title at 0x2d888c8>]
通配符
通配符 描述
* 匹配任何元素。
@* 匹配任何属性。
node() 匹配任何类型的节点。
# 选取 bookstore 所有子元素
>>> root.xpath('/bookstore/*')
[<Element book at 0x2d888c8>, <Element book at 0x2ca20a8>]
# 选取根节点的所有后代元素
>>> root.xpath('//*')
[<Element bookstore at 0x2c9cc88>, <Element book at 0x2d888c8>, <Element title at 0x2d88738>, <Element price at 0x2d88878>, <Element book at 0x2ca20a8>, <Element title at 0x2d88940>, <Element price at 0x2d88a08>]
# 选取根节点的所有具有属性节点的title元素
>>> root.xpath('//title[@*]')
[<Element title at 0x2d88738>, <Element title at 0x2d88940>]
# 选取当前节点下所有节点。'\n ' 是文本节点。
>>> root.xpath('node()')
['\n ', <Element book at 0x2d888c8>, '\n ', <Element book at 0x2d88878>, '\n']
# 选取根节点所有后代节点,包括元素、属性、文本。
>>> root.xpath('//node()')
[<Element bookstore at 0x2c9cc88>, '\n ', <Element book at 0x2d888c8>, '\n ', <Element title at 0x2d88738>, 'Harry Potter', '\n ', <Element price at 0x2d88940>, '29.99', '\n ', '\n ', <Element book at 0x2d88878>, '\n ', <Element title at 0x2ca20a8>, 'Learning XML', '\n ', <Element price at 0x2d88a08>, '39.95', '\n ', '\n']
或条件选取
使用 "|" 运算符,你可以选取符合“或”条件的若干路径。
# 选取所有book的title元素或者price元素
>>> root.xpath('//book/title|//book/price')
[<Element title at 0x2d88738>, <Element price at 0x2d88940>, <Element title at 0x2ca20a8>, <Element price at 0x2d88a08>]
# 选择所有title或者price元素
>>> root.xpath('//title|//price')
[<Element title at 0x2d88738>, <Element price at 0x2d88940>, <Element title at 0x2ca20a8>, <Element price at 0x2d88a08>]
# 选择book子元素title或者全部的price元素
>>> root.xpath('/bookstore/book/title|//price')
[<Element title at 0x2d88738>, <Element price at 0x2d88940>, <Element title at 0x2ca20a8>, <Element price at 0x2d88a08>]
lxml 用法
首先我们利用它来解析 HTML 代码,先来一个小例子来感受一下它的基本用法。
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><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)
result = etree.tostring(html)
print(result)
首先我们使用 lxml 的 etree 库,然后利用 etree.HTML 初始化,然后我们将其打印出来。
其中,这里体现了 lxml 的一个非常实用的功能就是自动修正 html 代码,大家应该注意到了,最后一个 li 标签,其实我把尾标签删掉了,是不闭合的。不过,lxml 因为继承了 libxml2 的特性,具有自动修正 HTML 代码的功能。
所以输出结果是这样的
<html><body>
<div>
<ul>
<li class="item-0"><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></li>
</ul>
</div>
</body></html>
不仅补全了 li 标签,还添加了 body,html 标签。
文件读取
除了直接读取字符串,还支持从文件读取内容。比如我们新建一个文件叫做 hello.html,内容为
<div>
<ul>
<li class="item-0"><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"><span class="bold">third item</span></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></li>
</ul>
</div>
利用 parse 方法来读取文件。
from lxml import etree
html = etree.parse('hello.html')
result = etree.tostring(html, pretty_print=True)
print(result)
同样可以得到相同的结果。
XPath 实例测试
python3解析库 lxml :http://www.cnblogs.com/zhangxinqi/p/9210211.html
依然以上一段程序为例
(1)获取 所有 的 <li> 标签
from lxml import etree
html = etree.parse('hello.html')
print type(html)
result = html.xpath('//li')
print result
print len(result)
print type(result)
print type(result[0])
运行结果
<type 'lxml.etree._ElementTree'>
[<Element li at 0x1014e0e18>, <Element li at 0x1014e0ef0>, <Element li at 0x1014e0f38>, <Element li at 0x1014e0f80>, <Element li at 0x1014e0fc8>]
5
<type 'list'>
<type 'lxml.etree._Element'>
可见,etree.parse 的类型是 ElementTree,
通过调用 xpath 以后,得到了一个列表,包含了 5 个 <li> 元素,每个元素都是 Element 类型。
获取所有节点。返回一个列表每个元素都是Element类型,所有节点都包含在其中
from lxml import etree
html = etree.parse('hello.html', etree.HTMLParser())
result = html.xpath('//*') # //代表获取子孙节点,*代表获取所有
print(type(html))
print(type(result))
print(result)
# 如要获取li节点,可以使用//后面加上节点名称,然后调用xpath()方法
html.xpath('//li') # 获取所有子孙节点的li节点
(2)获取 子节点
通过 / 或者 // 即可查找元素的 子节点 或者 子孙节点,如果想选择li节点的所有直接a节点,可以这样使用
# 通过追加/a选择所有li节点的所有直接a节点,因为//li用于选中所有li节点,/a用于选中li节点的所有直接子节点a
result=html.xpath('//li/a')
(3)获取 父节点
通过 / 或者 // 可以查找 子节点 或 子孙节点,那么要查找父节点可以使用 .. 来实现也可以使用 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']
'''
(4)属性 匹配
在选取的时候,我们还可以用 @符号 进行属性过滤。比如,这里如果要选取 class 为 link1.html 的 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="link1.html"]')
print(result)
# 获取 <li> 标签的所有 class
result = html.xpath('//li/@class')
print(result)
(5)文本 获取
我们用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)
(6)属性 获取
使用 @符号即可获取节点的属性,如下:获取所有li节点下所有a节点的href属性
result=html.xpath('//li/a/@href') #获取a的href属性
result=html.xpath('//li//@href') #获取所有li子孙节点的href属性
(7)属性 多值 匹配
如果某个属性的值有多个时,我们可以使用 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()就能精确匹配到节点了
[]
['第一个']
(8)多 属性 匹配
另外我们还可能遇到一种情况,那就是根据多个属性确定一个节点,这时就需要同时匹配多个属性,此时可用运用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']
(9)按序 选择
有时候,我们在选择的时候某些属性可能同时匹配多个节点,但我们只想要其中的某个节点,如第二个节点或者最后一个节点,这时可以利用中括号引入索引的方法获取特定次序的节点:
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
(10)节点轴 选择
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>]
# 获取 <li> 标签下 href 为 link1.html 的 <a> 标签
result = html.xpath('//li/a[@href="link1.html"]')
print result
# 获取 <li> 标签下的所有 <span> 标签 (应为是所有,所以使用 // )
result = html.xpath('//li//span')
# 获取 <li> 标签下的所有 class,不包括 <li>
result = html.xpath('//li/a//@class')
print result
# 获取最后一个 <li> 的 <a> 的 href
result = html.xpath('//li[last()]/a/@href')
print result
# 获取倒数第二个元素的内容
result = html.xpath('//li[last()-1]/a')
print result[0].text
# 获取 class 为 bold 的标签名
result = html.xpath('//*[@class="bold"]')
print result[0].tag
以上使用的是XPath轴的用法,更多轴的用法可参考:http://www.w3school.com.cn/xpath/xpath_axes.asp
案例应用:抓取TIOBE指数前20名排行开发语言
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author :
# @File : test_1.py
# @Software : PyCharm
# @description : XXX
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 # 获取网页内容
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)
except RequestException as e:
print('request is error!', e)
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)
def main():
url = 'https://www.tiobe.com/tiobe-index/'
data = one_to_page(url)
write_file(data)
if __name__ == '__main__':
main()
'''
{'2018年6月排行': '1', '2017年6排行': '1', '开发语言': 'Java', '评级': '15.932%', '变化率': '+2.66%'}
{'2018年6月排行': '2', '2017年6排行': '2', '开发语言': 'C', '评级': '14.282%', '变化率': '+4.12%'}
{'2018年6月排行': '3', '2017年6排行': '4', '开发语言': 'Python', '评级': '8.376%', '变化率': '+4.60%'}
{'2018年6月排行': '4', '2017年6排行': '3', '开发语言': 'C++', '评级': '7.562%', '变化率': '+2.84%'}
{'2018年6月排行': '5', '2017年6排行': '7', '开发语言': 'Visual Basic .NET', '评级': '7.127%', '变化率': '+4.66%'}
{'2018年6月排行': '6', '2017年6排行': '5', '开发语言': 'C#', '评级': '3.455%', '变化率': '+0.63%'}
{'2018年6月排行': '7', '2017年6排行': '6', '开发语言': 'JavaScript', '评级': '3.063%', '变化率': '+0.59%'}
{'2018年6月排行': '8', '2017年6排行': '9', '开发语言': 'PHP', '评级': '2.442%', '变化率': '+0.85%'}
{'2018年6月排行': '9', '2017年6排行': '-', '开发语言': 'SQL', '评级': '2.184%', '变化率': '+2.18%'}
{'2018年6月排行': '10', '2017年6排行': '12', '开发语言': 'Objective-C', '评级': '1.477%', '变化率': '-0.02%'}
{'2018年6月排行': '11', '2017年6排行': '16', '开发语言': 'Delphi/Object Pascal', '评级': '1.396%', '变化率': '+0.00%'}
{'2018年6月排行': '12', '2017年6排行': '13', '开发语言': 'Assembly language', '评级': '1.371%', '变化率': '-0.10%'}
{'2018年6月排行': '13', '2017年6排行': '10', '开发语言': 'MATLAB', '评级': '1.283%', '变化率': '-0.29%'}
{'2018年6月排行': '14', '2017年6排行': '11', '开发语言': 'Swift', '评级': '1.220%', '变化率': '-0.35%'}
{'2018年6月排行': '15', '2017年6排行': '17', '开发语言': 'Go', '评级': '1.189%', '变化率': '-0.20%'}
{'2018年6月排行': '16', '2017年6排行': '8', '开发语言': 'R', '评级': '1.111%', '变化率': '-0.80%'}
{'2018年6月排行': '17', '2017年6排行': '15', '开发语言': 'Ruby', '评级': '1.109%', '变化率': '-0.32%'}
{'2018年6月排行': '18', '2017年6排行': '14', '开发语言': 'Perl', '评级': '1.013%', '变化率': '-0.42%'}
{'2018年6月排行': '19', '2017年6排行': '20', '开发语言': 'Visual Basic', '评级': '0.979%', '变化率': '-0.37%'}
{'2018年6月排行': '20', '2017年6排行': '19', '开发语言': 'PL/SQL', '评级': '0.844%', '变化率': '-0.52%'}
'''
案例应用:解析 古文网 并打印 诗经 所对应的 URL
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author :
# @File : shijing.py
# @Software : PyCharm
# @description : XXX
import json
import traceback
import requests
from lxml import etree
"""
step1: 安装 lxml 库。
step2: from lxml import etree
step3: selector = etree.HTML(网页源代码)
step4: selector.xpath(一段神奇的符号)
"""
def parse():
url = 'https://www.gushiwen.org/guwen/shijing.aspx'
r = requests.get(url)
if r.status_code == 200:
selector = etree.HTML(r.text)
s_all_type_content = selector.xpath('//div[@class="sons"]/div[@class="typecont"]')
print(len(s_all_type_content))
article_list = list()
for s_type_content in s_all_type_content:
book_m1 = s_type_content.xpath('.//strong/text()')[0].encode('utf-8').decode('utf-8')
s_all_links = s_type_content.xpath('.//span/a')
article_dict = dict()
for s_link in s_all_links:
link_name = s_link.xpath('./text()')[0].encode('utf-8').decode('utf-8')
try:
link_href = s_link.xpath('./@href')[0].encode('utf-8').decode('utf-8')
except BaseException as e:
link_href = None
article_dict[link_name] = link_href
temp = dict()
temp[book_m1] = article_dict
article_list.append(temp)
print(json.dumps(article_list, ensure_ascii=False, indent=4))
else:
print(r.status_code)
if __name__ == '__main__':
parse()
pass
CSS 选择器——cssSelector 定位方式详解
CSS 选择器 参考手册:http://www.w3school.com.cn/cssref/css_selectors.asp
CSS 选择器 :http://www.runoob.com/cssref/css-selectors.html
Selenium之CSS Selector定位详解:https://www.bbsmax.com/A/MyJxLGE1Jn/
css selector
CSS选择器用于选择你想要的元素的样式的模式。
"CSS"列表示在CSS版本的属性定义(CSS1,CSS2,或对CSS3)。
选择器 示例 示例说明 CSS
.class .intro 选择所有class="intro"的元素 1
#id #firstname 选择所有id="firstname"的元素 1
* * 选择所有元素 2
element p 选择所有<p>元素 1
element,element div,p 选择所有<div>元素和<p>元素 1
element element div p 选择<div>元素内的所有<p>元素 1
element>element div>p 选择所有父级是 <div> 元素的 <p> 元素 2
element+element div+p 选择所有紧接着<div>元素之后的<p>元素 2
[attribute] [target] 选择所有带有target属性元素 2
[attribute=value] [target=-blank] 选择所有使用target="-blank"的元素 2
[attribute~=value] [title~=flower] 选择标题属性包含单词"flower"的所有元素 2
[attribute|=language] [lang|=en] 选择 lang 属性以 en 为开头的所有元素 2
:link a:link 选择所有未访问链接 1
:visited a:visited 选择所有访问过的链接 1
:active a:active 选择活动链接 1
:hover a:hover 选择鼠标在链接上面时 1
:focus input:focus 选择具有焦点的输入元素 2
:first-letter p:first-letter 选择每一个<P>元素的第一个字母 1
:first-line p:first-line 选择每一个<P>元素的第一行 1
:first-child p:first-child 指定只有当<p>元素是其父级的第一个子级的样式。 2
:before p:before 在每个<p>元素之前插入内容 2
:after p:after 在每个<p>元素之后插入内容 2
:lang(language) p:lang(it) 选择一个lang属性的起始值="it"的所有<p>元素 2
element1~element2 p~ul 选择p元素之后的每一个ul元素 3
[attribute^=value] a[src^="https"] 选择每一个src属性的值以"https"开头的元素 3
[attribute$=value] a[src$=".pdf"] 选择每一个src属性的值以".pdf"结尾的元素 3
[attribute*=value] a[src*="runoob"] 选择每一个src属性的值包含子字符串"runoob"的元素 3
:first-of-type p:first-of-type 选择每个p元素是其父级的第一个p元素 3
:last-of-type p:last-of-type 选择每个p元素是其父级的最后一个p元素 3
:only-of-type p:only-of-type 选择每个p元素是其父级的唯一p元素 3
:only-child p:only-child 选择每个p元素是其父级的唯一子元素 3
:nth-child(n) p:nth-child(2) 选择每个p元素是其父级的第二个子元素 3
:nth-last-child(n) p:nth-last-child(2) 选择每个p元素的是其父级的第二个子元素,从最后一个子项计数 3
:nth-of-type(n) p:nth-of-type(2) 选择每个p元素是其父级的第二个p元素 3
:nth-last-of-type(n) p:nth-last-of-type(2) 选择每个p元素的是其父级的第二个p元素,从最后一个子项计数 3
:last-child p:last-child 选择每个p元素是其父级的最后一个子级。 3
:root :root 选择文档的根元素 3
:empty p:empty 选择每个没有任何子级的p元素(包括文本节点) 3
:target #news:target 选择当前活动的#news元素(包含该锚名称的点击的URL) 3
:enabled input:enabled 选择每一个已启用的输入元素 3
:disabled input:disabled 选择每一个禁用的输入元素 3
:checked input:checked 选择每个选中的输入元素 3
:not(selector) :not(p) 选择每个并非p元素的元素 3
::selection ::selection 匹配元素中被用户选中或处于高亮状态的部分 3
:out-of-range :out-of-range 匹配值在指定区间之外的input元素 3
:in-range :in-range 匹配值在指定区间之内的input元素 3
:read-write :read-write 用于匹配可读及可写的元素 3
:read-only :read-only 用于匹配设置 "readonly"(只读) 属性的元素 3
:optional :optional 用于匹配可选的输入元素 3
:required :required 用于匹配设置了 "required" 属性的元素 3
:valid :valid 用于匹配输入值为合法的元素 3
:invalid :invalid 用于匹配输入值为非法的元素 3
CSS选择器的常见语法:
1. 根据 标签 定位 tagName (定位的是一组,多个元素)
find_element_by_css_selector("div")
2. 根据 id属性 定位 ( 注意:id 使用 # 表示)
find_element_by_css_selector("#eleid")
find_element_by_css_selector("div#eleid")
3. 根据 className 属性 定位(注意:class 属性 使用 . )
两种方式:前面加上 tag 名称。也可以不加。如果不加 tag 名称时,点不能省略。
find_element_by_css_selector('.class_value')
find_element_by_css_selector("div.eleclass")
find_element_by_css_selector('tag_name.class_value')
有的 class_value 比较长,而且中间有空格时,不能把空格原样写进去,那样不能识别。
这时,空格用点代替,前面要加上 tag_name。
driver.find_element_by_css_selector('div.panel.panel-email').click()
# <p class="important warning">This paragraph is a very important warning.</p>
driver.find_element_by_css_selector('.important')
driver.find_element_by_css_selector('.important.warning')
4. 根据 标签 属性 定位
两种方式,可以在前面加上 tag 名称,也可以不加。
find_element_by_css_selector("[attri_name='attri_value']")
find_element_by_css_selector("input[type='password']").send_keys('密码')
find_element_by_css_selector("[type='password']").send_keys('密码')
4.1 精确 匹配:
find_element_by_css_selector("div[name=elename]") #属性名=属性值,精确值匹配
find_element_by_css_selector("a[href]") #是否存在该属性,判断a元素是否存在href属性
注意:如果 class属性值 里带空格,用.来代替空格
4.2 模糊 匹配
find_element_by_css_selector("div[name^=elename]") #从起始位置开始匹配
find_element_by_css_selector("div[name$=name2]") #从结尾匹配
find_element_by_css_selector("div[name*=name1]") #从中间匹配,包含
4.3 多属性 匹配
find_element_by_css_selector("div[type='eletype][value='elevalue']") #同时有多属性
find_element_by_css_selector("div.eleclsss[name='namevalue'] #选择class属性为eleclass并且name为namevalue的div节点
find_element_by_css_selector("div[name='elename'][type='eletype']:nth-of-type(1) #选择name为elename并且type为eletype的第1个div节点
5. 定位 子元素 (A>B)
find_element_by_css_selector("div#eleid>input") #选择id为eleid的div下的所有input节点
find_element_by_css_selector("div#eleid>input:nth-of-type(4) #选择id为eleid的div下的第4个input节点
find_element_by_css_selector("div#eleid>nth-child(1)") #选择id为eleid的div下的第一个子节点
6. 定位 后代元素 (A空格B)
find_element_by_css_selector("div#eleid input") #选择id为eleid的div下的所有的子孙后代的 input 节点
find_element_by_css_selector("div#eleid>input:nth-of-type(4)+label #选择id为eleid的div下的第4个input节点的相邻的label节点
find_element_by_css_selector("div#eleid>input:nth-of-type(4)~label #选择id为eleid的div下的第4个input节点之后中的所有label节点
7. 不是 ( 否 )
find_element_by_css_selector("div#eleid>*.not(input)") #选择id为eleid的div下的子节点中不为input 的所有子节点
find_element_by_css_selector("div:not([type='eletype'])") #选择div节点中type不为eletype的所有节点
8. 包含
find_element_by_css_selector("li:contains('Goa')") # <li>Goat</li>
find_element_by_css_selector("li:not(contains('Goa'))) # <li>Cat</li>
9. by index
find_element_by_css_selector("li:nth(5)")
10. 路径 法
两种方式,可以在前面加上 tag 名称,也可以不加。注意它的层级关系使用大于号">"。
find_element_by_css_selector("form#loginForm>ul>input[type='password']").send_keys('密码')
高阶:
基本 css 选择器
CSS 选择器中,最常用的选择器 如下:
选择器 描述 举例
* 通配选择器,选择所有的元素 *
<type> 选择特定类型的元素,支持基本HTML标签 h1
.<class> 选择具有特定class的元素。 .class1
<type>.<class> 特定类型和特定class的交集。(直接将多个选择器连着一起表示交集) h1.class1
#<id> 选择具有特定id属性值的元素 #id1
属性选择器
除了最基本的核心选择器外,还有可以 基于属性 的 属性选择器:
选择器 描述 举例
[attr] 选取定义attr属性的元素,即使该属性没有值 [placeholder]
[attr="val"] 选取attr属性等于val的元素 [placeholder="请输入关键词"]
[attr^="val"] 选取attr属性开头为val的元素 [placeholder^="请输入"]
[attr$="val"] 选取attr属性结尾为val的元素 [placeholder$="关键词"]
[attr*="val"] 选取attr属性包含val的元素 [placeholder*="入关"]
[attr~="val"] 选取attr属性包含多个空格分隔的属性,其中一个等于val的元素 [placeholder~="关键词"]
[attr|="val"] 选取attr属性等于val的元素或第一个属性值等于val的元素 [placeholder|="关键词"]
<p class="important warning">This paragraph is a very important warning.</p>
selenium举例: (By.CSS_SELECTOR,'p[class="import warning"]')
属性与属性的值需要完全匹配,如上面用p[class='impprtant']就定位不到;
部分属性匹配:(By.CSS_SELECTOR,'p[class~="import warning"]');
子串匹配&特定属性匹配:
[class^="def"]:选择 class 属性值以 "def" 开头的所有元素
[class$="def"]:选择 class 属性值以 "def" 结尾的所有元素
[class*="def"]:选择class 属性值中包含子串 "def" 的所有元素
[class|="def"]:选择class 属性值等于"def"或以"def-"开头的元素(这个是特定属性匹配)
关系选择器
有一些选择器是基于层级之间的关系,这类选择器称之为关系选择器。
选择器 描述 举例
<selector> <selector> 第二个选择器为第一个选择器的后代元素,选取第二个选择器匹配结果 .class1 h1
<selector> > <selector> 第二个选择器为第一个选择器的直接子元素,选取第二个选择器匹配结果 .class1 > *
<selector> + <selector> 第二个选择器为第一个选择器的兄弟元素,选取第二个选择器的下一兄弟元素 .class1 + [lang]
<selector> ~ <selector> 第二个选择器为第一个选择器的兄弟元素,选取第二个选择器的全部兄弟元素 .class1 ~ [lang]
选择 某个元素 的 后代的元素:
selenium举例:(By.CSS_SELECTOR,‘div button’)
div元素的所有的后代元素中标签为button元素,不管嵌套有多深
选择 某个元素 的 子代元素:
selenium举例:(By.CSS_SELECTOR,‘div > button’)
div元素的所有的子代元素中标签为button元素(>符号前后的空格可有可无)
一个元素不好定位时,它的兄长元素很起眼,可以借助兄长来扬名,因此不妨称之为 "弟弟选择器".
即选择某个元素的弟弟元素(先为兄,后为弟):
selenium举例: (By.CSS_SELECTOR,'button + li')
button与li属于同一父元素,且button与li相邻,选择button下标签为li的元素
联合选择器与反选择器
利用 联合选择器与反选择器,可以实现 与和或 的关系。
选择器 描述 举例
<selector>,<selector> 属于第一个选择器的元素或者是属于第二个选择器的元素 h1, h2
:not(<selector>) 不属于选择器选中的元素 :not(html)
伪元素和伪类选择器
CSS选择器支持了 伪元素和伪类选择器。
:active 鼠标点击的元素
:checked 处于选中状态的元素
:default 选取默认值的元素
:disabled 选取处于禁用状态的元素
:empty 选取没有任何内容的元素
:enabled 选取处于可用状态的元素
:first-child 选取元素的第一个子元素
:first-letter 选取文本的第一个字母
:first-line 选取文本的第一行
:focus 选取得到焦点的元素
:hover 选取鼠标悬停的元素
:in-range 选取范围之内的元素
:out-of-range 选取范围之外的元素
:lang(<language>) 选取lang属性为language的元素
:last-child 选取元素的最后一个子元素
————————————————
版权声明:本文为CSDN博主「「已注销」」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lyshark_lyshark/article/details/125846734