Xpath:简单易用的网页内容提取工具

学习一时爽,一直学习一直爽 !

  Hello,大家好,我是Connor,一个从无到有的技术小白。上一次我们说到了 requests 的使用方法。到上节课为止,我们已经学完了所有的 Python 常用的访问库。那么当我们获取到了访问的内容之后,我们就应该从网页上提取我们想要的内容了。所以,今天我们来讲网页内容的常用提取工具之一:Xpath 。相比于 BeautifulSoup 而言,Xpath 更加简单易上手。

1.Xpath简介

  Xpath 是一门在 XML 文档中查找信息的语言。XPath 用于在 XML 文档中通过元素和属性进行导航。他是一种路径语言(XML Path Language),用来确定XML文档中某部分的位置。

  XPath基于XML的树状结构,提供在数据结构树中找寻节点的能力。起初XPath的提出的初衷是将其作为一个通用的、介于XPointerXSL间的语法模型。但是XPath很快的被开发者采用来当作小型*查询语言被广泛使用。比如说,当你打开一个网页后按 F12 进行元素检查。当你想要复制某个元素的路径的时候,你可以通过右键进行 Copy 操作。你会发现里面有 Copy Xpath 的选项。由此可见 Xpath 使用的广泛程度。

说了这么多Xpath使用的怎么怎么广泛,怎么怎么好用,我们还是来点实在的,看看在 Python 爬虫中到底如何使用 Xpath 来抓取我们想要的内容吧:


2. Xpath的安装

  在前面的教程中,我几乎从未提过某个库的安装,但是为什么在这里我要提出如何安装呢?原因很简单,Xpath只是 lxml 库中的一个模块,在 Python 很多库中都提供有 Xpath 的功能,但是最基本的还是 lxml 的这个库。效率最高。所以,你知道了,想要使用 Xpath 那么你就需要安装 lxml 库:

  1. pip install lxml

3.Xpath的语法

  其实说白了,Xpath 就是从 html 中选取节点。节点是通过沿着路径或者通过 step 来选取的。下面,我们将通过如下HTML文档来进行演示:

  1. html_doc ="""
  2. <html>
  3. <head></head>
  4. <body>
  5. <li>
  6. <a href="/book_16860.html" title="总裁的新鲜小妻">
  7. <img src="/16860s.jpg">
  8. </a>
  9. <img src="/kukuku/images/only.png" class="topss png_bg">
  10. <img src="abc.png" class="topss png_bg">
  11. <a href="/book_16860.html">总裁的新鲜小妻子</a>
  12. </li>
  13. <li>
  14. <a href="/book_16861.html" title="斗神天下">
  15. <img src="/16861s.jpg">
  16. </a>
  17. <img src="/kukuku/images/only.png" class="topss png_bg">
  18. <img src="def.png" class="topss png_bg">
  19. <a href="/book_16861.html">斗神天下</a>
  20. </li>
  21. </body>
  22. </html>"""

  首先,大家也都知道我们实际上从网页上获取的都是字符串格式。那么如果我们想要通过 Xpath 来提取我们想要的内容,我们首先要生成 HTML 的 DOM 树:

  1. from lxml import etree
  2. page = etree.HTML(html_doc)

3.1 路径查找

  如果我们想要使用路径查找,那我们首先需要知道 Xpath 的语法。Xpath 的主要语法有如下:

表达式 描述
nodename 选取名为nodename的子节点
/ 从根节点选取
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点
选取当前节点的父节点
@ 选取属性
  • 查找当前的子节点:
  1. In [1]: page.xpath('head')
  2. Out[1]: [<Element head at 0x7f185bfe5b08>]

  当前的节点位置是在 html,所以直接查询 head 节点可以查询出来,li 是html的孙节点,如果查询 li 将返回空值:

  1. In [2]: page.xpath('li')
  2. Out[2]: []
  • 从根节点进行查找:
  1. In [3]: page.xpath('/html')
  2. Out[3]: [<Element html at 0x11208be88>]

  从根节点进行查找,根节点下只有一个节点 html 节点,所以从根节点查找只能查找到 html,如果查找其它内容将返回空列表:

  1. In [4]: page.xpath('/li')
  2. Out[4]: []
  • 从整个文档所有节点查找:
  1. In [5]: page.xpath('//li')
  2. Out[5]:
  3. [<Element li at 0x1128c02c8>,
  4. <Element li at 0x111c74108>,
  5. <Element li at 0x111fd2288>,
  6. <Element li at 0x1128da348>]

  从整个文档进行查找可以查找整个文档中符合条件的节点,包括孙节点及以下。

  • 选取当前节点的父节点
  1. In [6]: page.xpath('//li')[0].xpath('..')
  2. Out[6]: [<Element body at 0x1128c0ac8>]
  • 选取属性:
  1. In [7]: page.xpath('//a')[1].xpath('@href')
  2. Out[7]: ['/book_16860.html']

  选取属性支持任意的标签属性,但是要注意如果选取出的节点有多个,需要指定是哪一个节点的属性。


3.2 节点查找

通过路径查找到节点以后,就需要从超找到的节点中选取我们需要的内容了。查找节点也有一些语法,如下:

表达式 结果
nodename[index] 选取符合要求的第 index 个元素
nodename[last()] 选取最后一个元素
nodename[position()< num] 选取前 num 个元素
nodename[@attribute] 选取带有属性名为 attribute 的元素
nodename[@attribute=‘value’] 选取带有属性名为 attribute 且 值为 value 的元素
  • 选取第一个 img 节点的 src 属性:
  1. In [1]: page.xpath('//li[1]/a[1]/img[1]/@src')
  2. Out[1]: ['/16860s.jpg']

  **注意:当你在选取一个节点的属性的时候,有一点需要注意。通过[index]选取出的节点是每一个符合条件的第[index]个符合条件的元素。**但是这么说比较抽象,我们举个例子。例如:

  1. In [2]: page.xpath('//li[1]') 选取所有符合 li 节点的第一个节点
  2. Out[2]: [<Element li at 0x7fb517327ac8>]

  通过上面的例子我们貌似看不出什么,那我们再看下面的这个例子:

  1. In [3]: page.xpath('//li//img[1]')
  2. Out[3]: [<Element img at 0x7f0c26328b08>,
  3. <Element img at 0x7f0c26328a88>,
  4. <Element img at 0x7f0c26328bc8>,
  5. <Element img at 0x7f0c26328c08>]

  你可以看到,我们选取出了4个 img 节点。原因很简单,看下面的代码和解释就能明白了:

  1. <html>
  2. <head></head>
  3. <body>
  4. <li>
  5. <a href="/book_16860.html" title="总裁的新鲜小妻">
  6. (一)<img src="/16860s.jpg">
  7. </a>
  8. (二)<img src="/kukuku/images/only.png" class="topss png_bg">
  9. (三)<img src="abc.png" class="topss png_bg">
  10. <a href="/book_16860.html">总裁的新鲜小妻子</a>
  11. </li>
  12. <li>
  13. <a href="/book_16861.html" title="斗神天下">
  14. (四)<img src="/16861s.jpg">
  15. </a>
  16. (五)<img src="/kukuku/images/only.png" class="topss png_bg">
  17. (六)<img src="def.png" class="topss png_bg">
  18. <a href="/book_16861.html">斗神天下</a>
  19. </li>
  20. </body>
  21. </html>
  1. (一)是第一个 <li> 节点的 <a> 节点里的第一个 <img> 节点。其路径为 <li>/<a>/<img>
  2. (二)是第一个 <li> 节点的 <img> 节点里的第一个 <img> 节点。其路径为 <li>/<img>
  3. (三)之所以没有被选中,是因为它是 <li> 标签下的第二个 <img> 标签
  4. (四)(五)(六)同理
  • 选取第二个元素开始的所有节点:
  1. In [4]: page.xpath('//img[position()>1]')
  2. Out[4]: [<Element img at 0x7f78ba63dac8>, <Element img at 0x7f78ba63da48>]
  • 选取带有指定属性与指定值的节点:
  1. In [5]: page.xpath('//a[@title="斗神天下"]')
  2. Out[5]: [<Element a at 0x7fdd0844fa48>]

3.3 未知节点

  当我们匹配时会出现路径不确定的情况,这个时候我们就要涉及到匹配未知节点。匹配未知节点也有对应的语法,如下:

通配符 描述
* 匹配任何元素节点
@* 匹配任何属性节点

匹配任何属性节点:

  1. In [1]: page.xpath('//li/a/*')
  2. Out[1]: [<Element img at 0x7f83af768b08>,
  3. <Element img at 0x7f83af768a88>,
  4. <Element img at 0x7f83af768bc8>,
  5. <Element img at 0x7f83af768c08>]

匹配任何元素节点:

  1. In [2]: page.xpath('//li/a[@*]')
  2. Out[2]: [<Element a at 0x7ff2dcf69b08>,
  3. <Element a at 0x7ff2dcf69a88>,
  4. <Element a at 0x7ff2dcf69bc8>,
  5. <Element a at 0x7ff2dcf69c08>]

3.4 获取节点中的文本

  通过 属性方法可以获取属性内的内容,但是位于节点之间的内容无法获取到,这个时候就可以通过 text()string() 方法来获得其中的文本:

通过 text() 获取某个节点中的文本:

  1. In [1]: page.xpath('//li/a[3]/text()')
  2. Out[1]: ['总裁的新鲜小妻子', '斗神天下']

  可以看到,通过 text() 属性可以很轻松的获取标签之间的文本。

通过 string() 获取某个节点中的文本:

  1. In [1]: page.xpath('string(//li[1]/a[3])')
  2. Out[1]: '总裁的新鲜小妻子'

3.5 选取多个路径

  有的时候我们需要同时查找多个条件,这个时候你可以通过在路径表达式中使用管道符("|"),选取若干个路径:

  1. In [1]: page.xpath('//li[1]/img[2]/@src | //li[1]/a[3]/text()')
  2. Out[1]: ['/kukuku/images/second.png', '总裁的新鲜小妻子']

  同时选取多个路径并不会生成一个新的列表,只有一个列表,所以你要考虑好如何提取你的返回结果。一般情况下还是不建议使用多条件来进行查找,因为多种提取结果混在一个列表不利于提取,即便可以提取,但也会增加计算量,降低程序性能。所以,尽量不要使用这种方法。


下期预告:

  Xpath用来用去还是那么费劲啊,有没有什么别的更简单的方法啊???当然有的,你可以喝着美味的汤就做完你需要做的事情了。敬请期待下期——BeautifulSoup:美味的汤

  以上就是所有的 Xpath 有关的内容啦,可能并不比别人讲的细,但是已经足够你做爬虫使用了。其实我也希望能够讲的更加细致一点,奈何我是一个正则党,我个人不太喜欢使用Xpath,如果你真的想要深入了解Xpth的话,推荐你到 W3C Xpath教程 >>> 去看看它们是怎么用的。

  好了,这就是今天的内容了,我是Connor,一个从无到有的技术小白。不知道今天你又学会了多少东西??我们下期再见!

学习一时爽,一直学习一直爽 !


系列文章连接:

Python 爬虫十六式 - 第一式:HTTP协议 >>>

Python 爬虫十六式 - 第二式:urllib 与 urllib3 >>>

Python 爬虫十六式 - 第三式:Requests的用法 >>>

Python 爬虫十六式 - 第五式:BeautifulSoup-美味的汤 >>>

Python 爬虫十六式 - 第六式:JQuery的假兄弟-pyquery >>>

Python 爬虫十六式 - 第七式:正则的艺术 >>>

Python爬虫十六式 - 第四式: 使用Xpath提取网页内容的更多相关文章

  1. Python 爬虫十六式 - 第六式:JQuery的假兄弟-pyquery

    PyQuery:一个类似jquery的python库 学习一时爽,一直学习一直爽   Hello,大家好,我是 Connor,一个从无到有的技术小白.上一次我们说到了 BeautifulSoup 美味 ...

  2. Python 爬虫十六式 - 第七式:正则的艺术

    RE:用匹配来演绎编程的艺术 学习一时爽,一直学习一直爽   Hello,大家好,我是 Connor,一个从无到有的技术小白.上一次我们说到了 pyquery 今天我们将迎来我们数据匹配部分的最后一位 ...

  3. Python 爬虫十六式 - 第五式:BeautifulSoup-美味的汤

    BeautifulSoup 美味的汤 学习一时爽,一直学习一直爽!    Hello,大家好,我是Connor,一个从无到有的技术小白.上一次我们说到了 Xpath 的使用方法.Xpath 我觉得还是 ...

  4. Python爬虫十六式 - 第三式:Requests的用法

    Requests: 让 HTTP 服务人类 学习一时爽,一直学习一直爽   Hello,大家好,我是Connor,一个从无到有的技术小白.今天我们继续来说我们的 Python 爬虫,上一次我们说到了 ...

  5. Python 爬虫十六式 - 第二式:urllib 与 urllib3

    Python请求标准库 urllib 与 urllib3 学习一时爽,一直学习一直爽!   大家好,我是 Connor,一个从无到有的技术小白.上一次我们说到了什么是HTTP协议,那么这一次我们就要动 ...

  6. Python 爬虫十六式 - 第一式:HTTP协议

    HTTP:伟大而又无闻的协议 学习一时爽,一直学习一直爽!   Hello,大家好啊,我是Connor,一个从无到有的技术小白.有的人一说什么是HTTP协议就犯愁,写东西的时候也没想过什么是HTTP协 ...

  7. Python爬虫(十六)_JSON模块与JsonPath

    本篇将介绍使用,更多内容请参考:Python学习指南 数据提取之JSON与JsonPATH JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它是的人们很容易 ...

  8. Python爬虫利器六之PyQuery的用法

    前言 你是否觉得 XPath 的用法多少有点晦涩难记呢? 你是否觉得 BeautifulSoup 的语法多少有些悭吝难懂呢? 你是否甚至还在苦苦研究正则表达式却因为少些了一个点而抓狂呢? 你是否已经有 ...

  9. Python爬虫实战六之抓取爱问知识人问题并保存至数据库

    大家好,本次为大家带来的是抓取爱问知识人的问题并将问题和答案保存到数据库的方法,涉及的内容包括: Urllib的用法及异常处理 Beautiful Soup的简单应用 MySQLdb的基础用法 正则表 ...

随机推荐

  1. SVM之KKT条件理解

    在SVM中,我们的超平面参数最终只与间隔边界上的向量(样本)有关,故称为支持向量机. 求解最优超平面,即求最大化间隔,或最小化间隔的倒数:||w||2/2,约束条件为yi(wTxi+b)>=1 ...

  2. [Vuejs] 给ref赋值需要注意的问题

    1.简单赋值 <div ref="refCon"></div> 访问方式: this.$refs.refCon 2.循环赋值,相同名称 <div v- ...

  3. Linux下用Java获取本机IP

    可能有多个网卡包括虚拟网卡,需要进行排除 String ip = ""; try { Enumeration<?> e1 = NetworkInterface.getN ...

  4. Python 对于分表的操作

    在操作数据库的业务里,我们系统采用了orm框架 ,避免了过多的写sql,利用实体对数据库进行操作 需求: 账户系统里的account表是进行了分表,分表规则为accountid进行20取模,测试环境分 ...

  5. Python自学

    print("\u4e2d\u56fd\") 报错,语法错误 修改,去掉尾部的\,正确 import datetimeprint("now:"+datetime ...

  6. shell简单脚本#1

    判断/etc/inittab文件是否大于100行,如果大于,则显示”/etc/inittab is a big file.”否者显示”/etc/inittab is a small file.” #! ...

  7. php 数组助手类

    ArrayHelper.php <?php /** * php 数组助手类 * Class ArrayHelper * @package app\helper */ class ArrayHel ...

  8. 【golang】浅析rune数据类型

    golang中string底层是通过byte数组实现的.中文字符在unicode下占2个字节,在utf-8编码下占3个字节,而golang默认编码正好是utf-8. golang中还有一个byte数据 ...

  9. vue项目中引入循环执行setInterval或者requestAnimationFrame的用法等

    项目中循环计时处理某些方法的情况还是比较常见的,一般会用setInterval来处理,但是这个方法会似的页面卡顿等使用体验不好. 所以就使用浏览器提供的requestAnimationFrame方法, ...

  10. CF Gym102028G Shortest Paths on Random Forests

    传送门 这题要求的期望,就是总权值(所有不在同一个连通块点对的贡献+同一连通块点对的贡献)/总方案(森林个数) 先求森林个数,森林是由一堆树组成的,而根据purfer序列,一棵\(n\)个点的有标号的 ...