zoukankan      html  css  js  c++  java
  • XPath解析

    使用XPath用来解析网页已经在https://www.cnblogs.com/wkfvawl/p/14725960.html上面提到了一点,但不是很详细,这里结合w3school的教程,使用python实践一下。

    https://www.w3school.com.cn/xpath/index.asp

    本文主要内容转自https://www.jianshu.com/p/85a3004b5c06

    XPath

        XPath,全称 XML Path Language,即 XML 路径语言,它是一门在 XML 文档中查找信息的语言。最初是用来搜寻 XML 文档的,但同样适用于 HTML 文档的搜索。所以在做爬虫时完全可以使用 XPath 做相应的信息抽取。

    1. XPath 概览

        XPath 的选择功能十分强大,它提供了非常简洁明了的路径选择表达式。另外,它还提供了超过 100 个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等,几乎所有想要定位的节点都可以用 XPath 来选择。
        官方文档:https://www.w3.org/TR/xpath/

    2. XPath 常用规则

    表达式描述
    nodename 选取此节点的所有子节点。
    / 从根节点选取。
    // 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
    . 选取当前节点。
    .. 选取当前节点的父节点。
    @ 选取属性。

        这里列出了 XPath 的常用匹配规则,示例如下:

    //title[@lang='eng']

        这是一个 XPath 规则,代表的是选择所有名称为 title,同时属性 lang 的值为 eng 的节点,后面会通过 Python 的 lxml 库,利用 XPath 进行 HTML 的解析。

    3. 安装

    windows->python3环境下:pip install lxml

    4. 实例引入

    首先导入 lxml 库的 etree 模块,然后声明一段 HTML 文本,调用 HTML 类进行初始化,成功构造一个 XPath 解析对象。注意:HTML 文本中最后一个 li 节点没有闭合,但是 etree 模块可以自动修正 HTML 文本。
    test.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">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>
    调用 tostring() 方法即可输出修正后的 HTML 代码,但结果是 bytes 类型,可以用 decode() 方法将其转化为 str 类型,结果如下:
    from lxml import etree
    
    html = etree.parse('./test.html', etree.HTMLParser())
    result = etree.tostring(html)
    print(result.decode('utf-8'))

    输出:

    经过处理后,li 节点标签被补全,并且还自动添加了 body、html 节点。还有一个 DOCTYPE 声明。

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
    <html><body><div>&#13;
    <ul>&#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;
    </li></ul>&#13;
    </div></body></html>

    5. 所有节点

        用以 // 开头的 XPath 规则来选取所有符合要求的节点:

    from lxml import etree
    
    html = etree.parse('./test.html', etree.HTMLParser())
    result = html.xpath('//*')
    print(result)
    
    # 运行结果
    """
    [<Element html at 0x1d6610ebe08>, <Element body at 0x1d6610ebf08>, 
    <Element div at 0x1d6610ebf48>, <Element ul at 0x1d6610ebf88>, 
    <Element li at 0x1d6610ebfc8>, <Element a at 0x1d661115088>, 
    <Element li at 0x1d6611150c8>, <Element a at 0x1d661115108>, 
    <Element li at 0x1d661115148>, <Element a at 0x1d661115048>, 
    <Element li at 0x1d661115188>, <Element a at 0x1d6611151c8>, 
    <Element li at 0x1d661115208>, <Element a at 0x1d661115248>]
    """

    * 代表匹配所有节点,返回的结果是一个列表,每个元素都是一个 Element 类型,后跟节点名称。
        也可以指定匹配的节点名称:

    from lxml import etree
    
    html = etree.parse('./test.html', etree.HTMLParser())
    result = html.xpath('//li')
    print(result)
    
    # 运行结果
    [<Element li at 0x23fb219af08>, <Element li at 0x23fb219af48>, <Element li at 0x23fb219af88>, 
    <Element li at 0x23fb219afc8>, <Element li at 0x23fb21c5048>]
    
    <Element li at 0x23fb219af08>

    取出其中某个对象时可以直接用索引。

    6. 子/孙节点

        通过 / 或 // 即可查找元素的子节点或子孙节点。选择 li 节点的所有直接 a 子节点:

    from lxml import etree
    
    html = etree.parse('./test.html', etree.HTMLParser())
    result = html.xpath('//li/a')
    print(result)

    此处的 / 用来获取直接子节点,如果要获取所有子孙节点,将 / 换成 // 即可。

    7. 父节点

        知道子节点,查询父节点可以用 .. 来实现:

    # 获得 href 属性为 link4.html 的 a 节点的父节点的 class 属性
    
    # 方法一
    from lxml import etree
    
    html = etree.parse('./test.html', etree.HTMLParser())
    result = html.xpath('//a[@href="link4.html"]/../@class')
    print(result)
    # 运行结果:['item-1']
    
    # 方法二
    from lxml import etree
    
    html = etree.parse('./test.html', etree.HTMLParser())
    result = html.xpath('//a[@href="link4.html"]/parent::*/@class')
    print(result)
    
    # 运行结果:['item-1']

    解释一下:a[@href="link4.html"] 取所有a元素,且这些元素拥有值为link4.html的href属性

    8. 属性匹配

        匹配时可以用@符号进行属性过滤:

    from lxml import etree
    
    html = etree.parse('./test.html', etree.HTMLParser())
    result = html.xpath('//li[@class="item-inactive"]')
    print(result)
    
    # 运行结果:[<Element li at 0x2089793a3c8>]

    9. 文本获取

        有两种方法:一是获取文本所在节点后直接获取文本,二是使用 //。

    # 第一种
    from lxml import etree
    
    html = etree.parse('./test.html', etree.HTMLParser())
    result = html.xpath('//li[@class="item-0"]/a/text()')
    print(result)
    
    # 第二种
    from lxml import etree
    
    html = etree.parse('./test.html', etree.HTMLParser())
    result = html.xpath('//li[@class="item-0"]//text()')
    print(result)

    两种方式运行结果对比:

    ['first item', 'fifth item']
    ['first item', 'fifth item', '
    ']

    第二种方法会获取到补全代码时换行产生的特殊字符,推荐使用第一种方法,可以保证获取的结果是整洁的。

    10. 属性获取

        在 XPath 语法中,@符号相当于过滤器,可以直接获取节点的属性值:

    from lxml import etree
    
    html = etree.parse('./test.html', etree.HTMLParser())
    # 用以 // 开头的 XPath 规则来选取所有符合要求的节点
    result = html.xpath('//li/a/@href')
    print(result)
    
    # 运行结果:['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']

    11. 属性多值匹配

        有时候,某些节点的某个属性可能有多个值:

    class节点有两个属性值li和li-first

    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)
    
    # 运行结果:['first item']

    12. 多属性匹配

        当前节点有多个属性时,需要同时进行匹配:

    from lxml import etree
    
    text = '''
    <li class="li li-first" name="item"><a href="link.html">first item</a></li>
    '''
    html = etree.HTML(text)
    result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
    print(result)
    
    # 运行结果:['first item']

    XPath 运算符

    下面列出了可用在 XPath 表达式中的运算符:

    运算符描述实例返回值
    | 计算两个节点集 //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

    13. 按序选择

        匹配结果有多个节点,需要选中第二个或最后一个,可以按照中括号内加索引或其他相应语法获得:

    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)
    
    """
    运行结果:
    ['first item']
    ['fifth item']
    ['first item', 'second item']
    ['third item']
    """

    14. 节点轴选择

        XPath 提供了很多节点轴选择方法,包括子元素、兄弟元素、父元素、祖先元素等:

    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)
    # 获取 div 祖先节点
    result = html.xpath('//li[1]/ancestor::div')
    print(result)
    # 获取当前节点所有属性值
    result = html.xpath('//li[1]/attribute::*')
    print(result)
    # 获取 href 属性值为 link1.html 的直接子节点
    result = html.xpath('//li[1]/child::a[@href="link1.html"]')
    print(result)
    # 获取所有的的子孙节点中包含 span 节点但不包含 a 节点
    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)
    
    """
    [<Element html at 0x231a8965388>, <Element body at 0x231a8965308>, <Element div at 0x231a89652c8>, <Element ul at 0x231a89653c8>]
    [<Element div at 0x231a89652c8>]
    ['item-0']
    [<Element a at 0x231a89653c8>]
    [<Element span at 0x231a89652c8>]
    [<Element a at 0x231a89653c8>]
    [<Element li at 0x231a8965308>, <Element li at 0x231a8965408>, <Element li at 0x231a8965448>, <Element li at 0x231a8965488>]
    """

     求xpath,找出含有某子元素的元素

    比如,找出所有含有<img>的<table>

    //table[.//img]

    作者:王陸

    -------------------------------------------

    个性签名:罔谈彼短,靡持己长。做一个谦逊爱学的人!

    本站使用「署名 4.0 国际」创作共享协议,转载请在文章明显位置注明作者及出处。鉴于博主处于考研复习期间,有什么问题请在评论区中提出,博主尽可能当天回复,加微信好友请注明原因

  • 相关阅读:
    count(*) 和 count(1)和count(列名)区别
    网页横向滚动条
    发送公众号模板消息
    tp中S与session()
    php 判断sql执行时间
    thinkphp联查
    php 获取当前时间
    微信分享
    测试用手机奇怪问题
    翻译|多少植物才能净化室内空气?
  • 原文地址:https://www.cnblogs.com/wkfvawl/p/14804667.html
Copyright © 2011-2022 走看看