zoukankan      html  css  js  c++  java
  • python解析库lxml的简单使用

     lxml是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高

    XPath,全称XML Path Language,即XML路径语言,它是一门在XML文档中查找信息的语言,它最初是用来搜寻XML文档的,但是它同样适用于HTML文档的搜索

    XPath的选择功能十分强大,它提供了非常简明的路径选择表达式,另外,它还提供了超过100个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等,几乎所有我们想要定位的节点,都可以用XPath来选择

    XPath于1999年11月16日成为W3C标准,它被设计为供XSLT、XPointer以及其他XML解析软件使用,更多的文档可以访问其官方网站:https://www.w3.org/TR/xpath/

    1、python库lxml的安装

    windows系统下的安装:

    复制代码
    #pip安装
    pip3 install lxml
    
    #wheel安装
    #下载对应系统版本的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml
    pip3 install lxml-4.2.1-cp36-cp36m-win_amd64.whl
    复制代码

    linux下安装:

    yum install -y epel-release libxslt-devel libxml2-devel openssl-devel
    
    pip3 install lxml

    验证安装:

    $python3
    >>>import lxml

    2、XPath常用规则

    表达式 描述
    nodename 选取此节点的所有子节点
    / 从当前节点选取直接子节点
    // 从当前节点选取子孙节点
    . 选取当前节点
    .. 选取当前节点的父节点
    @ 选取属性
    * 通配符,选择所有元素节点与元素名
    @* 选取所有属性
    [@attrib] 选取具有给定属性的所有元素
    [@attrib='value'] 选取给定属性具有给定值的所有元素
    [tag] 选取所有具有指定元素的直接子节点
    [tag='text'] 选取所有具有指定元素并且文本内容是text节点

    (1)读取文本解析节点

    复制代码
    from lxml import etree
    
    text='''
    <div>
        <ul>
             <li class="item-0"><a href="link1.html">第一个</a></li>
             <li class="item-1"><a href="link2.html">second item</a></li>
             <li class="item-0"><a href="link5.html">a属性</a>
         </ul>
     </div>
    '''
    html=etree.HTML(text) #初始化生成一个XPath解析对象
    result=etree.tostring(html,encoding='utf-8')   #解析对象输出代码
    print(type(html))
    print(type(result))
    print(result.decode('utf-8'))
    
    #etree会修复HTML文本节点
    <class 'lxml.etree._Element'>
    <class 'bytes'>
    <html><body><div>
        <ul>
             <li class="item-0"><a href="link1.html">第一个</a></li>
             <li class="item-1"><a href="link2.html">second item</a></li>
             <li class="item-0"><a href="link5.html">a属性</a>
         </li></ul>
     </div>
    </body></html>
    复制代码

    (2)读取HTML文件进行解析

    复制代码
    from lxml import etree
    
    html=etree.parse('test.html',etree.HTMLParser()) #指定解析器HTMLParser会根据文件修复HTML文件中缺失的如声明信息
    result=etree.tostring(html)   #解析成字节
    #result=etree.tostringlist(html) #解析成列表
    print(type(html))
    print(type(result))
    print(result)
    
    #
    <class 'lxml.etree._ElementTree'>
    <class 'bytes'>
    b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
    <html><body><div>&#13;
        <ul>&#13;
             <li class="item-0"><a href="link1.html">first item</a></li>&#13;
             <li class="item-1"><a href="link2.html">second item</a></li>&#13;
             <li class="item-inactive"><a href="link3.html">third item</a></li>&#13;
             <li class="item-1"><a href="link4.html">fourth item</a></li>&#13;
             <li class="item-0"><a href="link5.html">fifth item</a>&#13;
         </li></ul>&#13;
     </div>&#13;
    </body></html>'
    复制代码

    (3)获取所有节点

    返回一个列表每个元素都是Element类型,所有节点都包含在其中

    复制代码
    from lxml import etree
    
    html=etree.parse('test',etree.HTMLParser())
    result=html.xpath('//*')  #//代表获取子孙节点,*代表获取所有
    
    print(type(html))
    print(type(result))
    print(result)
    
    #
    <class 'lxml.etree._ElementTree'>
    <class 'list'>
    [<Element html at 0x754b210048>, <Element body at 0x754b210108>, <Element div at 0x754b210148>, <Element ul at 0x754b210188>, <Element li at 0x754b2101c8>, <Element a at 0x754b210248>, <Element li at 0x754b210288>, <Element a at 0x754b2102c8>, <Element li at 0x754b210308>, <Element a at 0x754b210208>, <Element li at 0x754b210348>, <Element a at 0x754b210388>, <Element li at 0x754b2103c8>, <Element a at 0x754b210408>]
    复制代码

    如要获取li节点,可以使用//后面加上节点名称,然后调用xpath()方法

    html.xpath('//li')   #获取所有子孙节点的li节点

    (4)获取子节点

    通过/或者//即可查找元素的子节点或者子孙节点,如果想选择li节点的所有直接a节点,可以这样使用

    result=html.xpath('//li/a')  #通过追加/a选择所有li节点的所有直接a节点,因为//li用于选中所有li节点,/a用于选中li节点的所有直接子节点a

    (5)获取父节点

    我们知道通过连续的/或者//可以查找子节点或子孙节点,那么要查找父节点可以使用..来实现也可以使用parent::来获取父节点

    复制代码
    from lxml import etree
    from lxml.etree import HTMLParser
    text='''
    <div>
        <ul>
             <li class="item-0"><a href="link1.html">第一个</a></li>
             <li class="item-1"><a href="link2.html">second item</a></li>
         </ul>
     </div>
    '''
    
    html=etree.HTML(text,etree.HTMLParser())
    result=html.xpath('//a[@href="link2.html"]/../@class')
    result1=html.xpath('//a[@href="link2.html"]/parent::*/@class')
    print(result)
    print(result1)
    
    
    #
    ['item-1']
    ['item-1']
    复制代码

    (6)属性匹配

    在选取的时候,我们还可以用@符号进行属性过滤。比如,这里如果要选取classitem-1li节点,可以这样实现:

    复制代码
    from lxml import etree
    from lxml.etree import HTMLParser
    text='''
    <div>
        <ul>
             <li class="item-0"><a href="link1.html">第一个</a></li>
             <li class="item-1"><a href="link2.html">second item</a></li>
         </ul>
     </div>
    '''
    
    html=etree.HTML(text,etree.HTMLParser())
    result=html.xpath('//li[@class="item-1"]')
    print(result)
    复制代码

    (7)文本获取

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

    复制代码
    from lxml import etree
    
    text='''
    <div>
        <ul>
             <li class="item-0"><a href="link1.html">第一个</a></li>
             <li class="item-1"><a href="link2.html">second item</a></li>
         </ul>
     </div>
    '''
    
    html=etree.HTML(text,etree.HTMLParser())
    result=html.xpath('//li[@class="item-1"]/a/text()') #获取a节点下的内容
    result1=html.xpath('//li[@class="item-1"]//text()') #获取li下所有子孙节点的内容
    
    print(result)
    print(result1)
    复制代码

    (8)属性获取

    使用@符号即可获取节点的属性,如下:获取所有li节点下所有a节点的href属性

    result=html.xpath('//li/a/@href')  #获取a的href属性
    result=html.xpath('//li//@href')   #获取所有li子孙节点的href属性

    (9)属性多值匹配

    如果某个属性的值有多个时,我们可以使用contains()函数来获取

    复制代码
    from lxml import etree
    
    text1='''
    <div>
        <ul>
             <li class="aaa item-0"><a href="link1.html">第一个</a></li>
             <li class="bbb item-1"><a href="link2.html">second item</a></li>
         </ul>
     </div>
    '''
    
    html=etree.HTML(text1,etree.HTMLParser())
    result=html.xpath('//li[@class="aaa"]/a/text()')
    result1=html.xpath('//li[contains(@class,"aaa")]/a/text()')
    
    print(result)
    print(result1)
    
    #通过第一种方法没有取到值,通过contains()就能精确匹配到节点了
    []
    ['第一个']
    复制代码

    (10)多属性匹配

    另外我们还可能遇到一种情况,那就是根据多个属性确定一个节点,这时就需要同时匹配多个属性,此时可用运用and运算符来连接使用:

    复制代码
    from lxml import etree
    
    text1='''
    <div>
        <ul>
             <li class="aaa" name="item"><a href="link1.html">第一个</a></li>
             <li class="aaa" name="fore"><a href="link2.html">second item</a></li>
         </ul>
     </div>
    '''
    
    html=etree.HTML(text1,etree.HTMLParser())
    result=html.xpath('//li[@class="aaa" and @name="fore"]/a/text()')
    result1=html.xpath('//li[contains(@class,"aaa") and @name="fore"]/a/text()')
    
    
    print(result)
    print(result1)
    
    
    #
    ['second item']
    ['second item']
    复制代码

    (11)XPath中的运算符

    运算符 描述 实例 返回值

    or

    age=19 or age=20 如果age等于19或者等于20则返回true反正返回false
    and age>19 and age<21 如果age等于20则返回true,否则返回false
    mod 取余 5 mod 2 1
    | 取两个节点的集合 //book | //cd 返回所有拥有book和cd元素的节点集合
    + 6+4 10
    - 6-4 2
    * 6*4 24
    div 除法 8 div 4 2
    = 等于 age=19 true
    != 不等于 age!=19 true
    < 小于 age<19 true
    <= 小于或等于 age<=19 true
    > 大于 age>19 true
    >=  大于或等于 age>=19 true

    此表参考来源:http://www.w3school.com.cn/xpath/xpath_operators.asp

    (12)按序选择

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

    复制代码
    from lxml import etree
    
    text1='''
    <div>
        <ul>
             <li class="aaa" name="item"><a href="link1.html">第一个</a></li>
             <li class="aaa" name="item"><a href="link1.html">第二个</a></li>
             <li class="aaa" name="item"><a href="link1.html">第三个</a></li>
             <li class="aaa" name="item"><a href="link1.html">第四个</a></li> 
         </ul>
     </div>
    '''
    
    html=etree.HTML(text1,etree.HTMLParser())
    
    result=html.xpath('//li[contains(@class,"aaa")]/a/text()') #获取所有li节点下a节点的内容
    result1=html.xpath('//li[1][contains(@class,"aaa")]/a/text()') #获取第一个
    result2=html.xpath('//li[last()][contains(@class,"aaa")]/a/text()') #获取最后一个
    result3=html.xpath('//li[position()>2 and position()<4][contains(@class,"aaa")]/a/text()') #获取第一个
    result4=html.xpath('//li[last()-2][contains(@class,"aaa")]/a/text()') #获取倒数第三个
    
    
    print(result)
    print(result1)
    print(result2)
    print(result3)
    print(result4)
    
    
    #
    ['第一个', '第二个', '第三个', '第四个']
    ['第一个']
    ['第四个']
    ['第三个']
    ['第二个']
    复制代码

    这里使用了last()、position()函数,在XPath中,提供了100多个函数,包括存取、数值、字符串、逻辑、节点、序列等处理功能,它们的具体作用可参考:http://www.w3school.com.cn/xpath/xpath_functions.asp

    (13)节点轴选择

    XPath提供了很多节点选择方法,包括获取子元素、兄弟元素、父元素、祖先元素等,示例如下:

    复制代码
    from lxml import etree
    
    text1='''
    <div>
        <ul>
             <li class="aaa" name="item"><a href="link1.html">第一个</a></li>
             <li class="aaa" name="item"><a href="link1.html">第二个</a></li>
             <li class="aaa" name="item"><a href="link1.html">第三个</a></li>
             <li class="aaa" name="item"><a href="link1.html">第四个</a></li> 
         </ul>
     </div>
    '''
    
    html=etree.HTML(text1,etree.HTMLParser())
    result=html.xpath('//li[1]/ancestor::*')  #获取所有祖先节点
    result1=html.xpath('//li[1]/ancestor::div')  #获取div祖先节点
    result2=html.xpath('//li[1]/attribute::*')  #获取所有属性值
    result3=html.xpath('//li[1]/child::*')  #获取所有直接子节点
    result4=html.xpath('//li[1]/descendant::a')  #获取所有子孙节点的a节点
    result5=html.xpath('//li[1]/following::*')  #获取当前子节之后的所有节点
    result6=html.xpath('//li[1]/following-sibling::*')  #获取当前节点的所有同级节点
    
    
    #
    [<Element html at 0x3ca6b960c8>, <Element body at 0x3ca6b96088>, <Element div at 0x3ca6b96188>, <Element ul at 0x3ca6b961c8>]
    [<Element div at 0x3ca6b96188>]
    ['aaa', 'item']
    [<Element a at 0x3ca6b96248>]
    [<Element a at 0x3ca6b96248>]
    [<Element li at 0x3ca6b96308>, <Element a at 0x3ca6b96348>, <Element li at 0x3ca6b96388>, <Element a at 0x3ca6b963c8>, <Element li at 0x3ca6b96408>, <Element a at 0x3ca6b96488>]
    [<Element li at 0x3ca6b96308>, <Element li at 0x3ca6b96388>, <Element li at 0x3ca6b96408>]
    复制代码

    以上使用的是XPath轴的用法,更多轴的用法可参考:http://www.w3school.com.cn/xpath/xpath_axes.asp

    (14)案例应用:抓取TIOBE指数前20名排行开发语言

    复制代码
    #!/usr/bin/env python
    #coding:utf-8
    import requests
    from requests.exceptions import RequestException
    from lxml import etree
    from lxml.etree import ParseError
    import json
    
    def one_to_page(html):
        headers={
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36'
        }
        try:
            response=requests.get(html,headers=headers)
            body=response.text  #获取网页内容
        except RequestException as e:
            print('request is error!',e)
        try:
            html=etree.HTML(body,etree.HTMLParser())  #解析HTML文本内容
            result=html.xpath('//table[contains(@class,"table-top20")]/tbody/tr//text()') #获取列表数据
            pos = 0
            for i in range(20):
                if i == 0:
                    yield result[i:5]
                else:
                    yield result[pos:pos+5]  #返回排名生成器数据
                pos+=5
        except ParseError as e:
             print(e.position)
    
    
    def write_file(data):   #将数据重新组合成字典写入文件并输出
        for i in data:
            sul={
                '2018年6月排行':i[0],
                '2017年6排行':i[1],
                '开发语言':i[2],
                '评级':i[3],
                '变化率':i[4]
            }
            with open('test.txt','a',encoding='utf-8') as f:
                f.write(json.dumps(sul,ensure_ascii=False) + '
    ') #必须格式化数据
                f.close()
            print(sul)
        return None
    
    
    def main():
        url='https://www.tiobe.com/tiobe-index/'
        data=one_to_page(url)
        revaule=write_file(data)
        if revaule == None:
            print('ok')
            
     
     
            
    if __name__ == '__main__':
        main()
    
    
    
    #
    {'2018年6月排行': '1', '2017年6排行': '1', '开发语言': 'Java', '评级': '15.368%', '变化率': '+0.88%'}
    {'2018年6月排行': '2', '2017年6排行': '2', '开发语言': 'C', '评级': '14.936%', '变化率': '+8.09%'}
    {'2018年6月排行': '3', '2017年6排行': '3', '开发语言': 'C++', '评级': '8.337%', '变化率': '+2.61%'}
    {'2018年6月排行': '4', '2017年6排行': '4', '开发语言': 'Python', '评级': '5.761%', '变化率': '+1.43%'}
    {'2018年6月排行': '5', '2017年6排行': '5', '开发语言': 'C#', '评级': '4.314%', '变化率': '+0.78%'}
    {'2018年6月排行': '6', '2017年6排行': '6', '开发语言': 'Visual Basic .NET', '评级': '3.762%', '变化率': '+0.65%'}
    {'2018年6月排行': '7', '2017年6排行': '8', '开发语言': 'PHP', '评级': '2.881%', '变化率': '+0.11%'}
    {'2018年6月排行': '8', '2017年6排行': '7', '开发语言': 'JavaScript', '评级': '2.495%', '变化率': '-0.53%'}
    {'2018年6月排行': '9', '2017年6排行': '-', '开发语言': 'SQL', '评级': '2.339%', '变化率': '+2.34%'}
    {'2018年6月排行': '10', '2017年6排行': '14', '开发语言': 'R', '评级': '1.452%', '变化率': '-0.70%'}
    {'2018年6月排行': '11', '2017年6排行': '11', '开发语言': 'Ruby', '评级': '1.253%', '变化率': '-0.97%'}
    {'2018年6月排行': '12', '2017年6排行': '18', '开发语言': 'Objective-C', '评级': '1.181%', '变化率': '-0.78%'}
    {'2018年6月排行': '13', '2017年6排行': '16', '开发语言': 'Visual Basic', '评级': '1.154%', '变化率': '-0.86%'}
    {'2018年6月排行': '14', '2017年6排行': '9', '开发语言': 'Perl', '评级': '1.147%', '变化率': '-1.16%'}
    {'2018年6月排行': '15', '2017年6排行': '12', '开发语言': 'Swift', '评级': '1.145%', '变化率': '-1.06%'}
    {'2018年6月排行': '16', '2017年6排行': '10', '开发语言': 'Assembly language', '评级': '0.915%', '变化率': '-1.34%'}
    {'2018年6月排行': '17', '2017年6排行': '17', '开发语言': 'MATLAB', '评级': '0.894%', '变化率': '-1.10%'}
    {'2018年6月排行': '18', '2017年6排行': '15', '开发语言': 'Go', '评级': '0.879%', '变化率': '-1.17%'}
    {'2018年6月排行': '19', '2017年6排行': '13', '开发语言': 'Delphi/Object Pascal', '评级': '0.875%', '变化率': '-1.28%'}
    {'2018年6月排行': '20', '2017年6排行': '20', '开发语言': 'PL/SQL', '评级': '0.848%', '变化率': '-0.72%'}
    复制代码

    XPath的更多用法参考:http://www.w3school.com.cn/xpath/index.asp

    python lxml库的更多用法参考:http://lxml.de/

    出处:https://www.cnblogs.com/zhangxinqi/p/9210211.html

    ==========================================================================================

    Python-- lxml用法

    目录

    lxml库(lxml安装可查看上一篇文章)

    Element类

    1、节点操作

    2、属性操作

    3、文本操作

    4、文件解析与输出

    5、ElementPath

    6、案例(尤其最后的一篇代码) 


    lxml库(lxml安装可查看上一篇文章)

    • python的HTML/XML的解析器
    • 官方文档:   http://lxml.de/index.html
    • 功能:
      • 解析HTML
      • 文件读取
      • etree和XPath的配合使用

    围绕三个问题:

    问题1:有一个XML文件,如何解析
    问题2:解析后,如果查找、定位某个标签
    问题3:定位后如何操作标签,比如访问属性、文本内容等

    导入模块,该库常用的XML处理功能都在lxml.etree中

    from lxml import etree  

    Element类

    Element是XML处理的核心类,Element对象可以直观的理解为XML的节点,大部分XML节点的处理都是围绕该类进行的。这部分包括三个内容:节点的操作、节点属性的操作、节点内文本的操作。

    1、节点操作

    1、创建Element对象
    使用Element方法,参数即节点名称。

    1.  
      >>> root = etree.Element('root')
    2.  
      >>> print(root)
    3.  
      <Element root at 0x2da0708>

    2、获取节点名称
    使用tag属性,获取节点的名称。

    1.  
      >>> print(root.tag)
    2.  
      root

    3、输出XML内容
    使用tostring方法输出XML内容,参数为Element对象。

    1.  
      >>> print(etree.tostring(root))
    2.  
      b'<root><child1/><child2/><child3/></root>'

    4、添加子节点
    使用SubElement方法创建子节点,第一个参数为父节点(Element对象),第二个参数为子节点名称。

    1.  
      >>> child1 = etree.SubElement(root, 'child1')
    2.  
      >>> child2 = etree.SubElement(root, 'child2')
    3.  
      >>> child3 = etree.SubElement(root, 'child3')

    5、删除子节点
    使用remove方法删除指定节点,参数为Element对象。clear方法清空所有节点。

    1.  
      >>> root.remove(child1) # 删除指定子节点
    2.  
      >>> print(etree.tostring(root))
    3.  
      b'<root><child2/><child3/></root>'
    4.  
      >>> root.clear() # 清除所有子节点
    5.  
      >>> print(etree.tostring(root))
    6.  
      b'<root/>'

    6、以列表的方式操作子节点
    可以将Element对象的子节点视为列表进行各种操作:

    1.  
      >>> child = root[0] # 下标访问
    2.  
      >>> print(child.tag)
    3.  
      child1
    4.  
       
    5.  
      >>> print(len(root)) # 子节点数量
    6.  
      3
    7.  
       
    8.  
      >>> root.index(child2) # 获取索引号
    9.  
      1
    10.  
       
    11.  
      >>> for child in root: # 遍历
    12.  
      ... print(child.tag)
    13.  
      child1
    14.  
      child2
    15.  
      child3
    16.  
       
    17.  
      >>> root.insert(0, etree.Element('child0')) # 插入
    18.  
      >>> start = root[:1] # 切片
    19.  
      >>> end = root[-1:]
    20.  
       
    21.  
      >>> print(start[0].tag)
    22.  
      child0
    23.  
      >>> print(end[0].tag)
    24.  
      child3
    25.  
       
    26.  
      >>> root.append( etree.Element('child4') ) # 尾部添加
    27.  
      >>> print(etree.tostring(root))
    28.  
      b'<root><child0/><child1/><child2/><child3/><child4/></root>'

    7、获取父节点
    使用getparent方法可以获取父节点。

    1.  
      >>> print(child1.getparent().tag)
    2.  
      root

    2、属性操作

    属性是以key-value的方式存储的,就像字典一样。
    

    1、创建属性

    可以在创建Element对象时同步创建属性,第二个参数即为属性名和属性值:

    1.  
      >>> root = etree.Element('root', interesting='totally')
    2.  
      >>> print(etree.tostring(root))
    3.  
      b'<root interesting="totally"/>'
    4.  
      也可以使用set方法给已有的Element对象添加属性,两个参数分别为属性名和属性值:
    5.  
       
    6.  
      >>> root.set('hello', 'Huhu')
    7.  
      >>> print(etree.tostring(root))
    8.  
      b'<root interesting="totally" hello="Huhu"/>'

    2、获取属性

    属性是以key-value的方式存储的,就像字典一样。直接看例子

    1.  
      # get方法获得某一个属性值
    2.  
      >>> print(root.get('interesting'))
    3.  
      totally
    4.  
       
    5.  
      # keys方法获取所有的属性名
    6.  
      >>> sorted(root.keys())
    7.  
      ['hello', 'interesting']
    8.  
       
    9.  
      # items方法获取所有的键值对
    10.  
      >>> for name, value in sorted(root.items()):
    11.  
      ... print('%s = %r' % (name, value))
    12.  
      hello = 'Huhu'
    13.  
      interesting = 'totally'

    也可以用attrib属性一次拿到所有的属性及属性值存于字典中:

    1.  
      >>> attributes = root.attrib
    2.  
      >>> print(attributes)
    3.  
      {'interesting': 'totally', 'hello': 'Huhu'}
    4.  
       
    5.  
      >>> attributes['good'] = 'Bye' # 字典的修改影响节点
    6.  
      >>> print(root.get('good'))
    7.  
      Bye

    3、文本操作

    标签及标签的属性操作介绍完了,最后就剩下标签内的文本了。可以使用text和tail属性、或XPath的方式来访问文本内容。 

    1、text和tail属性

    一般情况,可以用Element的text属性访问标签的文本。

    1.  
      >>> root = etree.Element('root')
    2.  
      >>> root.text = 'Hello, World!'
    3.  
      >>> print(root.text)
    4.  
      Hello, World!
    5.  
      >>> print(etree.tostring(root))
    6.  
      b'<root>Hello, World!</root>'```
    7.  
       
    8.  
      XML的标签一般是成对出现的,有开有关,但像HTML则可能出现单一的标签,如下面这段代码中的`<br/>`
    9.  
       
    10.  
      `<html><body>Text<br/>Tail</body></html>`
    11.  
       
    12.  
      Element类提供了tail属性支持单一标签的文本获取。
    13.  
      ```python
    14.  
      >>> html = etree.Element('html')
    15.  
      >>> body = etree.SubElement(html, 'body')
    16.  
      >>> body.text = 'Text'
    17.  
      >>> print(etree.tostring(html))
    18.  
      b'<html><body>Text</body></html>'
    19.  
       
    20.  
      >>> br = etree.SubElement(body, 'br')
    21.  
      >>> print(etree.tostring(html))
    22.  
      b'<html><body>Text<br/></body></html>'
    23.  
       
    24.  
      # tail仅在该标签后面追加文本
    25.  
      >>> br.tail = 'Tail'
    26.  
      >>> print(etree.tostring(br))
    27.  
      b'<br/>Tail'
    28.  
       
    29.  
      >>> print(etree.tostring(html))
    30.  
      b'<html><body>Text<br/>Tail</body></html>'
    31.  
       
    32.  
      # tostring方法增加method参数,过滤单一标签,输出全部文本
    33.  
      >>> print(etree.tostring(html, method='text'))
    34.  
      b'TextTail'

    2、XPath方式

    1.  
      # 方式一:过滤单一标签,返回文本
    2.  
      >>> print(html.xpath('string()'))
    3.  
      TextTail
    4.  
       
    5.  
      # 方式二:返回列表,以单一标签为分隔
    6.  
      >>> print(html.xpath('//text()'))
    7.  
      ['Text', 'Tail']

    方法二获得的列表,每个元素都会带上它所属节点及文本类型信息,如下:

    1.  
      >>> texts = html.xpath('//text()'))
    2.  
       
    3.  
      >>> print(texts[0])
    4.  
      Text
    5.  
      # 所属节点
    6.  
      >>> parent = texts[0].getparent()
    7.  
      >>> print(parent.tag)
    8.  
      body
    9.  
       
    10.  
      >>> print(texts[1], texts[1].getparent().tag)
    11.  
      Tail br
    12.  
       
    13.  
      # 文本类型:是普通文本还是tail文本
    14.  
      >>> print(texts[0].is_text)
    15.  
      True
    16.  
      >>> print(texts[1].is_text)
    17.  
      False
    18.  
      >>> print(texts[1].is_tail)
    19.  
      True

    4、文件解析与输出

    这部分讲述如何将XML文件解析为Element对象,以及如何将Element对象输出为XML文件。

    1. 文件解析

    文件解析常用的有fromstring、XML和HTML三个方法。接受的参数都是字符串。

    1.  
      >>> xml_data = '<root>data</root>'
    2.  
       
    3.  
      # fromstring方法
    4.  
      >>> root1 = etree.fromstring(xml_data)
    5.  
      >>> print(root1.tag)
    6.  
      root
    7.  
      >>> print(etree.tostring(root1))
    8.  
      b'<root>data</root>'
    9.  
       
    10.  
      # XML方法,与fromstring方法基本一样
    11.  
      >>> root2 = etree.XML(xml_data)
    12.  
      >>> print(root2.tag)
    13.  
      root
    14.  
      >>> print(etree.tostring(root2))
    15.  
      b'<root>data</root>'
    16.  
       
    17.  
      # HTML方法,如果没有<html>和<body>标签,会自动补上
    18.  
      >>> root3 = etree.HTML(xml_data)
    19.  
      >>> print(root3.tag)
    20.  
      html
    21.  
      >>> print(etree.tostring(root3))
    22.  
      b'<html><body><root>data</root></body></html>'

    2. 输出

    输出其实就是前面一直在用的tostring方法了,这里补充xml_declaration和encoding两个参数,前者是XML声明,后者是指定编码。

    1.  
      >>> root = etree.XML('<root><a><b/></a></root>')
    2.  
       
    3.  
      >>> print(etree.tostring(root))
    4.  
      b'<root><a><b/></a></root>'
    5.  
       
    6.  
      # XML声明
    7.  
      >>> print(etree.tostring(root, xml_declaration=True))
    8.  
      b"<?xml version='1.0' encoding='ASCII'?> <root><a><b/></a></root>"
    9.  
       
    10.  
      # 指定编码
    11.  
      >>> print(etree.tostring(root, encoding='iso-8859-1'))
    12.  
      b"<?xml version='1.0' encoding='iso-8859-1'?> <root><a><b/></a></root>"

    5、ElementPath

          讲ElementPath前,需要引入ElementTree类,一个ElementTree对象可理解为一个完整的XML树,每个节点都是一个Element对象。而ElementPath则相当于XML中的XPath。用于搜索和定位Element元素。

            这里介绍两个常用方法,可以满足大部分搜索、查询需求,它们的参数都是XPath语句(关于XPath的学习可以查看我之前的一片文章):
    findall():返回所有匹配的元素,返回列表
    find():返回匹配到的第一个元素

    1.  
      >>> root = etree.XML("<root><a x='123'>aText<b/><c/><b/></a></root>")
    2.  
       
    3.  
      # 查找第一个b标签
    4.  
      >>> print(root.find('b'))
    5.  
      None
    6.  
      >>> print(root.find('a').tag)
    7.  
      a
    8.  
       
    9.  
      # 查找所有b标签,返回Element对象组成的列表
    10.  
      >>> [ b.tag for b in root.findall('.//b') ]
    11.  
      ['b', 'b']
    12.  
       
    13.  
      # 根据属性查询
    14.  
      >>> print(root.findall('.//a[@x]')[0].tag)
    15.  
      a
    16.  
      >>> print(root.findall('.//a[@y]'))
    17.  
      []

    以上内容大多来自此原文:Python lxml教程-SKYue

    6、案例(尤其最后的一篇代码) 

    • 解析HTML,案例1.py
    • 文件读取,案例2.html, 案例2.py
    • etree和XPath的配合使用, 案例3.py

    案例1.py

    1.  
      '''
    2.  
      安装lxml
    3.  
      '''
    4.  
      from lxml import etree
    5.  
       
    6.  
      '''
    7.  
      用lxml来解析HTML代码
    8.  
      '''
    9.  
       
    10.  
      text = '''
    11.  
      <div>
    12.  
      <ul>
    13.  
      <li class="item-0"> <a href="0.html"> first item </a></li>
    14.  
      <li class="item-1"> <a href="1.html"> first item </a></li>
    15.  
      <li class="item-2"> <a href="2.html"> first item </a></li>
    16.  
      <li class="item-3"> <a href="3.html"> first item </a></li>
    17.  
      <li class="item-4"> <a href="4.html"> first item </a></li>
    18.  
      <li class="item-5"> <a href="5.html"> first item </a>
    19.  
      </ul>
    20.  
      </div>
    21.  
      '''
    22.  
       
    23.  
      # 利用etree.HTML把字符串解析成HTML文档
    24.  
      html = etree.HTML(text)
    25.  
      s = etree.tostring(html)
    26.  
      print(s)

    案例2.html

    1.  
      <?xml version="1.0" encoding="utf-8"?>
    2.  
       
    3.  
      <bookstore>
    4.  
      <book category="cooking">
    5.  
      <title lang="en">Everyday Italian</title>
    6.  
      <author>Gidada De</author>
    7.  
      <year>2018</year>
    8.  
      <price>23</price>
    9.  
      </book>
    10.  
       
    11.  
      <book category="education">
    12.  
      <title lang="en">Python is Python</title>
    13.  
      <author>Food War</author>
    14.  
      <year>2008</year>
    15.  
      <price>83</price>
    16.  
      </book>
    17.  
       
    18.  
      <book category="sport">
    19.  
      <title lang="en">Running</title>
    20.  
      <author>Klaus Kuka</author>
    21.  
      <year>2010</year>
    22.  
      <price>43</price>
    23.  
      </book>
    24.  
       
    25.  
      </bookstore>

    案例2.py 

    1.  
      from lxml import etree
    2.  
       
    3.  
      # 只能读取xml格式内容,html报错
    4.  
      html = etree.parse("./v30.html")
    5.  
       
    6.  
      rst = etree.tostring(html, pretty_print=True)
    7.  
      print(rst)

    案例3.py 

    1.  
      from lxml import etree
    2.  
       
    3.  
      # 只能读取xml格式内容,html报错
    4.  
      html = etree.parse("./v30.html")
    5.  
      print(type(html))
    6.  
       
    7.  
      rst = html.xpath('//book')
    8.  
      print(type(rst))
    9.  
      print(rst)
    10.  
       
    11.  
      # xpath的意识是,查找带有category属性值为sport的book元素
    12.  
      rst = html.xpath('//book[@category="sport"]')
    13.  
      print(type(rst))
    14.  
      print(rst)
    15.  
       
    16.  
      # xpath的意识是,查找带有category属性值为sport的book元素下的year元素
    17.  
      rst = html.xpath('//book[@category="sport"]/year')
    18.  
      rst = rst[0]
    19.  
      print(type(rst))
    20.  
      print(rst.tag)
    21.  
      print(rst.text)

    目前有很多xml,html文档的parser,如标准库的xml.etree , beautifulsoup  ,  还有lxml. 都用下来感觉lxml不错,速度也还行,就他了.

    围绕三个问题:

    • 问题1:有一个XML文件,如何解析
    • 问题2:解析后,如果查找、定位某个标签
    • 问题3:定位后如何操作标签,比如访问属性、文本内容等

    这些操作应该算是比较基础的,还可以自己在网上查找相关教程,官网更详细一点,进阶xpath语法,要在以后操作xml文件和html文件用上.

    1.  
      #!/usr/bin/python
    2.  
      # coding=utf-8
    3.  
      # __author__='dahu'
    4.  
      #
    5.  
      '''
    6.  
      Element是XML处理的核心类,
    7.  
      Element对象可以直观的理解为XML的节点,大部分XML节点的处理都是围绕该类进行的。
    8.  
      这部分包括三个内容:节点的操作、节点属性的操作、节点内文本的操作。
    9.  
      '''
    10.  
      from lxml import etree
    11.  
       
    12.  
      # 1.创建element
    13.  
      root = etree.Element('root')
    14.  
      print root, root.tag
    15.  
       
    16.  
      # 2.添加子节点
    17.  
      child1 = etree.SubElement(root, 'child1')
    18.  
      child2 = etree.SubElement(root, 'child2')
    19.  
       
    20.  
      # 3.删除子节点
    21.  
      # root.remove(child2)
    22.  
       
    23.  
      # 4.删除所有子节点
    24.  
      # root.clear()
    25.  
       
    26.  
      # 5.以列表的方式操作子节点
    27.  
      print(len(root))
    28.  
      print root.index(child1) # 索引号
    29.  
      root.insert(0, etree.Element('child3')) # 按位置插入
    30.  
      root.append(etree.Element('child4')) # 尾部添加
    31.  
       
    32.  
      # 6.获取父节点
    33.  
      print(child1.getparent().tag)
    34.  
      # print root[0].getparent().tag #用列表获取子节点,再获取父节点
    35.  
      '''以上都是节点操作'''
    36.  
       
    37.  
      # 7.创建属性
    38.  
      # root.set('hello', 'dahu') #set(属性名,属性值)
    39.  
      # root.set('hi', 'qing')
    40.  
       
    41.  
      # 8.获取属性
    42.  
      # print(root.get('hello')) #get方法
    43.  
      # print root.keys(),root.values(),root.items() #参考字典的操作
    44.  
      # print root.attrib #直接拿到属性存放的字典,节点的attrib,就是该节点的属性
    45.  
      '''以上是属性的操作'''
    46.  
       
    47.  
      # 9.text和tail属性
    48.  
      # root.text = 'Hello, World!'
    49.  
      # print root.text
    50.  
       
    51.  
      # 10.test,tail和text的结合
    52.  
      html = etree.Element('html')
    53.  
      html.text = 'html.text'
    54.  
      body = etree.SubElement(html, 'body')
    55.  
      body.text = 'wo ai ni'
    56.  
      child = etree.SubElement(body, 'child')
    57.  
      child.text='child.text' #一般情况下,如果一个节点的text没有内容,就只有</>符号,如果有内容,才会<>,</>都有
    58.  
      child.tail = 'tails' # tail是在标签后面追加文本
    59.  
      print(etree.tostring(html))
    60.  
      # print(etree.tostring(html, method='text')) # 只输出text和tail这种文本文档,输出的内容连在一起,不实用
    61.  
       
    62.  
      #11.Xpath方式
    63.  
      # print(html.xpath('string()')) #这个和上面的方法一样,只返回文本的text和tail
    64.  
      print(html.xpath('//text()')) #这个比较好,按各个文本值存放在列表里面
    65.  
      tt=html.xpath('//text()')
    66.  
      print tt[0].getparent().tag #这个可以,首先我可以找到存放每个节点的text的列表,然后我再根据text找相应的节点
    67.  
      # for i in tt:
    68.  
      # print i,i.getparent().tag,' ',
    69.  
       
    70.  
      #12.判断文本类型
    71.  
      print tt[0].is_text,tt[-1].is_tail #判断是普通text文本,还是tail文本
    72.  
      '''以上都是文本的操作'''
    73.  
       
    74.  
      #13.字符串解析,fromstring方式
    75.  
      xml_data = '<html>html.text<body>wo ai ni<child>child.text</child>tails</body></html>'
    76.  
      root1=etree.fromstring(xml_data) #fromstring,字面意思,直接来源字符串
    77.  
      # print root1.tag
    78.  
      # print etree.tostring(root1)
    79.  
       
    80.  
      #14.xml方式
    81.  
      root2 = etree.XML(xml_data) #和fromstring基本一样,
    82.  
      print etree.tostring(root2)
    83.  
       
    84.  
      #15.文件类型解析
    85.  
      tree =etree.parse('text') #文件解析成元素树
    86.  
      root3 = tree.getroot() #获取元素树的根节点
    87.  
      print etree.tostring(root3,pretty_print=True)
    88.  
       
    89.  
      parser= etree.XMLParser(remove_blank_text=True) #去除xml文件里的空行
    90.  
      root = etree.XML("<root> <a/> <b> </b> </root>",parser)
    91.  
      print etree.tostring(root)
    92.  
       
    93.  
      #16.html方式
    94.  
      xml_data1='<root>data</root>'
    95.  
      root4 = etree.HTML(xml_data1)
    96.  
      print(etree.tostring(root4))#HTML方法,如果没有<html>和<body>标签,会自动补上
    97.  
      #注意,如果是需要补全的html格式:这样处理哦
    98.  
      with open("quotes-1.html",'r')as f:
    99.  
      a=H.document_fromstring(f.read().decode("utf-8"))
    100.  
       
    101.  
      for i in a.xpath('//div[@class="quote"]/span[@class="text"]/text()'):
    102.  
      print i
    103.  
       
    104.  
      #17.输出内容,输出xml格式
    105.  
      print etree.tostring(root)
    106.  
      print(etree.tostring(root, xml_declaration=True,pretty_print=True,encoding='utf-8'))#指定xml声明和编码
    107.  
      '''以上是文件IO操作'''
    108.  
       
    109.  
      #18.findall方法
    110.  
      root = etree.XML("<root><a x='123'>aText<b/><c/><b/></a></root>")
    111.  
      print(root.findall('a')[0].text)#findall操作返回列表
    112.  
      print(root.find('.//a').text) #find操作就相当与找到了这个元素节点,返回匹配到的第一个元素
    113.  
      print(root.find('a').text)
    114.  
      print [ b.text for b in root.findall('.//a') ] #配合列表解析,相当帅气!
    115.  
      print(root.findall('.//a[@x]')[0].tag) #根据属性查询
    116.  
      '''以上是搜索和定位操作'''
    117.  
      print(etree.iselement(root))
    118.  
      print root[0] is root[1].getprevious() #子节点之间的顺序
    119.  
      print root[1] is root[0].getnext()
    120.  
      '''其他技能'''
    121.  
      # 遍历元素数
    122.  
      root = etree.Element("root")
    123.  
      etree.SubElement(root, "child").text = "Child 1"
    124.  
      etree.SubElement(root, "child").text = "Child 2"
    125.  
      etree.SubElement(root, "another").text = "Child 3"
    126.  
      etree.SubElement(root[0], "childson").text = "son 1"
    127.  
      # for i in root.iter(): #深度遍历
    128.  
      # for i in root.iter('child'): #只迭代目标值
    129.  
      # print i.tag,i.text
    130.  
      # print etree.tostring(root,pretty_print=True)

    出处:https://blog.csdn.net/ydw_ydw/article/details/82227699

    您的资助是我最大的动力!
    金额随意,欢迎来赏!
    款后有任何问题请给我留言。

    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐按钮。
    如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我。(●'◡'●)

    如果你觉得本篇文章对你有所帮助,请给予我更多的鼓励,求打             付款后有任何问题请给我留言!!!

    因为,我的写作热情也离不开您的肯定支持,感谢您的阅读,我是【Jack_孟】!

  • 相关阅读:
    Spring IOC
    C++ 内存模型
    C++ 多态
    Java 多态
    Java 自动装箱与自动拆箱
    C++ priority_queue
    多个页面使用到一些名称类的同一个接口,借助vuex实现
    element-ui自定义表单验证
    vue项目中导出excel文件
    数组对象根据某个属性进行排序
  • 原文地址:https://www.cnblogs.com/mq0036/p/13161350.html
Copyright © 2011-2022 走看看