• lxml与xpath


    lxml是一个Python的一个解析库,用于解析HTML和XML,支持Xpaxth解析。由于lxml底层是使用C语言编写的,所以解析效率非常高。

    一.安装lxml

    主要是介绍windows下的安装

    1.使用pip安装

    pip install lxml
    

    如果安装出错,表明缺少依赖库,如libxm12。这时候可以采用wheel方式安装

    2.本地安装

    这里给出个网址
    python包Windows 二进制文件
    在该页面下搜索lxml的whl文件即可。
    下载完成之后,cmd切换运行到下载解压后的目录,执行如下命令

    pip install lxml-4.6.3-cp310-cp310-win_amd64.whl
    

    3。操作XML

    lxml可以读取xml文件,也可以使用字符串形式的xml文档。如果要读取xml文件,需要使用parse函数,该函数需要传入一个XML文件名。如果要解析字符串形式的XML文档,需要使用fromstring函数,该函数的参数就是XML字符串。

    from lxml import etree
    # 读取xml文件
    tree   = etree.parse('products.xml')
    print(type(tree))
    # 将tree重新转化为字符串形式的xml文档,并输出
    print(str(etree.tostring(tree,encoding = "utf-8"),'utf-8'))
    # 获得根节点
    root = tree.getroot()
    print(type(root))
    # 输出根节点的名称
    print('root:',root.tag)
    # 获得根节点的所有子节点
    children = root.getchildren()
    print('--------------输出产品信息--------------')
    # 迭代这些子节点,并输出对应的属性和节点文本
    for child in children:
    
        print('product id = ', child.get('id'))
        print('child[0].name = ',child[0].text)
        print('child[1].price = ', child[1].text)
    
    
    # 分析字符串形式的XML文档
    root   = etree.fromstring('''
    <products> 
        <product1 name="iPhone"/>
        <product2 name="iPad"/>
    </products>
    ''')
    
    print('------------新产品------------')
    # 输出根节点的节点名
    print('root =',root.tag)
    children = root.getchildren()
    # 迭代这些子节点,并输出节点的名称和name属性名
    for child in children:
        print(child.tag,'name = ',child.get('name'))
    

    4.操作HTML文档

    html本质上与xml类似,都是由若干类似<tag></tag>的节点组成的。他们之间的不同之处在于XML除了这些节点,不能再有其他东西,而HTML的语法比较自由,不仅可以有节点,还可以有其他任何文本。
    用lxml库操作html与操作XML类似,同样可以通过getroot方法获得根节点,通过get方法获得节点属性值,通过text属性获取节点内容,通过索引的方式引用了子节点。

    from lxml import etree
    parser = etree.HTMLParser()
    print(type(parser))
    tree   = etree.parse('test.html', parser)
    root = tree.getroot()
    result = etree.tostring(root,encoding='utf-8',
                          pretty_print=True, method="html")
    print(str(result,'utf-8'))
    print(root.tag)
    print('lang =',root.get('lang'))
    print('charset =',root[0][0].get('charset'))
    print('charset =',root[0][1].text)
    

    二.XPath

    XPath的英文名称是XML path Language ,中文是XML路径语言,他是一种在XML文档中查找信息的语言,最初是用于在XML文档中搜索节点的,但同样可以用于HTML文档的搜索,因为XML与HTML是同源的;
    绝大多数HTML分析库都支持Xpath。这里介绍lxml库中使用Xpath过滤掉HTML代码中的节点
    lxml装载HTML代码有如下两种方式
    1.从文件装载,通过parse函数指定的HTML文件名
    2.从代码装载,通过HTML函数指定HTML代码

    2.1提取test.html文件中的标题,以及一段HTML代码中特定a节点的herf属性值和节点文本

    from lxml import etree
    
    parser = etree.HTMLParser()
    tree   = etree.parse('test.html', parser)
    # 使用xpath定位title节点,返回一个节点集合
    titles = tree.xpath('/html/head/title')
    if len(titles) > 0:
        # 输出title节点的文本
        print(titles[0].text)
    # 定义一段html代码
    html = '''
    <div>
        <ul>
            <li class="item1"><a href="https://geekori.com"> geekori.com</a></li>
            <li class="item2"><a href="https://www.jd.com"> 京东商城</a></li>
            <li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
        </ul>
    </div>
    '''
    # 分析HTML代码
    tree = etree.HTML(html)
    # 使用xpath定位class属性值为item2的<li>节点
    aTags = tree.xpath("//li[@class='item2']")
    if len(aTags) > 0:
        # 得到该<li>节点中<a>节点的href属性值和文本
        print(aTags[0][0].get('href'),aTags[0][0].text)
    # https://www.jianshu.com/p/2ae6d51522c3
    

    注意:
    1.通过XPath定位节点返回的是节点集合,即使只有一个节点,返回的也是一个节点集合
    2.使用XPath分析的HTML文档并不一定是标准的,可以么有像<html>、<head>、<body>这些的、节点。任何一段符合HTML语法标准的代码都可以使用XPath进行定位

    2.2选取所有节点

    ‘//’:以两个斜杠(//)开头的XPath规则会选取所有符合要求的节点。表示任意节点,所以//*表示的是选取所有节点
    这里展示一个取出所有的<a>节点,并输出节点的名称

    from lxml import etree
    
    parser = etree.HTMLParser()
    html   = etree.parse('demo.html', parser)
    # 选取demo.html文件中所有的节点
    nodes = html.xpath('//*')
    print('共',len(nodes),'个节点')
    # 输出所有节点的节点名
    print(nodes)
    for i  in range(0,len(nodes)):
        print(nodes[i].tag,end=' ')
    
    # 按层次输出节点,indent是缩进
    def printNodeTree(node, indent):
        print(indent + node.tag)
        indent += "  "
        children = node.getchildren()
        if len(children) > 0:
            for i in range(0,len(children)):
                # 递归调用
                printNodeTree(children[i],indent)
    
    print()
    # 按层次输出节点的节点,nodes[0]是根节点(html节点)
    printNodeTree(nodes[0],"")
    nodes = html.xpath('//a')
    print()
    print('共',len(nodes),'个<a>节点')
    print(nodes)
    # 输出所有<a>节点的文本
    for i  in range(0,len(nodes)):
        print(nodes[i].text,end=' ')
    

    2.3选取子节点

    “/”后是直接子节点。“//”是选取所有该子节点(在该父节点之下的所有)

    from lxml import etree
    
    parser = etree.HTMLParser()
    html   = etree.parse('demo.html', parser)
    nodes = html.xpath('//li/a')
    print('共',len(nodes),'个节点')
    print(nodes)
    for i  in range(0,len(nodes)):
        print(nodes[i].text,end=' ')
    
    print()
    nodes = html.xpath('//ul//a')
    print('共',len(nodes),'个节点')
    print(nodes)
    for i  in range(0,len(nodes)):
        print(nodes[i].text,end=' ')
    
    print()
    nodes = html.xpath('//ul/a')
    print('共',len(nodes),'个节点')
    print(nodes)
    

    2.4选取父节点

    如果知道子节点,想得到父节点,可以使用..parent::*..等效

    from lxml import etree
    # 如果知道子节点,想得到父节点,可以使用"..";“parent::*”与"..“等效
    parser = etree.HTMLParser()
    html   = etree.parse('demo.html', parser)
    # 选取属性值为href="https://www.jd.com"<a>标签的父节点,并输出父节点的属性值
    result = html.xpath('//a[@href="https://www.jd.com"]/../@class')
    print('class属性 =',result)
    # 
    result = html.xpath('//a[@href="https://www.jd.com"]/parent::*/@class')
    print('class属性 =',result)
    

    2.5属性匹配与获取

    引用属性值需要在属性名前面加@,如@class表示class属性,XPath的过滤条件需要放到一对中括号([..])中,就是过滤(提取)出该属性值为什么的标签;如果不将属性值放在[..]中,就是获取属性值,如'//a/@herf'表示获取所有<a>节点的href的属性值

    from lxml import etree
    
    parser = etree.HTMLParser()
    html   = etree.parse('demo.html', parser)
    nodes = html.xpath('//a[@href="https://geekori.com"]')
    print('共',len(nodes),'个节点')
    
    for i  in range(0,len(nodes)):
        print(nodes[i].text)
    
    
    nodes = html.xpath('//a[contains(@href,"www")]')
    print('共',len(nodes),'个节点')
    
    for i  in range(0,len(nodes)):
        print(nodes[i].text)
    
    urls = html.xpath('//a[contains(@href,"www")]/@href')
    
    for i  in range(0,len(urls)):
        print(urls[i])
    

    2.6多属性匹配

    使用and,or关键字和contains函数来匹配多属性,注意XPath对大小写敏感,这里全部小写

    from lxml import etree
    
    parser = etree.HTMLParser()
    html   = etree.parse('demo.html', parser)
    aList = html.xpath('//a[@href="https://www.jd.com" or @href="https://geekori.com"]')
    for a in aList:
        print(a.text,a.get('href'))
    
    #  <li class="item4" value="1234"><a href="https://www.microsoft.com">微软</a></li>
    print('----------------')
    # 选择herf属性值包含www,并且父节点中value属性值等于1234的<a>节点
    aList = html.xpath('//a[contains(@href,"www") and ../@value="1234"]')
    for a in aList:
        print(a.text,a.get('href'))
    

    除了and和or外,Xpath中还有很多运算符

    2.7按序选择节点

    在XPath使用索引的方式与Python中引用列表中元素的方式相似,都是在中括号中使用索引。
    如'//li[1]'表示选择所有<li>节点中的第一个<li>节点。XPath中的索引是从1开始的,而Python列表的索引是从0开始的。
    XPath中的索引还可以使用XPath的内置函数,如position()表示当前位置,list()表示最后的位置。例如'//li[position() = 3]'表示选择所有'

  • '节点中的第3个<li>节点,与'//li[3]'的效果相同

    from lxml import etree
    
    parser = etree.HTMLParser()
    text = '''
    <div>
       <a href="https://geekori.com"> geekori.com</a>
       <a href="https://www.jd.com"> 京东商城</a>
       <a href="https://www.taobao.com">淘宝</a>
       <a href="https://www.microsoft.com">微软</a>
       <a href="https://www.google.com">谷歌</a>
    </div>
    '''
    html   = etree.HTML(text)
    a1 = html.xpath('//a[1]/text()')
    a1 = html.xpath('//a[1]/text()')
    a2 = html.xpath('//a[2]/text()')
    print(a1,a2)
    lasta = html.xpath('//a[last()]/text()')
    print(lasta)
    aList = html.xpath('//a[position() > 3]/text()')
    print(aList)
    aList = html.xpath('//a[position() = 2 or position() = last() - 1]/text()')
    print(aList)
    

    2.8节点轴选择

    XPath提供了许多节点轴选择方法,包括获取祖先节点、兄弟节点、子孙节点等。
    ancestor轴
    attribute轴
    child轴
    descendant轴
    following轴
    following-sibling轴

    from lxml import etree
    
    '''
    ancestor轴
    attribute轴
    child轴
    descendant轴
    following轴
    following-sibling轴
    '''
    parser = etree.HTMLParser()
    text = '''
    <html>
    <head>
        <meta charset="UTF-8">
        <title>XPath演示</title>
    </head>
    <body class="item">
    <div>
        <ul class="item" >
            <li class="item1"><a href="https://geekori.com"> geekori.com</a></li>
            <li class="item2"><a href="https://www.jd.com">京东商城</a>
                                <value url="https://geekori.com"/>
                                <value url="https://www.google.com"/>
            </li>
            <li class="item3"><a href="https://www.taobao.com">淘宝</a>
                              <a href="https://www.tmall.com/">天猫</a></li>
            <li class="item4" value="1234"><a href="https://www.microsoft.com">微软</a></li>
            <li class="item5"><a href="https://www.google.com">谷歌</a></li>
        </ul>
    </div>
    </body>
    </html>
    '''
    html   = etree.HTML(text)
    # ancestor轴,用于获取所有的祖先节点,后面必须跟两个冒号(::),然后是节点选择器
    # 这里的*表示匹配所有的节点
    result = html.xpath('//li[1]/ancestor::*')
    # 输出结果:html body div ul
    for value in result:
        print(value.tag, end= ' ')
    
    print()
    # attribute轴匹配所有class属性值为item的祖先节点
    result = html.xpath('//li[1]/ancestor::*[@class="item"]')
    # 输出结果:body ul
    for value in result:
        print(value.tag, end= ' ')
    
    
    print()
    # 使用attribute轴获取第4个<li>节点的所有属性值
    result = html.xpath('//li[4]/attribute::*')
    # 输出结果:['item4','1234']
    print(result)
    
    # child轴获取第三个<li>节点的所有子节点
    result = html.xpath('//li[3]/child::*')
    for value in result:
        print(value.get('href'), value.text,end= ' ')
    print()
    # descendant轴获取第二个<li>节点的所有名为value的子孙节点
    result = html.xpath('//li[2]/descendant::value')
    for value in result:
        print(value.get('url'),end= ' ')
    print()
    # following轴获取第一个<li>节点后的所有子节点(包括子孙节点)
    result = html.xpath('//li[1]/following::*')
    for value in result:
        print(value.tag,end= ' ')
    print()
    #  following轴获取第一个<li>节点后位置大于4的所有子节点
    result = html.xpath('//li[1]/following::*[position() > 4]')
    for value in result:
        print(value.tag,end= ' ')
    print()
    # following-sibling轴获取第一个<li>节点后所有同级的节点
    result = html.xpath('//li[1]/following-sibling::*')
    for value in result:
        print(value.tag,end= ' ')
    print()
    

    2.9在Chrome中自动获得XPath代码

    在页面右键菜单单击"检查"命令显示开发者工具,然后定位到导航条的某一条连接(如"秒杀"),在Elements选项卡中定位到"秒杀",选择对应的HTML代码,然后右击,在弹出的菜单中单击Copy->Copy XPath命令。
    一般这个获得的Xpath代码需要做些修改,比如,获取多个最后的标签,就要把'/'替换成'//'

    2.10使用Chrome验证XPath

    $x是用来运行XPath的函数
    $x('//*[@id="1"]/h3') 注意:这里如果XPath代码中包含的是双引号,参数要用单引号括起来,如果XPath代码中包含的是单引号,参数要用双引号括起来。

努力拼搏吧,不要害怕,不要去规划,不要迷茫。但你一定要在路上一直的走下去,尽管可能停滞不前,但也要走。
  • 相关阅读:
    触动心灵的话语
    join和os.path.join 的用法
    os.path.join 的用法
    Python中函数的定义必须在调用的前面
    矩阵和数组的区别
    Object detection with deep learning and OpenCV
    YOLO Object Detection with OpenCV
    Python-OpenCV中VideoCapture类的使用
    Object Detection: Face Detection using Haar Cascades
    有关目标检测的文章
  • 原文地址:https://www.cnblogs.com/wkhzwmr/p/15229625.html
  • Copyright © 2020-2023  润新知