XPath与lxml库介绍及爬虫案例_张行之的博客-程序员宅基地

技术标签: 爬虫  xpath  python  lxml  Python  

XPath与lxml库介绍及爬虫案例

XPath介绍

XPath(XML Path Language)是一门在XML文档中查找信息的语言,可用来在XML文档中对元素和属性进行遍历。

XPath的节点

XPath的节点有7种类型:文档节点,元素节点,属性节点,文本节点,命名空间节点,处理指令节点,注释节点。对于我们需要关注的是前面4个节点。下面看xml文档。

<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>
    <book>
        <title lang="en">Harry Potter</title>
        <author>J K. Rowling</author> 
        <year>2005</year>
        <price>29.99</price>
    </book>
</bookstore>


<bookstore> # 文档节点

<book>  # 元素节点,属于<bookstore>的子节点

<title>/<author>/<year>/<price>  # 元素节点,属于<book>节点的子节点


<title lang="en">Harry Potter</title>   
lang    # 属性节点,是<title>节点的属性
Harry Potter  # 文本节点,是<title>节点的文本

XPath选取节点

XPath使用路径表达式在XML文档中选取节点。节点是通过沿着路径或者step来选取的。

路径表达式:

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

image

谓语

谓语用来查找某个特定的节点或者包含某个指定的值节点。

谓语被嵌在方括号[]中。

在下面表格中,列出带有谓语的一些路径表达式,以及结果:

路径表达式 结果
/bookstore/book[1] 选取属于bookstore子元素的第一个book元素
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素
/bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素
//title[@lang] 选取所有拥有名为lang属性的title元素
//title[@lang=’en’] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00

选取未知节点

XPath通配符可用来选取未知的XML元素

通配符 描述
* 匹配任何元素节点
@* 匹配任何属性节点
node() 匹配任何类型的节点

选取若干路径

通过在路径表达式中使用”|”运算符,可以选取若干路径。

路径表达式 描述
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素
//title | // price 选取文档中的所有 title 和 price 元素
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素

XPath运算符

image


lxml类库介绍

lxml类库是一个Html/XML的解析器,主要功能是如何解析和提取HTML/XML数据。

lxml的安装

pip install lxml

lxml的简单使用

etree将文本转成html:

# 将文本转成html对象
html = etree.HTML(text) 

# 将html对象转成html的文本信息
etree.tostring(html)

示例:

from lxml import etree

if __name__ == '__main__':
    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对象
    html = etree.HTML(text)
    # 将对象转成html文本
    result = etree.tostring(html)
    # 打印输出
    print(result.decode('utf-8'))

输出结果:会自动添加,标签,补齐缺少的标签。

<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>

解析html转成文本

新建一个text.html文件,文件内容:

<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>

解析文本输出:

from lxml import etree

if __name__ == '__main__':
    # 解析text.html文件
    html = etree.parse('text.html')
    # 将html对象转成str
    result = etree.tostring(html)
    # 输出
    print(result.decode('utf-8'))

输出结果:

<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>

XPath与lxml联合使用

获取所有\

  • 标签
  • from lxml import etree
    
    
    if __name__ == '__main__':
        # 解析text.html文件,返回一个html对象
        html = etree.parse('text.html')
        print(type(html))  # 输出html对象类型    <class 'lxml.etree._ElementTree'>
        # xpath解析语法解析,获取所有的<li>标签的内容
        result = html.xpath('//li')
        print(type(result))  # 输出结果的类型  <class 'list'>
        for item in result:
            print(type(item))   # 输出每个item对象的类型 <class 'lxml.etree._Element'>
            print(etree.tostring(item).decode('utf-8')) # 输出<li>标签的文本内容 如:<li class="item-0"><a href="link1.html">first item</a></li>

    输出结果:

    image

    获取\

  • 标签的class=’item-1’属性
  • from lxml import etree
    
    
    if __name__ == '__main__':
    
        html = etree.parse('text.html')
        result = html.xpath('//li[@class="item-1"]')
        for item in result:
            print(etree.tostring(item).decode('utf-8'))

    输出结果:

    <li class="item-1"><a href="link2.html">second item</a></li>
    
    <li class="item-1"><a href="link4.html">fourth item</a></li>

    获取\

  • 标签下的href为link1.html的标签
  • if __name__ == '__main__':
        html = etree.parse('text.html')
        result = html.xpath('//li/a[@href="link1.html"]')
        for item in result:
            print(etree.tostring(item).decode('utf-8'))    # 输出 匹配的信息   <a href="link1.html">first item</a>
            print(item.text)    # 输出标签的<a>文本信息   first item
    

    输出结果:

    <a href="link1.html">first item</a>
    first item

    XPath爬虫案例

    #!/usr/bin/env python
    # encoding: utf-8
    
    """
    __author__: Widsom Zhang
    __time__: 2017/11/13 18:32
    """
    import json
    import random
    import urllib.request
    from lxml import etree
    
    
    def download_image(url, headers):
        """
        下载图片
        :param url: 图片的url
        :param headers: http的请求头
        :return:
        """
        # 截取图片的url
        lists = url.split('/')
        # 拼接图片保存的地址路径
        filename = 'image/' + lists[-1]
        # 将请求到的数据写入文件
        with open(filename, 'wb')as f:
            f.write(get_response(url, headers))
    
    
    def write_image_url(url):
        """
        将图片的url写入文件
        :param url:
        :return:
        """
        # 以拼接的方式写入
        with open("image/imageurl.txt", 'a')as f:
            # 每写入一个换行
            f.write(url + "\n")
    
    
    def get_response(url, headers):
        """
        获取响应对象
        :param url: 请求的url
        :param headers: 请求头信息
        :return: 返回服务器的响应信息
        """
        req = urllib.request.Request(url, headers=headers)
        resp = urllib.request.urlopen(req)
        return resp.read()
    
    
    def parse_image(result):
        """
        解析html信息,获取image的url的策略
        :param result: html信息
        :return:
        """
        # 通过etree库将html信息转成对象
        html = etree.HTML(result)
        # 通过xpath解析规则,获取需要的图片url信息
        images = html.xpath('//li[@class="box"]/a/img/@src')
        for image in images:
            print(image)
            # 下载图片
            # download_image(image, headers)    # 下载图片太慢,这里注释了
            # 将图片的url写入文件
            write_image_url(image)
    
    
    if __name__ == '__main__':
        """
            xpath爬虫示例:
    
                爬取的网站是:http://tu.duowan.com/m/bxgif
    
                使用fiddler软件抓包分析:
                    在浏览器中输入上面的url,加载到30条需要的数据,随着滚动条往下拖动,数据再次加载且浏览器的url没有变化
                    初步判断采用的是ajax框架加载数据,通过抓包工具找到加载的url。
    
                ajax加载的url:
                    http://tu.duowan.com/m/bxgif?offset=30&order=created&math=0.2097926790117744
                    url返回的json数据格式:
                    {
                        "html": "...",
                        "more": true,
                        "offset": 60,
                        "enabled": true
                    }
                    http://tu.duowan.com/m/bxgif?offset=60&order=created&math=0.9387482944610999
                    {
                        "html": "...",
                        "more": true,
                        "offset": 90,
                        "enabled": true
                    }
    
                    注:html字段是html中的"<li>..."的html数据,可以使用lxml和xpath解析,具体看代码
    
                通过查看html页面的源码,可以发现,offset是json数据返回的offset,order字段是固定的,math字段是一个(0,1)的随机数。
    
        """
    
        # 需要爬取的url
        url = 'http://tu.duowan.com/m/bxgif'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36'
        }
        # 将请求url的响应信息,通过xpath解析规则解析
        parse_image(get_response(url, headers))
    
        # 每次请求30条数据
        offset = 30
        more = True
        # 循环遍历30次,获取需要的数据(为什么是30,因为该网站数据不多,也就1000多)
        while more:
            # 拼接url
            url2 = 'http://tu.duowan.com/m/bxgif?offset=' + str(offset) + '&order=created&math=' + str(random.random())
            print(url2)
            result2 = get_response(url2, headers)
            # 解析json数据
            dict = json.loads(result2)
            # 获取html的value值
            result = dict['html']
            # offset的值
            offset = dict['offset']
            print(type(offset))
            print(str(offset))
            # 获取more的value值
            more = dict['more']
            # 如果more为true,表示有更多
            if more:
                # 解析image的url
                parse_image(result)
    
    
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_33689414/article/details/78553499

智能推荐

Java 2 实用教程(第5版)耿祥义版 习题六_用下面哪一个选项的代码完善abstractclass不会出现编译错误-程序员宅基地

一、问答题1.接口中能声明变量吗?2.接口中能定义非抽象方法吗?3.什么叫接口的回调?4.接口中的常量可以不指定初值吗?5.可以在接口中只声明常量,不声明抽象方法吗?二、选择题1.下列哪个叙述是正确的A.一个类最多可以实现两个接口。B.如果一个抽象类实现某个接口,那么它必须要重写接口中的全部方法。C.如果一个非抽象类实现某个接口,那么它可以只重写接口中的部分..._用下面哪一个选项的代码完善abstractclass不会出现编译错误

64位win7安装numpy,scipy-程序员宅基地

在win7下安装numpy有2种方法:1.可以通过easy_install 2.直接安装其实这2种方法都是获取numpy资源包的2种途径,我使用easy_install 老是提示超时,所以直接下的资源包

安装hive+mysql,卡在schematool -dbType mysql -initSchema这一步的解决方案_failed to get schema version. underlying cause: co-程序员宅基地

这两天安装hive时,在最后一步,schematool -dbType mysql -initSchema报org.apache.hadoop.hive.metastore.HiveMetaException: Failed to get schema version.Underlying cause: com.mysql.jdbc.exceptions.jdbc4.Communica..._failed to get schema version. underlying cause: com.mysql.jdbc.exceptions.jd

Math 类中一些常用的数学运算_math加法-程序员宅基地

求两个数中的最大值:Math.max()求两个数重点的最小值:Math.min() 计算一个数的绝对值:Math.abs()求 a 的 b 次方:Math.pow(a,b) 注意:返回值是 double四舍五入取整:Math.round()_math加法

Java ServletContext详解_java.servlet.servletcontext-程序员宅基地

Java ServletContext详解什么是ServletContext?根据字面意思即Servlet上下文服务器会为每一个工程创建一个对象,这个对象就是ServletContext对象,这个对象是全局唯一的,工程内部的所有servlet都共享这个对象,所有应用程序共享对象怎么创建ServletContext对象?ServletContext对象的作用与相关方法以上就是ServletContext对象的一些基础知识,如有错误还请各位批评指正,喜欢我的文章可以点赞呀,也可以关注我,我会经常_java.servlet.servletcontext

主动式正弦光栅双目立体视觉原理与程序实现——多步相移、多频外差、视差法_堆动光栅是怎样造成正弦波相位变化和光程差的-程序员宅基地

去年组会汇报的一次PPT,给基础学习的伙伴分享一下一、背景及意义二、原理基础三、程序实现_堆动光栅是怎样造成正弦波相位变化和光程差的

随便推点

矩阵分析一子空间和特征分解_子空间分解-程序员宅基地

线性方程组Ax=b的行视图是超平面,列视图是列向量的线性组合。从这个视角,将矩阵与向量组联系起来了。5.1 线性相关、线性无关定义:给定向量组A:a1,a2,...,ama1,a2,...,ama_1,a_2,...,a_m,如果存在不全为零的数k1,k2,,...,kmk1,k2,,...,kmk_1,k_2,,...,k_m,使得k1a1+k2a2+...+kmam=0k1a1+k..._子空间分解

MOOC清华《程序设计基础》第5章:分书问题-代码一-程序员宅基地

代码一是MOOC版视频中清华大学徐明星老师的算法://问题描述://有编号为0,1,2,3,4的5本书,准备分给5个人A,B,C,D,E,//每个人阅读兴趣用一个二维数组描述,写一个程序,输出所有分书方案,让人人皆大欢喜 #include using namespace std;int Num; //方案数int take[5]; //5本书分别分给谁(用户编号)boo

一种数据增广方案(Data Augmentation)MixMatch算法 另附代码分析_mixmatch代码-程序员宅基地

MixMatch 算法来源于 MixMatch: A Holistic Approach to Semi-Supervised Learning 这篇文章,客观来说这篇文章并不能严格的算作是数据增广,应该是一种半监督的训练方法,即使用少量数据训练模型使模型达到举一反三的目的。但是我认为这仍然可以归为数据增广的范畴,因为数据增广的目的就是防止模型训练过拟合,使之能在更大的数据集上也有好的表现,只不过..._mixmatch代码

JS中判断对象是否含有某个属性的方法_」s判断对弟仲是否含有某个属性-程序员宅基地

您查询的关键词是:判断对象是否有某属性 以下是该网页在北京时间 2016年12月10日 08:29:53 的快照;如果打开速度慢,可以尝试快速版;如果想更新或删除快照,可以投诉快照。百度和网页 http://www.cnblogs.com/kongxianghai/archive/2013/04/12/3015803.html 的作者无关,不对其内容负责。百度快照谨为网络故_」s判断对弟仲是否含有某个属性

@GestureState (SwiftUI 中文文档手册)_swiftui gesturestate-程序员宅基地

@GestureState属性包装程序类型,当用户执行手势时更新属性,并在手势结束时将属性重置回其初始状态。@propertyWrapper @frozen struct GestureState<Value>总览将属性声明为,作为绑定传递给它,作为手势回调的参数,并接收对其的更新。当手势变为非活动状态时,声明为隐式重置的属性使其适合于跟踪瞬态。@GestureStateupdating(_:body:)@GestureState向添加长按手势Circle,并在手势过程中通过将属性声_swiftui gesturestate

瞬变电磁matlab,基于Matlab的矿井瞬变电磁超前探测三维显示技术-程序员宅基地

矿井瞬变电磁法是一类非接触式探测技术,属于时间域电磁法,它利用不接地回线向采掘空间周围的煤岩体中发射一次场,通过在发射间歇测量煤岩体中电性不均匀体感应产生的二次场随时间的变化,来达到查明各种地质目标体的目的[1]。目前,超前探测成果图的显示都只是二维显示,如矿井直流电法常用的为视电阻率切断面图、视电阻率切平面图等二维图形。但是,地下是全空间的,用电磁法探测到的信息是整个立体空间中的介质的综合影响,..._tem 数据三维显示模块