美文网首页
数据解析-Xpath

数据解析-Xpath

作者: 逍遥_yjz | 来源:发表于2021-06-10 08:11 被阅读0次

    1. xpath安装及基本语法

    环境的安装:pip install lxml
    
    • 1.xpath的解析原理
      实例化一个etree类型的对象,且将页面源码数据加载到该对象中
      需要调用该对象的xpath方法结合着不同形式的xpath表达式进行标签定位和数据提取

    • 2.etree对象的实例化
      etree.parse(fileNane) #本地的html文件
      etree.HTML(page_text) #声明了一段HTML文本,调用HTML类进行初始化,构造了一个XPath解析对象
      xpath方法返回的永远是一个列表

        实例化报错:爬虫中lxml.etree.XMLSyntaxError问题
      

    解决方法:
    修改代码如下:

    1. 解析本地的HTML源码文件,假设其为:XX.html,根据实际更改。
    parser = etree.HTMLParser(encoding="utf-8")
    tree = etree.parse('XX.html', parser=parser)
    
    2. 解析从互联网获取的HTML源码数据。
    page_text = requests.get(url, headers).text
    parser = etree.HTMLParser(encoding="utf-8")
    tree = etree.HTML(page_text, parser=parser)
    
    • 3.标签定位
      xpath表达式中最最侧的/表示的含义是说,当前定位的标签必须从根节点开始进行定位
      xpath表达式中最左侧的//表示可以从任意位置进行标签定位
      xpath表达式中非最左侧的//表示的是多个层级的意思
      xpath表达式中非最左侧的/表示的是一个层级的意思

    • 4.属性定位
      //标签名[@arrtName='value']
      循环中标签定位: ./表示当前标签
      索引定位://标签名/li[3] #第三个li标签

      1. 提取数据
      • 5.1取文本:
        /text():取直系的文本内容
        //text():取所有的文本内容,循环中不能再用索引,例如文本中有br标签分割
      • 5.2取属性:
        tag/@attrName
    举例:      
    from lxml import etree
    tree = etree.parse('./test.html')
    tree.xpath('/html/head/meta')[0] #绝对路径
    tree.xpath('//meta')[0] #相对路径,将整个页面源码中所有的meta进行定位
    tree.xpath('/html//meta')[0] 
    
    #属性定位
    tree.xpath('//div[@class="song"]')
    
    #索引定位
    tree.xpath('//div[@class="tang"]/ul/li[3]') #该索引是从1开始
    tree.xpath('//div[@class="tang"]//li[3]') #该索引是从1开始
    
    #取文本
    tree.xpath('//p[1]/text()')
    tree.xpath('//div[@class="song"]//text()')
    
    #取属性
    tree.xpath('//a[@id="feng"]/@href')
    

    2. xpath表达式语法

    2.1 使用/表示搜索层级

    起始的/表示根节点

    /放在首位的时候,表示从根节点开始定位。

    后续的/表示层级(bs4的>)
    from lxml import etree
    # 实例化好了一个etree对象,且将被解析的源码加载到了该对象中
    tree = etree.parse('alice.html')
    
    # 第一个斜杠表示从根节点开始遍历的
    # 后续的斜杠表示层级递进
    # 返回值为所有符合表达式规则的属性对象,使用text获取文本内容
    tree.xpath('/html/body/title')[0].text
    
    

    //表示跨越层级(bs4的)

    from lxml import etree
    # 实例化好了一个etree对象,且将被解析的源码加载到了该对象中
    tree = etree.parse('alice.html')
    # 第一个斜杠表示从根节点开始遍历的
    tree.xpath('//title')
    

    ./表示从当前层级往下走

    from lxml import etree
    # 实例化好了一个etree对象,且将被解析的源码加载到了该对象中
    tree = etree.parse('alice.html')
    # 第一个斜杠表示从根节点开始遍历的
    body = tree.xpath('//body')[0]
    
    # 从body往下走
    title = body.xpath('./title')[0]
    print(title.text)
    
    # 以下代码直接报错,因为/是从根节点开始的,不是从body开始的
    title = body.xpath('/title')[0]
    print(title.text)
    
    output >> IndexError: list index out of range
    

    2.2 定位

    定位分为两种,一种是属性定位,一种是索引定位。两种方法结合使用会更加精确。

    2.2.1 属性定位

    语法tag[@attrName="attrValue"]

    from lxml import etree
    
    # 实例化好了一个etree对象,且将被解析的源码加载到了该对象中
    tree = etree.parse('alice.html')
    
    # 使用类名精确定位p标签
    tree.xpath('/html/body/p[@class="story"]')
    
    # 得到符合要求的列表
    output >> [<Element p at 0x1c442ae1ac8>, <Element p at 0x1c442ae1e88>]
    

    2.2.2 索引定位

    索引定位就是在之前的选择器中多加入了一个索引,2.2.2.1的代码修改为下列代码

    from lxml import etree
    
    # 实例化好了一个etree对象,且将被解析的源码加载到了该对象中
    tree = etree.parse('alice.html')
    
    # 使用下标精准索引某一标签
    tree.xpath('/html/body/p[@class="story"][1]')
    
    # 得到符合要求的列表
    output >> <Element p at 0x1c442ae1ac8>
    
    

    注意,这里的索引下标是从1开始的,而不是从0开始

    2.3 输出

    • 在上面的选择器后面加入/text()输出当前标签的文本内容
    • 加入//text()输出所有子标签的内容
    from lxml import etree
    
    # 实例化好了一个etree对象,且将被解析的源码加载到了该对象中
    tree = etree.parse('alice.html')
    
    # 使用text()打印输出
    tree.xpath('/html/body/p[@class="story"]/text()')
    
    # 得到文本内容
    output >> ['Once upon a time there were three little sisters; and their names were\n        ',
     ',\n        ',
     ' and\n        ',
     '; and they lived at the bottom of a well.',
     '...']
    

    可以观察到,所有在a标签中的内容都没有打印出来

    在使用//text()后,就可以打印出a标签的内容了

    from lxml import etree
    
    # 实例化好了一个etree对象,且将被解析的源码加载到了该对象中
    tree = etree.parse('alice.html')
    
    # 使用text()打印输出
    tree.xpath('/html/body/p[@class="story"]//text()')
    
    # 得到全部文本内容
    output >> ['Once upon a time there were three little sisters; and their names were\n        ',
     'Elsie',
     ',\n        ',
     'Lacie',
     ' and\n        ',
     'Tillie',
     '; and they lived at the bottom of a well.',
     '...']
    

    3. demo

    爬取糗事百科的段子内容和作者名称

    # coding:utf-8
    import requests
    
    from lxml import etree
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
    }
    url = 'https://www.qiushibaike.com/text/page/4/'
    page_text = requests.get(url,headers=headers).text
    #print(page_text)
    tree = etree.HTML(page_text)
    div_list = tree.xpath('//div[@class="col1 old-style-col1"]/div')
    print(div_list)
    for div in div_list:
        author = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()')[0]
        # 糗事百科中有作者和段子内容,作者分为实名用户和匿名用户,但通过对糗事百科的源码,当是匿名用户的时候,文本内容就取不到,所以返回None,但是爬取到的内容也就是None,不是想要的结果,解决:  ./div[1]/a[2]/h2/text()取实名用户,./div[1]/span[2]/h2/text()取匿名用户
        content = div.xpath('.//div[@class="content"]/span//text()')
        content = ''.join(content)
        print(author,content)
    

    输出:

    撞死一只羊
     
    
    
    上周公司去日料店聚餐,没想到平时有女神光环的一位女生,一脱下鞋的那股酸臭味,把在场的人都纷纷「震撼」住了。臭就算了,居然还没穿袜子!可想而知那双脚的攻击性有多强!当时我的心情是已经不想干饭了,想赶紧跑。而女神自己也尴尬到跑去找卫生间洗脚。不然难以想象一顿有味道的午餐怎么吃得下去。过后我悄悄问女神,为啥那天没穿袜子,她说实在是找不到合适的袜子搭配那双鞋!
    
    
    
    邱子是漂亮的
     
    
    
    今天我两个同事吵架了,不管我怎么劝,他们就是不肯打架
    
    
    
    骑着二哈啃黄瓜
     
    
    
    闺蜜喝酒不爱去第二场,就是k t v,,,,好死不死,又被我拉去了,三杯酒下肚,就嗨起来了,比谁都会玩,中途她有电话,包厢太吵,她就出去接,一出门就被服务员告诫了:姑凉,我们这里不允许窜野台哦。。。
    
    

    爬取糗事百科笑话的标题和内容

    http://www.lovehhy.net/Joke/Detail

    from lxml import etree
    import requests
    url = 'http://www.lovehhy.net/Joke/Detail/QSBK/'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
        'Cookie':'bdshare_firstime=1573442373487; ASP.NET_SessionId=0yoewt3nnhet3apass0u4hj5; ASPSESSIONIDQADBDDCA=AAKLABFACJHJNHHCMCKEJGJB; __51cke__=; Hm_lvt_03da7ad267ef3d61ce133d6c12f67140=1573442375,1573478536; ASPSESSIONIDSACCBCCA=BCOLDPEALOKMHFJJMHODNHGB; Hm_lpvt_lovehhy=1573479577; Hm_lvt_lovehhy=1573479577; Hm_lpvt_03da7ad267ef3d61ce133d6c12f67140=1573479707; __tins__2343955=%7B%22sid%22%3A%201573478536404%2C%20%22vd%22%3A%2011%2C%20%22expires%22%3A%201573481507039%7D; __51laig__=11'
    }
    joke_text = requests.get(url=url,headers=headers).text
    #print(joke_text)
    tree = etree.HTML(joke_text)
    url_text = tree.xpath('//*[@class="post_recommend_new"]/h3/a/@href')
    #url_text
    for i in url_text:
        qiu_url =  i
        print(i)
        content_text = requests.get(url=qiu_url,headers=headers).text
        tree = etree.HTML(content_text)
        title = tree.xpath('//*[@id="read"]/h1/text()')[0]
        content = tree.xpath('//*[@id="fontzoom"]/text()')[0]
        print(title,content)
    

    输出:

    http://www.lovehhy.net/Yulu/View/170956
    
                    图文语录 - 生活中难免有不如意的时候 
                      “   生活中难免有不如意的时候   路还很长 你不要绝望   打起精神来重新出发   温暖的事以后一定会发生的                                                      ” ?
    http://www.lovehhy.net/Yulu/View/170955
    
                    图文语录 - 但我希望对方可以肯定我 
                      “ 我喜欢能带给我正面影响的人,正面的影响不代表对方有多棒,但我希望对方可以肯定我,看得到我身上发光的地方,我也愿意回馈对方同等分量的重视。”
    
    

    https://blog.csdn.net/weixin_42763696/article/details/108712503
    https://www.cnblogs.com/tangjian219/p/11978047.html

    相关文章

      网友评论

          本文标题:数据解析-Xpath

          本文链接:https://www.haomeiwen.com/subject/gzqyeltx.html