欢迎光临
我们一直在努力

XPath高级语法完全指南:从轴语法到实战应用

一、XPath是什么?为什么重要?

大家好!今天我们来深入探讨XPath的高级语法。如果你做过网页爬虫,一定知道XPath是解析HTML/XML的利器。但很多人只停留在基础用法上,今天我要带你解锁XPath的全部潜力!

XPath的重要性:

  • 精准定位:在复杂HTML结构中精确定位元素

  • 效率提升:一条高级XPath可能代替多行代码

  • 灵活性:处理各种复杂的网页结构

  • 广泛应用:不仅用于爬虫,还在XML处理、数据提取等领域广泛使用


  • 二、XPath轴语法详解(13种核心轴)

    轴是XPath中最强大的功能之一,它定义了节点与当前节点之间的关系

    2.1 基础环境设置

    from lxml import etree

    # 示例XML结构
    parse_str = '''
    <bookstore>
    <book>
    <title>Python编程</title>
    <author>张三</author>
    </book>
    </bookstore>
    '''
    html = etree.HTML(parse_str)

    2.2 13种轴语法全解析

    1. child:: – 子节点轴

    选择当前节点的所有子节点

    # 完整语法
    result = html.xpath('//book/child::title/text()')
    print("child:: 结果:", result) # 输出: ['Python编程']

    # 简写形式(推荐)
    result = html.xpath('//book/title/text()')
    print("简写结果:", result) # 输出: ['Python编程']

    2. parent:: – 父节点轴

    选择当前节点的父节点

    # 完整语法
    result = html.xpath('//author/parent::book')
    print("父节点:", result)

    # 简写形式(推荐)
    result = html.xpath('//author/..')
    print("简写父节点:", result)

    3. ancestor:: – 祖先节点轴

    选择当前节点的所有祖先节点

    result = html.xpath('//title/ancestor::bookstore')
    print("祖先节点:", result)

    4. ancestor-or-self:: – 祖先及自身轴

    选择当前节点的所有祖先节点及自身

    result = html.xpath('//author/ancestor-or-self::*')
    print("祖先及自身:", result)

    5. descendant:: – 后代节点轴

    选择当前节点的所有后代节点(子节点、孙节点等)

    result = html.xpath('//bookstore/descendant::author')
    print("后代节点:", result)

    # 简写形式(非常常用!)
    result = html.xpath('//bookstore//author')
    print("简写后代:", result)

    6. descendant-or-self:: – 后代及自身轴

    选择当前节点的所有后

    result = html.xpath('//bookstore/descendant-or-self::*')
    print("后代及自身:", result)

    7. following-sibling:: – 后续同级兄弟轴

    选择当前节点之后的所有同级节点

    str_2 = '''
    <bookstore>
    <title id="0">…</title>
    <book id="1">…</book>
    <book id="2">…</book>
    <book id="3">…</book>
    <title id="4">…</title>
    </bookstore>
    '''
    html = etree.HTML(str_2)

    result = html.xpath('//book[1]/following-sibling::book/@id')
    print("后续兄弟节点:", result) # 输出: ['2', '3']

    8. preceding-sibling:: – 前驱同级兄弟轴

    选择当前节点之前的所有同级节点

    result = html.xpath('//book[3]/preceding-sibling::book/@id')
    print("前驱兄弟节点:", result) # 输出: ['1', '2']

    9. following:: – 后续节点轴

    选择文档中当前节点之后的所有节点(按文档顺序)

    result = html.xpath('//book[1]/following::title/@id')
    print("后续所有节点:", result) # 输出: ['4']

    10. preceding:: – 前驱节点轴

    选择文档中当前节点之前的所有节点(按文档顺序)

    result = html.xpath('//title[last()]/preceding::title/@id')
    print("前驱所有节点:", result) # 输出: ['0']

    11. attribute:: – 属性轴

    选择当前节点的所有属性

    str_3 = '''
    <book id="101" category="tech">Freedom</book>
    <book id="102">1</book>
    '''
    html = etree.HTML(str_3)

    # 完整语法
    result = html.xpath('//book/attribute::*')
    print("所有属性:", result)

    # 简写形式(最常用!)
    result = html.xpath('//book/@*')
    print("简写属性:", result) # 输出: ['101', 'tech', '102']

    12. namespace:: – 命名空间轴

    选择当前节点的所有命名空间节点(较少使用)

    result = html.xpath('//book/namespace::*')
    print("命名空间:", result)

    13. self:: – 自身轴

    选择当前节点自身

    # 完整语法
    result = html.xpath('//book/self::*')
    print("自身节点:", result)

    # 简写形式
    result = html.xpath('//book/.')
    print("简写自身:", result)

    2.3 轴语法记忆口诀

    text

    child:: – 孩子
    parent:: – 父母
    ancestor:: – 祖先
    descendant:: – 后代
    sibling:: – 兄弟
    self:: – 自己
    attribute:: – 属性


    三、位置路径与谓词

    3.1 位置路径表达式

    # 绝对路径 – 从根节点开始
    result = html.xpath('/bookstore/book/title')

    # 相对路径 – 从当前节点开始
    # 假设当前节点是bookstore
    result = html.xpath('book/title')

    # 任意位置 – 最常用!
    result = html.xpath('//title') # 所有title元素
    result = html.xpath('//book//title') # book下的所有title

    3.2 位置谓词(数字筛选)

    # 第一个book元素
    result = html.xpath('//book[1]')

    # 最后一个book元素
    result = html.xpath('//book[last()]')

    # 倒数第二个book元素
    result = html.xpath('//book[last()-1]')

    # 位置大于1的book元素
    result = html.xpath('//book[position()>1]')

    # 前3个book元素
    result = html.xpath('//book[position()<=3]')

    3.3 属性谓词

    python

    # 具有id属性的book元素
    result = html.xpath('//book[@id]')

    # id属性值为'101'的book元素
    result = html.xpath('//book[@id="101"]')

    # category属性为'tech'的book元素
    result = html.xpath('//book[@category="tech"]')

    # id不等于'101'的book元素
    result = html.xpath('//book[@id!="101"]')

    # 多属性筛选
    result = html.xpath('//book[@id and @category]')

    3.4 文本内容谓词

    python

    # price子元素值大于35的book
    result = html.xpath('//book[price>35]')

    # author子元素文本为'张三'的book
    result = html.xpath('//book[author="张三"]')

    # title包含'Python'的book
    result = html.xpath('//book[contains(title, "Python")]')

    3.5 组合谓词

    python

    # 使用and连接多个条件
    result = html.xpath('//book[@category="tech" and price>30]')

    # 使用or连接多个条件
    result = html.xpath('//book[@id="101" or @category="fiction"]')

    # 复杂组合
    result = html.xpath('//book[position()>=2 and position()<=4 and price>40]')


    四、XPath函数大全

    4.1 节点集函数

    # last() – 返回最后一个位置
    result = html.xpath('//book[last()]')
    result = html.xpath('//book[position()=last()]')

    # position() – 返回当前位置
    result = html.xpath('//book[position() mod 2 = 0]') # 偶数位置
    result = html.xpath('//book[position()=1 or position()=last()]')

    # count() – 统计节点数量
    result = html.xpath('//bookstore[count(book)>5]')
    # 注意:count()在谓词中直接使用

    4.2 字符串函数

    # text() – 获取文本内容
    result = html.xpath('//book/text()')

    # string() – 转换为字符串
    result = html.xpath('string(//book[1]/price)')

    # concat() – 连接字符串
    result = html.xpath('concat(//book[1]/author, "-", //book[1]/title)')

    # contains() – 是否包含子串
    result = html.xpath('//book[contains(title, "Python")]')
    result = html.xpath('//book[contains(@category, "tech")]')

    # starts-with() – 是否以指定前缀开始
    result = html.xpath('//book[starts-with(title, "Python")]')

    # substring() – 提取子字符串
    # 获取第1到第6个字符
    result = html.xpath('substring(//book[1]/title, 1, 6)')
    # 从第2个字符到末尾
    result = html.xpath('substring(//book[1]/title, 2)')

    # string-length() – 字符串长度
    result = html.xpath('//book[string-length(title)>10]')

    # normalize-space() – 删除多余空格
    test_2 = '''
    <author>
    张三
    </author>
    '''
    html = etree.HTML(test_2)
    result = html.xpath('normalize-space(//author)')
    print("规范化空格:", result) # 输出: '张三'

    4.3 数值函数

    # number() – 转换为数值
    result = html.xpath('number(//book[1]/price)')

    # floor() – 向下取整
    result = html.xpath('floor(//book[1]/price)')

    # ceiling() – 向上取整
    result = html.xpath('ceiling(//book[1]/price)')

    # round() – 四舍五入
    result = html.xpath('round(//book[1]/price)')

    4.4 布尔函数

    # boolean() – 转换为布尔值
    result = html.xpath('boolean(//book)')

    # not() – 逻辑非
    result = html.xpath('//book[not(@category)]') # 没有category属性的book
    result = html.xpath('//book[not(price > 30)]') # price不大于30的book

    # true()/false() – 布尔常量
    # 在比较中使用

    4.5 集合函数(XPath 2.0+)

    # distinct-values() – 返回唯一值
    # 注意:lxml不完全支持XPath 2.0
    # distinct-values(//book/@category)


    五、运算符详解

    5.1 算术运算符

    # 基本算术运算
    result = html.xpath('//book[price * 0.9 < 40]') # 价格的90%小于40
    result = html.xpath('//book[price div 2 > 20]') # 价格除以2大于20
    result = html.xpath('//book[position() mod 2 = 0]') # 偶数位置

    5.2 比较运算符

    # 等于
    result = html.xpath('//book[@id="101"]')

    # 不等于
    result = html.xpath('//book[@id!="101"]')

    # 小于(在XML中需要转义)
    result = html.xpath('//book[price &lt; 30]') # 使用实体
    result = html.xpath('//book[price < 30]') # 某些上下文可用

    # 小于等于
    result = html.xpath('//book[price <= 30]')

    # 大于
    result = html.xpath('//book[price > 35]')

    # 大于等于
    result = html.xpath('//book[price >= 35]')

    5.3 逻辑运算符

    # and – 逻辑与
    result = html.xpath('//book[@category="tech" and price>30]')

    # or – 逻辑或
    result = html.xpath('//book[price<20 or price>50]')

    # not() – 逻辑非(函数形式)
    result = html.xpath('//book[not(@category="fiction")]')


    六、通配符与节点测试

    6.1 通配符

    # * – 匹配任意元素节点
    result = html.xpath('//book/*') # book的所有子元素

    # @* – 匹配任意属性节点
    result = html.xpath('//book/@*') # book的所有属性
    result = html.xpath('//*[@*]') # 具有任意属性的所有元素

    6.2 节点测试

    # node() – 匹配任意节点
    result = html.xpath('//book/node()') # book的所有子节点

    # text() – 匹配文本节点
    result = html.xpath('//book/title/text()')

    # comment() – 匹配注释节点
    # result = html.xpath('//comment()')

    # processing-instruction() – 匹配处理指令节点
    # result = html.xpath('//processing-instruction()')


    七、综合实战演练

    现在我们来一个完整的实战,把前面学的都用上!

    from lxml import etree

    # 复杂的XML结构
    parse_str = '''
    <?xml version="1.0" encoding="UTF-8"?>
    <bookstore>
    <category name="科技" id="cat1">
    <book id="101" category="tech" language="zh">
    <title>Python编程入门</title>
    <author>张三</author>
    <price>45.50</price>
    <year>2023</year>
    <description>这是一本关于Python编程的入门书籍。</description>
    </book>
    <book id="102" category="tech" language="en">
    <title>Advanced JavaScript</title>
    <author>John Smith</author>
    <price>52.00</price>
    <year>2022</year>
    <description>Advanced JavaScript programming techniques.</description>
    </book>
    <book id="103" category="tech" language="zh">
    <title>数据结构与算法</title>
    <author>李四</author>
    <price>38.00</price>
    <year>2023</year>
    </book>
    </category>
    <category name="文学" id="cat2">
    <book id="201" category="fiction" language="zh">
    <title>红楼梦</title>
    <author>曹雪芹</author>
    <price>65.00</price>
    <year>1791</year>
    </book>
    <book id="202" category="fiction" language="zh">
    <title>西游记</title>
    <author>吴承恩</author>
    <price>58.00</price>
    <year>1592</year>
    </book>
    </category>
    <category name="历史" id="cat3">
    <book id="301" category="history">
    <title>中国通史</title>
    <author>王五</author>
    <price>72.00</price>
    <year>2021</year>
    </book>
    </category>
    </bookstore>
    '''

    html = etree.HTML(parse_str)

    print("=" * 60)
    print("实战演练开始!")
    print("=" * 60)

    # 1. 基础查询
    print("\\n1. 所有书籍的ID:")
    result = html.xpath('//book/@id')
    print(result) # ['101', '102', '103', '201', '202', '301']

    # 2. 轴语法组合
    print("\\n2. 科技类别的第一个书籍之后的所有书籍:")
    result = html.xpath('//category[@name="科技"]/book[1]/following-sibling::book/@id')
    print(result) # ['102', '103']

    # 3. 复杂谓词
    print("\\n3. 中文书籍且价格小于50:")
    result = html.xpath('//book[@language="zh" and price<50]/@id')
    print(result) # ['101', '103']

    # 4. 函数应用
    print("\\n4. 标题包含'Python'的书籍:")
    result = html.xpath('//book[contains(title, "Python")]/@id')
    print(result) # ['101']

    print("\\n5. 标题长度大于10的书籍:")
    result = html.xpath('//book[string-length(title)>10]/@id')
    print(result) # ['101', '102', '103', '201', '202', '301']

    # 5. 高级组合
    print("\\n6. 奇数位置的书籍:")
    result = html.xpath('//book[position() mod 2 = 1]/@id')
    print(result) # ['101', '103', '201', '301']

    print("\\n7. 没有描述信息的书籍:")
    result = html.xpath('//book[not(description)]/@id')
    print(result) # ['103', '201', '202', '301']

    # 6. 跨层级查询
    print("\\n8. 作者为张三的书籍标题:")
    result = html.xpath('//book[author="张三"]/title/text()')
    print(result) # ['Python编程入门']

    # 7. 属性与文本组合
    print("\\n9. 连接作者和书名:")
    result = html.xpath('concat(//book[1]/author, " – ", //book[1]/title)')
    print(result) # '张三 – Python编程入门'

    # 8. 综合挑战
    print("\\n10. 科技类别中,2023年出版且价格>40的书籍:")
    result = html.xpath('//category[@name="科技"]/book[year=2023 and price>40]/@id')
    print(result) # ['101']

    print("\\n11. 所有类别中至少有一本书价格>60的类别:")
    result = html.xpath('//category[book[price>60]]/@name')
    print(result) # ['文学', '历史']

    print("\\n12. 第一个类别的最后一本书:")
    result = html.xpath('//category[1]/book[last()]/@id')
    print(result) # ['103']


    八、常见问题与解决方案

    问题1:XPath提取不到数据

    可能原因:

  • 网页结构有变化

  • XPath写错了

  • 有iframe或动态加载内容

  • 解决方案:

    # 1. 打印HTML确认结构
    print(etree.tostring(html, encoding='unicode')[:500])

    # 2. 使用更通用的XPath
    # 不推荐://div[3]/ul[2]/li[4]/a
    # 推荐://a[contains(@class, "target-class")]

    # 3. 使用浏览器开发者工具复制XPath
    # 右键元素 → 复制 → 复制XPath

    问题2:提取的文本有空格或换行

    解决方案:

    # 使用normalize-space()函数
    result = html.xpath('normalize-space(//div/text())')

    # 或者用strip()后处理
    result = [text.strip() for text in html.xpath('//div/text()')]

    问题3:需要处理相对路径

    解决方案:

    # 先找到父元素,再从父元素开始查找
    books = html.xpath('//book')
    for book in books:
    # 在book元素的上下文中查找
    title = book.xpath('.//title/text()') # 注意开头的点!
    author = book.xpath('.//author/text()')


    九、实战演示

    拿之前博客发的一篇 SpiderBuf–爬虫练习网站手把手带练(最新独特版10-20)-CSDN博客

    里面的第十五题 来举例子  但是之前的写法是最方便最简单的

    这里我直接将页面的源代码复制到本地html文件中

    首先来提取这个豆瓣的评分  和这个span是一个节点  定位到这个豆瓣电影评分 提取后面兄弟的第一个文本节点 

    score = li.xpath('.//span[contains(text(),"豆瓣电影评分:")]/following-sibling::text()[1]')

    提取出来是这样的  前面还有个空格 我们可以 转换成字符串 然后去除两端的空格

    from lxml import etree

    # 读取文件
    with open('GQ.html', 'r', encoding='utf-8') as f:
    text = f.read()

    html = etree.HTML(text).xpath('.//div[contains(@class,"row")]')
    for li in html:
    score = ''.join(li.xpath('.//span[contains(text(),"豆瓣电影评分:")]/following-sibling::text()[1]')).strip()

    其次这个导演 也是一样的

    director = ''.join(li.xpath('.//span[contains(text(),"导演")]/following-sibling::span/text()')).strip()

    # 定位到和导演这个span标签 后面的兄弟节点span中的文本

    接着我们来看这个 主演  主演有很多 回了这个 其它的也就会了

    思路:  定位到主演这个span标签 然后定位到其父标签 之后的兄弟节点 中的文本 拿到第一个文本即可

    actors = ''.join(li.xpath('.//span[contains(text(),"主演")]/following-sibling::span[1]/text()'))
    actors += ''.join(li.xpath('normalize-space(.//span[contains(text(),"主演")]/../following-sibling::text()[1])'))

    提取出来是以上图片中的样子  用 这个noarmalize-space函数去除空格 和之前的字段做个拼接即可

    大家也可以多用高级语法 去提取复杂的静态文件 多加练习运用   Practice makes perfect!


    你的点赞和关注是我更新的最大动力!

    如果这篇教程对你有帮助:

  • 👍 点赞支持一下

  • 💬 评论分享你的XPath使用经验

  • 🔔 关注我获取更多技术教程

  • ⭐ 收藏本文,方便随时查阅

  • XPath之路,我们一起精通! 🚀

    赞(0)
    未经允许不得转载:171主机测评 » XPath高级语法完全指南:从轴语法到实战应用
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址