• 初入xpath


    写在前面

    在获取到服务器响应(HTML源码)之后,我们可以通过正则来提取想要的信息,但是编写正则太过麻烦,也容易出错。然而强大的python有强大的解析库,可以供苦逼的码农食用,例如lxml, beautiful Soup. pyquery等,通过这些解析库,就可以根据网页的ID ,class等属性或者节点之间的层次关系来获取想要的数据。

    这篇水文,写的是通过lxml库来实现用XPath来提取需要的信息。

    #windows平台下安装
    pip3 install lxml
    

    XPath,全称XML path Language,即XML路径语言,它是一门在XML文档中查找信息的语言,但是它同样适用于HTML文档的查找信息。

    1.初始化xpath对象

    想要用XPath在HTML源码中提取想要的信息,需要用etree模块对HTML源码进行初始化构造XPath对象。

    情景1 —— 对HTML字符串进行初始化

    html = etree.HTML(text)
    

    注释

    etree.HTML():构造了一个XPath解析对象并对HTML文本进行自动修正。

    demo

    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>
    '''
    #初始化一个xpath对象
    html = etree.HTML(text)
    #利用etree.tostring()方法进行转换称为字符串进行输出,不过却是tytes类型的,用decode转成str类型。
    result = etree.tostring(html).decode('utf-8')
    print(result)
    """ 
    输出 
    <html><body><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>
         </li></ul>
     </div>
    </body></html>
    """
    

    情景2 —— 对HTML文档进行初始化

    html = etree.parse('./text.html_path',etree.HTMLParser())
    

    2.xpath常用规则

    表达式 描述
    nodename 选取此节点的所有子节点
    / 选取当前节点的所有子节点
    // 选取当前节点的所有子孙节点
    . 选取当前节点
    .. 选取当前节点的父节点
    @ 选取属性
    [@attrib_name='value‘] 选取具有属性attrib_name属性并且值等于value属性的元素

    3.XPath筛选数据基础

    result = html.xpath('匹配规则')
    

    注释

    所有满足要求的元素对象以列表的形式返回。

    (1)获取所有节点

    result = html.xpath('//*')
    

    demo

    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)
    element = html.xpath("//*")  #单双引号都可以
    print(len(element)) #输出14
    print(element) #以列表的形式输出所有满足要求的元素
    
    """ [<Element html at 0x2ab2b37c1c0>, <Element body at 0x2ab2b6a9b40>,
     <Element div at 0x2ab2b6a9bc0>, <Element ul at 0x2ab2b6a9c80>,
      <Element li at 0x2ab2b6a9cc0>, <Element a at 0x2ab2b6a9d40>,
       <Element li at 0x2ab2b6a9d80>, <Element a at 0x2ab2b6a9dc0>,
        <Element li at 0x2ab2b6a9e00>, <Element a at 0x2ab2b6a9d00>,
     <Element li at 0x2ab2b6a9e40>, <Element a at 0x2ab2b6a9e80>, 
     <Element li at 0x2ab2b6a9ec0>, <Element a at 0x2ab2b6a9f00>] 
     """
    

    (2)获取指定元素

    指定元素名来获取

    result = html.xpath('//element_name')
    

    demo —— 获取所有的li元素

    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)
    element = html.xpath("//li")  #单双引号都可以
    print(len(element))   #s输出 5
    print(element)
    """
    输出
    
     [<Element li at 0x1d9476a9a40>,
     <Element li at 0x1d9476a9ac0>, 
     <Element li at 0x1d9476a9b80>, 
     <Element li at 0x1d9476a9bc0>,
    <Element li at 0x1d9476a9c00>] 
      """
    

    (3)获取子节点

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

    demo —— 获取a节点

    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)
    
    #方法一 : 通过子节点的方式
    element = html.xpath("//li/a")  #单双引号都可以
    print(len(element)) 
    print(element)
    
    #方法二: 通过孙节点的方式
    element = html.xpath("//ul//a")  #单双引号都可以
    print(len(element))  
    print(element)
    
    """ 
    都输出
    5
    [<Element a at 0x16ee8cb9b00>, 
    <Element a at 0x16ee8cb9b80>,
     <Element a at 0x16ee8cb9c40>, 
     <Element a at 0x16ee8cb9c80>, 
     <Element a at 0x16ee8cb9cc0>]
     """
    

    (4)属性匹配

    在选取信息的时候可以用@符号和中括号[ ]进行属性的过滤。

    demo —— 选取class属性等于item-inactive的li

    ​```
    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)
    
    #方法一 : 通过子节点的方式
    element = html.xpath("//li[@class='item-inactive']")  #单双引号都可以
    print(len(element)) 
    print(element)
    """ 
    输出
    1
    [<Element li at 0x211c9da9b00>]
    """
    

    (5)获取文本

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

    demo

    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)
    element = html.xpath("//li[@class='item-inactive']/a/text()")  
    print(element)
    """ 
    输出
    ['third item']
     """
    

    用XPath中text()方法来获取文本,可能会包括一些不需要的其他文本,比如换行符( )

    (6)获取属性

    前面提到了,用关键字@来进行class属性筛选,这里属性的获取也是用关键字@。

    demo

    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)
    element = html.xpath("//li/a/@href")  
    print(element)
    """ 
    输出
    ['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
     """
    

    属性获取和属性筛选的对比

    名称 描述
    属性获取 属性获取直接是@加属性名称
    属性筛选 属性筛选用中括号来限制怕[@class='value']

    4.XPath筛选数据进阶

    (1)属性多值匹配

    有时候,某些节点某个属性可能会有多个值例如

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

    如果用之前筛选属性的方法来筛选的话会解析不到该节点。

    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[@class='li']//text()")
    print(result)#输出 []
    

    这时候就要用到contains(@属性名称,值)函数,代码改写如下:

    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')]//text()")
    print(result)#输出 [['first item']]
    

    等效于

    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[@class="li li-first"]//text()')
    print(result)#输出 [['first item']]
    

    (2)多属性的匹配

    有时候还会遇到多个属性确定一个节点的情况,例如

    <!-- 同时具有class属性和name属性 -->
    <li class="li li-first" name="item"><a href="link.html">first item</a></li>
    

    这个时候就要用到运算符and来连接两个条件

    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"]//text()')
    print(result)#输出 ['first item']
    

    其他XPath运算符

    运算符 描述
    or
    amd
    mod 取余
    | 交集
    + 数值加法
    - 数值减法
    * 乘法
    div 除法
    = 等于
    != 不等于
    < 小于
    > 大于
    >= 大于等于

    (3)按序选择

    有时候,我们在选择的时候某些属性可能同时匹配了多个节点,但是只想要其中的某个节点,如第二个节点或者最后一个节点,可以利用中括号传入索引的方法获取特定次序的节点。

    from lxml import etree
    text = text = '''
    <div>
        <ul>
             <li class="item-0"><a href="link1.html">one</a></li>
             <li class="item-1"><a href="link2.html">two</a></li>
             <li class="item-inactive"><a href="link3.html">three</a></li>
             <li class="item-1"><a href="link4.html"> four</a></li>
             <li class="item-0"><a href="link5.html">five</a>
         </ul>
     </div>
    '''
    html = etree.HTML(text)
    #返回第一个li里的a的文本
    one = html.xpath('//li[1]//a/text()')
    print(one) #输出 ['one']
    #选取前三个
    two=html.xpath('//li[position()<=3]/a/text()')
    print(two) #输出 ['one', 'two', 'three']
    #选取最后一个
    last = html.xpath('//li[last()]/a/text()')
    print(last) #输出['five']
    #选取倒数第二个
    four = html.xpath('//li[last()-1]/a/text()')
    print(four) #输出[' four']
    

    括号里面的数字是从1开始的。

    (4)节点轴选择

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

    from lxml import etree
    text = text = '''
    <div>
        <ul>
             <li class="item-0" name='one'><a href="link1.html">one</a></li>
             <li class="item-1"><a href="link2.html">two</a></li>
             <li class="item-inactive"><a href="link3.html">three</a></li>
             <li class="item-1"><a href="link4.html"> four</a></li>
             <li class="item-0"><a href="link5.html">five</a>
         </ul>
     </div>
    '''
    html = etree.HTML(text)
    #选取第一个li的所有祖先节点
    result = html.xpath('//li[1]/ancestor::*')
    print(result) 
    '''
    [<Element html at 0x28c1a0dd140>,
     <Element body at 0x28c1a3e99c0>, 
     <Element div at 0x28c1a3e9a80>, 
     <Element ul at 0x28c1a3e9ac0>]
    '''
    #选取第一个li的div祖先
    result = html.xpath('//li[1]/ancestor::div')
    print(result) #输出 [<Element div at 0x2e75ee89bc0>]
    
    #选取当前节点的所有属性值
    result = html.xpath('//li[1]/attribute::*')
    print(result) #输出 ['item-0', 'one']
    #获取子孙节点中的a节点
    result = html.xpath('//li[1]/child::a//text()')
    print(result) #输出 ['one']
    

    更多轴用法

    摘自【Python 3网络爬虫开发实战 ,崔庆才著 】

    内容有所改动

  • 相关阅读:
    【秒懂音视频开发】03_重识音频
    【秒懂音视频开发】02_重识声音
    【秒懂音视频开发】01_移动通信技术的发展
    [mysql]错误解决之"Failed to start MySQL Server"
    Ubuntu 将Python从默认的2.7升级到3.* 版本
    宝塔面板后台设置免费SSL证书Let's Encrypt(三)
    Let's Encrypt 证书申请及配置(二)
    快速签发 Let's Encrypt 证书指南(一)
    DJANGO中多种重定向方法使用
    反解法|逆向思维
  • 原文地址:https://www.cnblogs.com/lc-snail/p/13232920.html
Copyright © 2020-2023  润新知