# Python3 爬虫学习笔记 C07

TRHX

2019-08-25

Python3 学习笔记 / 爬虫学习

字数统计:

3,126字

阅读时长≈

14分

> Python3 爬虫学习笔记第七章 —— 【解析库 lxml】

# 【7.1】关于 lxml

lxml 是 Python 的一个解析库，支持 HTML 和 XML 的解析，支持 XPath 解析方式，解析效率非常高，使用前需要用命令 `pip3 install lxml` 安装 lxml 库

# 【7.2】使用 XPath

XPath（XML Path Language）即 XML 路径语言， lxml 解析库使用的正是 XPath 语法，最初是用来搜寻 XML 文档的，是一门在 XML 文档中查找信息的语言，它同样适用于 HTML 文档的搜索

XPath 常用规则

表达式 | 描述
--- | ---
nodename | 选取此节点的所有子节点
/ | 从当前节点选取直接子节点
// | 从当前节点选取子孙节点
. | 选取当前节点
.. | 选取当前节点的父节点
@ | 选取属性
* | 通配符，选择所有元素节点与元素名
@* | 选取所有属性
[@attrib] | 选取具有给定属性的所有元素
[@attrib=’value’] | 选取给定属性具有给定值的所有元素
[tag] | 选取所有具有指定元素的直接子节点
[tag=’text’] | 选取所有具有指定元素并且文本内容是text节点

浏览器插件 XPath Helper，在线验证 XPath，谷歌商店下载地址：https://chrome.google.com/webstore/detail/hgimnogjllphhhkhlmebbmlgjoejdpjl

XPath 基本使用方法：首先使用代码 `from lxml import etree`导入库，然后将 HTML 文档变成一个对象，再调用对象的方法去查找指定的节点，方法有两种：`tree = etree.parse()` 为本地文件查找，`tree = etree.HTML()` 为网络文件查找，再使用语句 `tree.xpath()` 查找指定节点。

# 【7.3】查找所有节点

新建一个 xpath.html 本地文件，内容如下：

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>xpath测试</title>
    </head>
    <body>
    <div class="song">
        火药
        <b>指南针</b>
        <b>印刷术</b>
        造纸术
    </div>
    <div class="tang">
        <ul>
            <li class="balove">停车坐爱枫林晚，霜叶红于二月花。</li>
            <li id="hua">商女不知亡国恨，隔江犹唱后庭花。</li>
            <li class="love" name="yang">一骑红尘妃子笑，无人知是荔枝来。</li>
            <li id="bei">葡萄美酒夜光杯，欲饮琵琶马上催。</li>
            <li><a href="http://www.baidu.com/">百度一下</a> </li>
        </ul>
        <ol>
            <li class="balucy">寻寻觅觅冷冷清清，凄凄惨惨戚戚。</li>
            <li class="lily">咋暖还寒时候，最难将息。</li>
            <li class="lilei">三杯两盏淡酒。</li>
            <li>怎敌他晚来风急。</li>
            <li>雁过也，正伤心，却是旧时相识。</li>
            <li>爱情三十六计</li>
            <li>什么是爱情</li>
        </ol>
    </div>
    </body>
    </html>

查找所有节点：

    1
    2
    3
    4
    5
    from lxml import etree

    html = etree.parse('./xpath.html')
    result = html.xpath('//*')
    print(result)

使用 `*` 代表匹配所有节点，整个 xpath.html 文件中的所有节点都会被获取到，返回形式是一个列表，每个元素是 Element 类型，其后跟了节点的名称，如 html、body、div、ul、li、a 等，所有节点都包含在列表中，输出结果如下：

    1
    [<Element html at 0x1a836a34508>, <Element head at 0x1a836a344c8>, <Element meta at 0x1a836a345c8>, <Element title at 0x1a836a34608>, <Element body at 0x1a836a34648>, <Element div at 0x1a836a346c8>, <Element b at 0x1a836a34708>, <Element b at 0x1a836a34748>, <Element div at 0x1a836a34788>, <Element ul at 0x1a836a34688>, <Element li at 0x1a836a347c8>, <Element li at 0x1a836a34808>, <Element li at 0x1a836a34848>, <Element li at 0x1a836a34888>, <Element li at 0x1a836a348c8>, <Element a at 0x1a836a34908>, <Element ol at 0x1a836a34948>, <Element li at 0x1a836a34988>, <Element li at 0x1a836a349c8>, <Element li at 0x1a836a34a08>, <Element li at 0x1a836a34a48>, <Element li at 0x1a836a34a88>, <Element li at 0x1a836a34ac8>, <Element li at 0x1a836a34b08>]

# 【7.4】查找子节点

通过 / 或 // 即可查找元素的子节点或子孙节点：

    1
    2
    3
    4
    5
     from lxml import etree

    html = etree.parse('./xpath.html')
    result = html.xpath('//ul/li')
    print(result)

选择 ul 节点的所有直接 li 子节点：

    1
    [<Element li at 0x2a094d044c8>, <Element li at 0x2a094d045c8>, <Element li at 0x2a094d04608>, <Element li at 0x2a094d04648>, <Element li at 0x2a094d04688>]

# 【7.5】查找父节点

知道了子节点，也可以用 .. 或者 parent:: 查找其父节点

    1
    2
    3
    4
    5
     from lxml import etree

    html = etree.parse('./xpath.html')
    result = html.xpath('//ol/../@class')
    print(result)

    1
    2
    3
    4
    5
    from lxml import etree

    html = etree.parse('./xpath.html')
    result = html.xpath('//ol/parent::*/@class')
    print(result)

先查找到 ol 节点，随后获取其父节点以及其 class 属性：

    1
    ['tang']

# 【7.6】属性匹配

有时候 HTML 包含多个相同名的节点，而节点的属性是不一样的，此时可以用 @ 符号进行属性过滤

    1
    2
    3
    4
    5
     from lxml import etree

    html = etree.parse('./xpath.html')
    result = html.xpath('//li[@class="balucy"]')
    print(result)

xpath.html 文件中，只有一个 class 为 balucy 的节点：`<li class="balucy">寻寻觅觅冷冷清清，凄凄惨惨戚戚。</li>`，运行以上代码将返回一个该元素：

    1
    [<Element li at 0x16e53aa54c8>]

# 【7.7】文本获取

使用 text() 方法即可提取节点中的文本：

    1
    2
    3
    4
    5
     from lxml import etree

    html = etree.parse('./xpath.html')
    result = html.xpath('//li[@class="balucy"]/text()')
    print(result)

输出结果：

    1
    ['寻寻觅觅冷冷清清，凄凄惨惨戚戚。']

再次观察 xpath.html 文件中的 `<ol></ol>`这一部分：

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <ol>
        <li class="balucy">寻寻觅觅冷冷清清，凄凄惨惨戚戚。</li>
        <li class="lily">咋暖还寒时候，最难将息。</li>
        <li class="lilei">三杯两盏淡酒。</li>
        <li>怎敌他晚来风急。</li>
        <li>雁过也，正伤心，却是旧时相识。</li>
        <li>爱情三十六计</li>
        <li>什么是爱情</li>
    </ol>

如果我们想要提取 `<li>` 节点里面所有的文本，就可以使用 `html.xpath('//ol/li/text()')` 语句：

    1
    2
    3
    4
    5
    from lxml import etree

    html = etree.parse('./xpath.html')
    result = html.xpath('//ol/li/text()')
    print(result)

输出结果：

    1
    ['寻寻觅觅冷冷清清，凄凄惨惨戚戚。', '咋暖还寒时候，最难将息。', '三杯两盏淡酒。', '怎敌他晚来风急。', '雁过也，正伤心，却是旧时相识。', '爱情三十六计', '什么是爱情']

同样还有另一种方法，使用 `html.xpath('//ol//text()')` 语句，`//` 将会选取所有子孙节点的文本，`<ol>` 和 `<li>` 节点下的换行符也将被提取出来：

    1
    2
    3
    4
    5
    from lxml import etree

    html = etree.parse('./xpath.html')
    result = html.xpath('//ol//text()')
    print(result)

输出结果：

    1
    ['\n        ', '寻寻觅觅冷冷清清，凄凄惨惨戚戚。', '\n        ', '咋暖还寒时候，最难将息。', '\n        ', '三杯两盏淡酒。', '\n        ', '怎敌他晚来风急。', '\n        ', '雁过也，正伤心，却是旧时相识。', '\n        ', '爱情三十六计', '\n        ', '什么是爱情', '\n    ']

# 【7.8】属性获取

与属性匹配一样，属性获取仍然使用 @：

    1
    2
    3
    4
    5
     from lxml import etree

    html = etree.parse('./xpath.html')
    result = html.xpath('//ul/li[5]/a/@href')
    print(result)

获取 href 属性：

    1
    ['http://www.baidu.com/']

# 【7.9】一个属性包含多个值的匹配

某个节点的某个属性可能有多个值，例如：

    1
    <li class="li li-first"><a href="link.html">first item</a></li>

li 节点的 class 属性有 li 和 li-first 两个值，如果使用 `html.xpath('//li[@class="li"]` 语句，将无法成功匹配，这时就需要使用 contains 方法了，第一个参数传入属性名称，第二个参数传入属性值，只要此属性包含所传入的属性值，就可以完成匹配了

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

输出结果：

    1
    ['first item']

# 【7.10】多个属性匹配一个节点

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

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

输出结果：

    1
    ['first item']

示例中运用了运算符 and 来连接，此外常见的运算符如下：

运算符 | 描述 | 实例 | 返回值
--- | --- | --- | ---
or | 或 | age=19 or age=20 | 如果 age 是 19 或者 20，则返回 true。如果 age 是其他值，则返回 false
and | 与 | age>19 and age<21 | 如果 age 大于 19 且小于 21，则返回 true。如果 age 是其他值，则返回 false
mod | 计算除法的余数 | 5 mod 2 | 1
| | 计算两个节点集 | //book | //cd | 返回所有拥有 book 和 cd 元素的节点集
+ | 加法 | 10 + 5 | 15
- | 减法 | 10 - 5 | 5
* | 乘法 | 10 * 5 | 50
div | 除法 | 10 div 5 | 2
= | 等于 | age=19 | 如果 age 是 19，则返回 true。如果 age 不是 19，则返回 false
!= | 不等于 | age!=19 | 如果 age 不是 19，则返回 true。如果 age 是 19，则返回 false
< | 小于 | age<19 | 如果 age 小于 19，则返回 true。如果 age 不小于 19，则返回 false
<= | 小于或等于 | age<=19 | 如果 age 小于等于 19，则返回 true。如果 age 大于 19，则返回 false
> | 大于 | age>19 | 如果 age 大于 19，则返回 true。如果 age 不大于 19，则返回 false
>= | 大于或等于 | age>=19 | 如果 age 大于等于 19，则返回 true。如果 age 小于 19，则返回 false

# 【7.11】按顺序选择节点

某些属性可能同时匹配了多个节点，如果要选择其中几个节点，可以利用中括号传入索引的方法获取特定次序的节点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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 = html.xpath('//li[1]/a/text()')
    print(result)
    result = html.xpath('//li[last()]/a/text()')
    print(result)
    result = html.xpath('//li[position()<3]/a/text()')
    print(result)
    result = html.xpath('//li[last()-2]/a/text()')
    print(result)

- li[1]：选取第一个 li 节点；
- li[last()]：选取最后一个 li 节点；
- position()<3：选取位置小于 3 的 li 节点；
- li[last()-2]：选取倒数第三个 li 节点

输出结果：

    1
    2
    3
    4
    ['first item']
    ['fifth item']
    ['first item', 'second item']
    ['third item']

# 【7.12】节点轴选择

节点轴选择：获取子元素、兄弟元素、父元素、祖先元素等

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    from lxml import etree

    text = '''
    <div>
        <ul>
             <li class="item-0"><a href="link1.html"><span>first item</span></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 = html.xpath('//li[1]/ancestor::*')
    print(result)
    result = html.xpath('//li[1]/ancestor::div')
    print(result)
    result = html.xpath('//li[1]/attribute::*')
    print(result)
    result = html.xpath('//li[1]/child::a[@href="link1.html"]')
    print(result)
    result = html.xpath('//li[1]/descendant::span')
    print(result)
    result = html.xpath('//li[1]/following::*[2]')
    print(result)
    result = html.xpath('//li[1]/following-sibling::*')
    print(result)

输出结果：

    1
    2
    3
    4
    5
    6
    7
    [<Element html at 0x1d3749e9548>, <Element body at 0x1d3749e94c8>, <Element div at 0x1d3749e9488>, <Element ul at 0x1d3749e9588>]
    [<Element div at 0x1d3749e9488>]
    ['item-0']
    [<Element a at 0x1d3749e9588>]
    [<Element span at 0x1d3749e9488>]
    [<Element a at 0x1d3749e9588>]
    [<Element li at 0x1d3749e94c8>, <Element li at 0x1d3749e95c8>, <Element li at 0x1d3749e9608>, <Element li at 0x1d3749e9648>]

基本语法：轴名称::节点测试[谓语]

轴名称对应的结果：

轴名称 | 结果
--- | ---
ancestor | 选取当前节点的所有先辈（父、祖父等）
ancestor-or-self | 选取当前节点的所有先辈（父、祖父等）以及当前节点本身
attribute | 选取当前节点的所有属性
child | 选取当前节点的所有子元素
descendant | 选取当前节点的所有后代元素（子、孙等）
descendant-or-self | 选取当前节点的所有后代元素（子、孙等）以及当前节点本身
following | 选取文档中当前节点的结束标签之后的所有节点
namespace | 选取当前节点的所有命名空间节点
parent | 选取当前节点的父节点
preceding | 选取文档中当前节点的开始标签之前的所有节点
preceding-sibling | 选取当前节点之前的所有同级节点
self | 选取当前节点

实例：

例子 | 结果
--- | ---
child::book | 选取所有属于当前节点的子元素的 book 节点
attribute::lang | 选取当前节点的 lang 属性
child::* | 选取当前节点的所有子元素
attribute::* | 选取当前节点的所有属性
child::text() | 选取当前节点的所有文本子节点
child::node() | 选取当前节点的所有子节点
descendant::book | 选取当前节点的所有 book 后代
ancestor::book | 选择当前节点的所有 book 先辈
ancestor-or-self::book | 选取当前节点的所有 book 先辈以及当前节点（如果此节点是 book 节点）
child::*/child::price | 选取当前节点的所有 price 孙节点

您的喜欢是作者写作最大的动力！❤️

- PayPal
- AliPay
- WeChatPay
- QQPay

Donate

最后更新于 2019年9月24日

爬虫

lxml

XPath

###### 上一页

#### Python3 爬虫学习笔记 C08

###### 爬虫 Beautiful Soup

###### 下一页

#### Python3 爬虫学习笔记 C06

###### 爬虫 正则表达式

#### 评论

## ๑۩ﺴ TRHX ﺴ۩๑

本文目录
站内导航

- 所有文章
- 文章归档
- RSS订阅
- 我的友链
- 留言板
- 关于博主

欢迎光临
访客地图
交流群组

QQ 交流群 Telegram 交流群

文章分类

- BLOG
  (4)
- CDN
  (1)
- Hexo
  (9)
- Java
  (1)
- Linux
  (2)
- Markdown
  (2)
- Python3 学习笔记
  (39)
- 基础学习
  (9)
- 学习经验
  (4)
- 爬虫学习
  (18)
- 爬虫实战
  (8)
- VPS
  (1)
- WEB前端
  (2)
- 图床
  (1)

热门标签
12306 58同城 Ajax BLOG Beautiful Soup CDN Coding Pages Deepin Elicpse GitHub Pages Github Github Pages HTTPS Hexo ICP备案 JDBC JS 预加载 Markdown Material X MongoDB MySQL PEP8 PicGo Pygame Python Redis SEO SQL Server 2012 SSR Selenium Ubuntu User-Agent VMware VPS XPath if语句 input()函数 instant.page jsDelivr lxml pyspider requests spfk urllib while循环 主题个性化 代理 公安备案 函数 列表 变量 哔哩哔哩 图床 图形验证码 垃圾 备份 字典 安居客 年终总结 异常 技巧 操作列表 数据类型 文件 文件储存 无界面浏览器 模块 正则表达式 滑动验证码 点触验证码 爬虫 猫眼电影 瓜子二手车 类 继承 编辑器 虎扑论坛 豆瓣电影
最近在听