• Python爬虫〇七———数据解析之XPATH的使用


    今天来总结最后一种说句解析的方式——XPath。 

    XPath是一门在XML文档中查找信息的语言,用于在XML文档中通过元素和属性进行导航。

    XPath简介

     下面来简单介绍一下XPath。

    XPath的特点

    1. XPath使用路径表达式在XML文档中进行导航
    2. XPath包含一个标准函数库
    3. XPath是XSLT(Extensible Stylesheet Language Transformation)中的主要元素
    4. XPath是一个W3C标准

    XPath解析方法

    1. 实例化一个etree对象,在实例化的时候讲被解析的页面源码数据加载到该对象中
    2. 调用etree的xpath方法结合xpath表达式实现标签的定位和数据的获取

    环境的配置

    XPath需要lxml库,lxml库的安装我们在前面讲bs4的时候已经说过了,可以通过pip直接安装。

    pip3 install lxml

    etree对象的实例化

    和bs4一样,etree对象的实例化分两种情况

    加载本地的html文件

    加载本地html文件有两种方式,比方有个test.html文件

    ####################方法1####################
    from lxml import etree
    with open('./test.html','r') as f:
        data = f.read()
        tree = etree.HTML(data)
    
    ####################方法2####################
    from lxml import etree
    tree = etree.parse('./test.html',etree.HTMLParser())

    一个是吧html代码读取以后用HTML方法处理,还有一种方法是直接指定路径,但是这种方法一定要指定一个解释器。

    加载互联网上爬取的源码数据

    加载网络爬取的数据的方法和上面的第一种方法一样,就是把拿到的字符串加载到HTML方法。

     XPath表达式

    在拿到tree对象以后,我们要通过XPath表达式来定位到所需要的标签以及里面的内容 。假设我们本地存有一个test.html文件,内容和上一章的一样

     <html>
      <head>
       <title>
        The Dormouse's story
       </title>
      </head>
      <body>
       <p class="title">
        <b>
         The Dormouse's story
        </b>
       </p>
       <p class="story">
        Once upon a time there were three little sisters; and their names were
        <a class="sister" href="http://example.com/elsie" id="link1">
         Elsie
        </a>
        ,
        <a class="sister" href="http://example.com/lacie" id="link2">
         Lacie
        </a>
        and
        <a class="sister" href="http://example.com/tillie" id="link2">
         Tillie
        </a>
        ; and they lived at the bottom of a well.
       </p>
       <p class="story">
        ...
       </p>
      </body>
     </html>
    test.html

    然后实例化一个tree对象,并且把带解析的源码加载进去

    from lxml import etree
    tree = etree.parse('./test.html',etree.HTMLParser())

    下面我们就用这个tree对象来讲xpath到用法

    XPath术语

    想要了解XPath的用法,我们要先了解XPath的基本术语

    节点(Node)

    在XPath中有其中类型的节点:元素,属性,文本,命名空间,处理指令以及文档节点,整个文档是被作为节点树来对待的,树的根被称为文档节点或根节点。

    基本值(Atomic Value)

    又称原子值,无父或子节点

    节点关系

    节点关系分为父,子同胞,先辈,后代。顾名思义就是各个节点之间的关系,要注意的是先辈是包含父类关系的,而后代也包含子级关系的。

    xpath节点选取

    首先要了解最基础的xpath表达式

    表达式 描述
    nodename 选取此节点的所有子节点
    / 从根节点选取
    //

    放在开始从匹配到当前节点选择文档中的节点,而不考虑其位置

    放在两个标签内表示间隔多个标签层级

    . 选取当前节点
    .. 选取当前节点的父节点
    @ 选取属性

    下面可以针对上面的表达式,结合test.html实例化的tree对象代码来演示一下

    依靠节点

    a_tags = tree.xpath('/html/body/p/a')
    print(a_tags)
    ##########输出##########
    [<Element a at 0x7fdb8f189e00>, <Element a at 0x7fdb8f226b00>, <Element a at 0x7fdb8f10f8c0>]

    上面的代码就是搜索html——>body——>p——>a标签,返回值是一个列表,里面放到都是<class 'lxml.etree._Element'>对象

    依靠属性定位

    print(tree.xpath('//a[@class="sister"]'))
    ##########输出##########
    [<Element a at 0x7fdb8f36cfc0>, <Element a at 0x7fdb8f11d4c0>, <Element a at 0x7fdb8f099380>]

    上面的代码就实现了通过指定class的值搜索到对应的a标签。

    谓语

    我们还可以通过谓语(Predicates)来超找某个特定的节点或者包含某个指定值得节点

    在下面的表格中,列出来一些带有谓语的表达式和其对应的结果

    表达式 结果
    /html/body/p/a[1] 选取a元素的第一个元素
    /html/body/p/a[last()] 获取最后一个a元素(注意带括号)
    /html/body/p/a[last()-1] 获取倒数第二个a元素(注意-1的位置)
    /html/body/p/a[position()<3] 获取最前面两个属于p元素的子元素a
    //a[@class] 获取所有有class属性的a元素

    //a[@class='sister]

    获取所有class属性值为sister的a元素
    /html/body/div[p>20] 获取html->body->div下值大于20的p标签(该用法常用于xml中)
    /html/body/div[p>20]/a 获取html->body->div下单a标签,且其中p标签内的值应大于20

    这里有个要注意点地方,就是在获取第n个元素的时候,在表达式和列表里切片的n的值出来的是不一样的

    st = """
    <html>
    <body>
    <ul>
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    </ul>
    <body>
    </html>
    """
    
    tree = etree.HTML(st,etree.HTMLParser())
    t1 = tree.xpath('/html/body/ul/li[1]/text()')
    print('t1',t1)
    t2 = tree.xpath('/html/body/ul/li/text()')
    print('t2',t2[1])
    ##########输出##########
    t1 ['0']
    t2 1

    通配符

    在匹配到时候还可以使用通配符来匹配任意一个节点

    通配符 效果 案例 效果
    * 匹配任何元素节点 /html/body/*/li 匹配body标签下任意标签下单li标签
    @* 匹配任何属性节点 //p[@*] 匹配具有任意属性的p标签
    node() 匹配任何类型的节点 //* 选取文档中所有元素

    多个路径的选取

    在xpath表达式中我们可以通过管道符来选取多个路径,这里不在放案例了。

    文本获取

    在索引到需要的标签后我们就要获取标签里的文本。文本的获取有两个方法

    • /text()
    • //text()

    注意,一个是单斜杠,另一个是双斜杠,效果如下

    s="""
    <div class='test'>div标签内<p>p标签内</p></div>
    """
    
    tree = etree.HTML(s,etree.HTMLParser())
    print('//',tree.xpath('//div//text()'))
    print('/',tree.xpath('//div/text()'))
    ##########输出##########
    // ['div标签内', 'p标签内']
    / ['div标签内']

    单斜杠是获取本标签直系的文本内容

    双斜杠包含了本标签及后代标签内的文本内容。

    案例

     下面结合一个案例来试一下XPath的使用

    需求,从站长之家爬取免费的简历模板

    url:https://sc.chinaz.com/jianli/free.html,要爬取5页的数据内容

    直接放代码吧,看看怎么讲一下

     1 import requests
     2 from lxml import etree
     3 
     4 for page_index in range(1,6):     #爬取1-5页内容,指定url
     5     if page_index ==1:            #第一页的url和后面的规律不同
     6         url = 'https://sc.chinaz.com/jianli/free.html'
     7     else:
     8         url = 'https://sc.chinaz.com/jianli/free_{}.html'.format(page_index)
     9     
    10     page = requests.get(url=url)
    11     page.encoding='utf-8'
    12     page_text = page.text
    13     
    14 
    15     tree = etree.HTML(page_text,etree.HTMLParser())
    16 
    17     tag = tree.xpath('//div[@class="box col3 ws_block masonry-brick"]')
    18 
    19     tags = tree.xpath('//div[@id="main"]//div[contains(@class,"box") and contains(@class,"ws_block")]//p/a')
    20     for tag in tags:
    21         model_url = 'http:'+tag.xpath('./@href')[0]
    22         model_name = tag.xpath('./text()')[0]
    23         print(model_url)
    24         
    25         model_page_text = requests.get(url=model_url).text
    26         model_tree = etree.HTML(model_page_text,etree.HTMLParser())
    27         
    28         model_link = model_tree.xpath('//ul[@class="clearfix"]//a/@href')[0]
    29         
    30     
    31         print(model_link)
    32         file_name = model_name+'.rar'
    33         
    34         with open('./简历模板/'+file_name,'wb') as f:
    35             data = requests.get(url=model_link).content
    36             f.write(data)
    37         print(file_name,'finished!')
    38         
    39     print('----------page{}finish!----------')

    就是分了三个层次

    第一层先爬取主页内容,在主页上有若干模板的子链接(页面2),第二层爬取页面2的内容,获取到待下载文件的名称以及链接地址;第三层直接爬取文件(二进制的压缩包),爬取后永久化存储至本地。

    这里有个问题,运行一段时间后可能会报一个错误:Httpconnectionpool,是因为短时间发起来高频的请求导致ip被禁,或者http连接处中的连接资源被耗尽。针对第一个我们在下一章会讲到请求代理操作

    第二个可以在headers里加一个新的键值对:Conection:"close"

  • 相关阅读:
    spring boot基础知识
    使用PrintDocument定制打印格式
    vue刷新页面出现闪烁
    递归限制级数
    导出Excel的2个方法
    通过反射获取对象名称和值
    asp.net mvc 使用bootstrap的模态框插件modal
    记录用到的mssql的几个方法
    记录几个字符串转html的帮助类,以防忘记
    git 命令
  • 原文地址:https://www.cnblogs.com/yinsedeyinse/p/14427785.html
Copyright © 2020-2023  润新知