Xpath学习

Posted youngxinwei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Xpath学习相关的知识,希望对你有一定的参考价值。

 

技术图片

一:基本操作

 1 from lxml import etree
 2 text = ‘‘‘
 3 <div>
 4 <ul>
 5 <li class="item-0"><a href="link1.html">first item</li>
 6 <li class="item-1"><a href="link2.html">second item</a></li>
 7 <li class="item-inactive"><a href="link3.html">third item</a></li>
 8 <li class="item-1"><a href="link4.html">fourth item</a></li>
 9 <li class="item-0"><a href="link5.html">fifth item</a>
10 </ul>
11 </div>
12 ‘‘‘
13 # 调用HTML类进行初始化,这样就成功构造了一个Xpath解析对象
14 # 注意:HTML文本中最后一个li节点是没有闭合的,但是etree模块可以自动修正HTML文本
15 html = etree.HTML(text)
16 # 调用tostring()方法即可输出修正后的HTML代码,但是结果是bytes类型
17 result = etree.tostring(html)
18 # 这里利用decode()方法将其转成str类型
19 # 输出结果可以看到经过处理之后,li节点标签被补全,并且还自动添加了body、html节点
20 print(result.decode(utf-8))

运行结果:

 1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a1.py
 2 <html><body><div>
 3 <url>
 4 <li class="item-0"><a href="link1.html">first item</a></li>
 5 <li class="item-1"><a href="link2.html">second item</a></li>
 6 <li class="item-inactive"><a href="link3.html">third item</a></li>
 7 <li class="item-1"><a href="link4.html">fourth item</a></li>
 8 <li class="item-0"><a href="link5.html">fifth item</a>
 9 
10 </li></ul></div>
11 </body></html>
12 
13 Process finished with exit code 0

 

读取文本进行解析:

1 from lxml import etree
2 # 直接读取文本进行解析
3 html = etree.parse(./test.html, etree.HTMLParser())
4 # 调用tostring()方法即可输出修正后的HTML代码,但是结果是bytes类型
5 result = etree.tostring(html)
6 # 输出结果多了一个DOCTYPE声明,不过对解析无任何影响
7 print(result.decode(utf-8))

运行结果:

"D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a2.py
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><div>&#13;
<url>&#13;
<li class="item-0"><a href="link1.html">first item</a></li>&#13;
<li class="item-1"><a href="link2.html">second item</a></li>&#13;
<li class="item-inactive"><a href="link3.html">third item</a></li>&#13;
<li class="item-1"><a href="link4.html">fourth item</a></li>&#13;
<li class="item-0"><a href="link5.html">fifth item</a>&#13;
&#13;
</li></url></div>&#13;
</body></html>

Process finished with exit code 0

 

二、选取节点:-所有节点

1 from lxml import etree
2 # 直接读取文本进行解析
3 html = etree.parse(./test.html, etree.HTMLParser())
4 # //表示从当前节点选取子孙节点,*表示匹配所有节点,也就是整个HTML文本都会被获取
5 result = html.xpath(//*)
6 # 返回结果是一个列表,每个元素都是Element类型
7 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a3.py
2 [<Element html at 0x2786c00>, <Element body at 0x2786cc0>, <Element div at 0x2786d00>, <Element ul at 0x2786d40>, <Element li at 0x2786d80>, <Element a at 0x2786e00>, <Element li at 0x2786e40>, <Element a at 0x2786e80>, <Element li at 0x2786ec0>, <Element a at 0x2786dc0>, <Element li at 0x2786f00>, <Element a at 0x2786f40>, <Element li at 0x2786f80>, <Element a at 0x2786fc0>]
3 
4 Process finished with exit code 0

 

可以看到返回形式是一个列表,每个元素都是Element类型,后面跟了节点的名称,如:html、body、ul、li等,所有节点都包含在列表中了

 

 

在匹配时指定节点名称:获取或有li节点

1 from lxml import etree
2 # 直接读取文本进行解析
3 html = etree.parse(./test.html, etree.HTMLParser())
4 # 这里选取所有li节点,可以使用//,然后直接加上节点名称即可
5 result = html.xpath(//li)
6 # 返回结果是一个列表,每个元素都是Element类型
7 print(result)
8 # 由于返回结果是列表,如果要取出其中一个对象,可以使用索引的方法
9 print(result[0])

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a3.py
2 [<Element li at 0x2766dc0>, <Element li at 0x2766e00>, <Element li at 0x2766e40>, <Element li at 0x2766e80>, <Element li at 0x2766ec0>]
3 <Element li at 0x2766dc0>
4 
5 Process finished with exit code 0

 

三、选取节点:-子节点

获取li节点下所有a节点

 1 from lxml import etree
 2 # 直接读取文本进行解析
 3 html = etree.parse(./test.html, etree.HTMLParser())
 4 # 选择li节点的所有直接a子节点
 5 # 这里通过追加/a即选择了所有li节点的所有直接a子节点
 6 # 因为//li表示选中所有li节点,/a用于选中li节点的所有直接子节点a,
 7 # 二者组合在一起即获取了所有li节点的所有直接a子节点
 8 result = html.xpath(//li/a)
 9 # 返回结果是一个列表,每个元素都是Element类型
10 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a4.py
2 [<Element a at 0x2786f40>, <Element a at 0x2786f80>, <Element a at 0x2786fc0>, <Element a at 0x278a040>, <Element a at 0x278a080>]
3 
4 Process finished with exit code 0

另一种写法:

获取ul节点下所有a节点

1 from lxml import etree
2 # 直接读取文本进行解析
3 html = etree.parse(./test.html, etree.HTMLParser())
4 # 选择ul节点下所有子孙a节点
5 result = html.xpath(//ul//a)
6 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a5.py
2 [<Element a at 0x2772fc0>, <Element a at 0x277a040>, <Element a at 0x277a080>, <Element a at 0x277a0c0>, <Element a at 0x277a100>]
3 
4 Process finished with exit code 0

但是如果这里用//u/a,就无法获取任何结果了,因为/用于获取直接子节点,而在ul节点下没有直接的a子节点,只有li节点,所以无法匹配到任何结果。

因此,这里要注意//与/的区别,其中/用于获取直接子节点,//用于获取之孙节点

 

四、选取节点:-父节点

首先选中href为link4.html的a节点,然后再获取其父节点,然后再获取其class属性

 1 from lxml import etree
 2 text = """
 3 <div>
 4 <ul>
 5 <li class="item-0"><a href="link1.html">first item</a></li>
 6 <li class="item-1"><a href="link2.html">second item</a></li>
 7 <li class="item-inactive"><a href="link3.html">third item</a></li>
 8 <li class="item-1"><a href="link4.html">fourth item</a></li>
 9 <li class="item-0"><a href="link5.html">fifth item</a>
10 </ul>
11 </div>
12 """
13 # 调用HTML类进行初始化,这样就成功构造了一个Xpath解析对象
14 # 注意:HTML文本中最后一个li节点是没有闭合的,但是etree模块可以自动修正HTML文本
15 html = etree.HTML(text)
16 # 首先选中href为link4.html的a节点,然后再获取其父节点,然后再获取其class属性
17 result = html.xpath(//a[@href = "link4.html"]/../@class)
18 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a6.py
2 [item-1]
3 
4 Process finished with exit code 0

 

例二:选择class为item-0的两个li节点

 1 from lxml import etree
 2 text = """
 3 <div>
 4 <ul>
 5 <li class="item-0"><a href="link1.html">first item</a></li>
 6 <li class="item-1"><a href="link2.html">second item</a></li>
 7 <li class="item-inactive"><a href="link3.html">third item</a></li>
 8 <li class="item-1"><a href="link4.html">fourth item</a></li>
 9 <li class="item-0"><a href="link5.html">fifth item</a>
10 </ul>
11 </div>
12 """
13 # 调用HTML类进行初始化,这样就成功构造了一个Xpath解析对象
14 # 注意:HTML文本中最后一个li节点是没有闭合的,但是etree模块可以自动修正HTML文本
15 html = etree.HTML(text)
16 # 选择class为item-0的两个li节点
17 result = html.xpath(//li[@class = "item-0"])
18 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a7.py
2 [<Element li at 0x2769080>, <Element li at 0x27690c0>]
3 
4 Process finished with exit code 0

 

五、获取文本

方法:利用XPath的text()方法获取节点中的文本

有两种方法

比如要获取li节点的文本,

一是先获取其内部a节点,再获取a节点的文本。/表示从根节点选取

二是使用获取到li节点后使用//来获取文本,//表示从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。

方法一:

 1 from lxml import etree
 2 text = """
 3 <div>
 4 <ul>
 5 <li class="item-0"><a href="link1.html">first item</a></li>
 6 <li class="item-1"><a href="link2.html">second item</a></li>
 7 <li class="item-inactive"><a href="link3.html">third item</a></li>
 8 <li class="item-1"><a href="link4.html">fourth item</a></li>
 9 <li class="item-0"><a href="link5.html">fifth item</a></li>
10 </ul>
11 </div>
12 """
13 # 调用HTML类进行初始化,这样就成功构造了一个Xpath解析对象
14 # 注意:HTML文本中最后一个li节点是没有闭合的,但是etree模块可以自动修正HTML文本
15 html = etree.HTML(text)
16 # 选择class为item-0的两个li节点,再获取内部的a子节点,再获取文本
17 result = html.xpath(//li[@class = "item-0"]/a/text())
18 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a7.py
2 [first item, fifth item]
3 
4 Process finished with exit code 0

方法二:

 1 from lxml import etree
 2 text = """
 3 <div>
 4 <ul>
 5 <li class="item-0"><a href="link1.html">first item</a></li>
 6 <li class="item-1"><a href="link2.html">second item</a></li>
 7 <li class="item-inactive"><a href="link3.html">third item</a></li>
 8 <li class="item-1"><a href="link4.html">fourth item</a></li>
 9 <li class="item-0"><a href="link5.html">fifth item</a></li>
10 </ul>
11 </div>
12 """
13 # 调用HTML类进行初始化,这样就成功构造了一个Xpath解析对象
14 # 注意:HTML文本中最后一个li节点是没有闭合的,但是etree模块可以自动修正HTML文本
15 html = etree.HTML(text)
16 # 选择class为item-0的两个li节点,不考虑位置来获取文本,使用//
17 result = html.xpath(//li[@class = "item-0"]//text())
18 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a7.py
2 [first item, fifth item]
3 
4 Process finished with exit code 0

 

六、获取属性

 例一:

 1 from lxml import etree
 2 text = """
 3 <div>
 4 <ul>
 5 <li class="item-0"><a href="link1.html">first item</a></li>
 6 <li class="item-1"><a href="link2.html">second item</a></li>
 7 <li class="item-inactive"><a href="link3.html">third item</a></li>
 8 <li class="item-1"><a href="link4.html">fourth item</a></li>
 9 <li class="item-0"><a href="link5.html">fifth item</a></li>
10 </ul>
11 </div>
12 """
13 html = etree.HTML(text)
14 result = html.xpath(//li/a/@href)
15 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a8.py
2 [link1.html, link2.html, link3.html, link4.html, link5.html]
3 
4 Process finished with exit code 0

注意:这里是通过@href来获取节点的href属性,此处和属性匹配的方法不同,属性匹配时中括号加属性名和值来限定某个属性,如[@href="link1.html"],

而此处的@href指的是获取节点的某个属性。

七、属性多值匹配

有时候,某些节点的某个属性可能有多个值,这时候用之前的属性匹配就无法匹配了

例如:

1 from lxml import etree
2 text = """
3 <li class="li li-first"><a href="link.html">first item</a></li>
4 """
5 html = etree.HTML(text)
6 result = html.xpath(//li[@class = "li"]/a/text())
7 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a9.py
2 []
3 
4 Process finished with exit code 0

这时候要用contains()方法,代码改写为:

1 from lxml import etree
2 text = """
3 <li class="li li-first"><a href="link.html">first item</a></li>
4 """
5 html = etree.HTML(text)
6 result = html.xpath(//li[contains(@class, "li")]/a/text())
7 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a9.py
2 [first item]
3 
4 Process finished with exit code 0

这样通过contains()方法,第一个参数传入属性名称,第二个参数传入属性值,只要此属性值包含所传入的属性值,就可以匹配成功。

此种方法在某个节点的某个属性有多个值的时候经常用到。

八、多属性同时匹配

根据多个属性确定一个节点,此时就需要同时匹配多个属性。可以使用运算符and来连接

1 from lxml import etree
2 text = """
3 <li class="li li-first" name = "item"><a href="link.html">first item</a></li>
4 """
5 html = etree.HTML(text)
6 # 使用and连接选择的两个属性
7 result = html.xpath(//li[contains(@class, "li") and @name ="item"]/a/text())
8 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a10.py
2 [first item]
3 
4 Process finished with exit code 0

九、按序选择

有时候,我们再选择的时候某些属性可能同时匹配了多个节点,但是我们只想要其中的某个节点,这时候可以利用中括号传入索引的方法来获取特定的次序的节点

 1 from lxml import etree
 2 text = """
 3 <div>
 4 <ul>
 5 <li class="item-0"><a href="link1.html">first item</a></li>
 6 <li class="item-1"><a href="link2.html">second item</a></li>
 7 <li class="item-inactive"><a href="link3.html">third item</a></li>
 8 <li class="item-1"><a href="link4.html">fourth item</a></li>
 9 <li class="item-0"><a href="link5.html">fifth item</a></li>
10 </ul>
11 </div>
12 """
13 
14 html = etree.HTML(text)
15 # 选择第一个li节点,注意[]中传入的是1,而不是0
16 result = html.xpath(//li[1]/a/text())
17 print(result)
18 # 选择最后一个li节点,[]中传入last()
19 result = html.xpath(//li[last()]/a/text())
20 print(result)
21 print(**20)
22 # 选择位置小于3的li节点,也就是前两个li节点,[]总传入position()<3
23 result = html.xpath(//li[position()<3]/a/text())
24 print(result)
25 # 选择倒数第三个li节点,[]中传入last()-2即可。因为last()是最后一个,所以last()-2就是倒数第三个
26 result = html.xpath(//li[last()-2]/a/text())
27 print(result)

运行结果:

1 "D:Program Files (x86)pythonpython.exe" E:/python/python爬虫/xpath/a11.py
2 [first item]
3 [fifth item]
4 ********************
5 [first item, second item]
6 [third item]
7 
8 Process finished with exit code 0

 

以上是关于Xpath学习的主要内容,如果未能解决你的问题,请参考以下文章

XPath 歧义

xpath 学习

爬虫学习(十四)——xpath项目实践

使用 xpath 同时选择属性和内容?

爬虫Xpath高级用法

代码review_学习笔记_1