• python3 使用 lxml 库解析 HTML


    python3 lxml

    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
    
    xpath 常用规则
    表达式 描述
    nodename 选取此节点的所有子节点
    / 从当前节点选取直接子节点
    // 从当前节点选取子孙节点
    . 选取当前节点
    .. 选取当前节点的父节点
    @ 选取属性
    * 通配符,选择所有元素节点与元素名
    @* 选取所有属性
    [@attrib] 选取具有给定属性的所有元素
    [@attrib='value'] 选取给定属性具有给定值的所有元素
    [tag] 选取所有具有指定元素的直接子节点
    [tag='text'] 选取所有具有指定元素并且文本内容是text节点
    xpath 中的运算符
    运算符 描述 实例 返回值
    or age=19 or age = 20 如果 age 等于 19 或者等于 20 则返回 True, 否则返回 False
    an age > 19 and age < 22 如果 age 大于 19 小于 22 则返回 True, 否则返回 False
    mod 取余 5 mod 2 取 5 除以 2 的余数 1
    丨【管道符】 取两个节点的集合 //book 丨【管道符】 //cd 返回拥有 book 和 cd 元素的节点集合
    +, -, *, div 加, 减, 乘, 除 8 加/减/乘/除 4 12/4/32/2
    =, !=, <, <=, >, >= 等于, 不等于, 小于, 小于等于, 大于, 大于等于 age =/ != / < / <= / > / >= 19 age 等于 / 不等于 / 小于 / 小于等于 / 大于 / 大于等于 19, 则返回 True, 否则返回 False
    HTML解析
    • test_lxml.html HTML 文件
    <head><title>The Dormouse's story</title></head>
    
    <p class="story">
    	this is P label
    	<a href="http://www.baidu.com" class="baidu" id="link1"><span>baidu</span></a><span>this is span</span>
    	<a href="http://www.cnblogs.com" class="cnblogs" id="link2"><span>cnblogs</span></a>
    </p>
    <div>
    	<ul>
    		<li class="item-0 li" name="item0"><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>
    		<li class="aaa li-aaa"><a href="link6.html">aaaaa item</a></li>
    		<li class="li li-first" name="item6"><a href="link6.html"><span>six item</span></a></li>
    		<li class="li li-first" name="item7"><a href="link7.html"><span>seven item</span></a></li>
    	</ul>
    	<ul class="ul2">
    		<li class="item-10 li" name="item10"><a href="link10.html">10 item</a></li>
    		<li class="item-11 li" name="item11"><a href="link11.html">11 item</a></li>
    		<li class="item-12 li" name="item12"><a href="link12.html">12 item</a></li>
    		<li class="item-13 li" name="item13"><a href="link13.html">13 item</a></li>
    		<li class="item-14 li" name="item14"><a href="link14.html">14 item</a></li>
    		<li class="item-15 li" name="item15"><a href="link15.html">15 item</a></li>
    		<li class="item-16 li" name="item16"><a href="link16.html">16 item</a></li>
    	</ul>
    </div>
    
    • 读取文本解析节点
    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'))
    
    • 读取 HTML 文件进行解析
    from lxml import etree
    html_path = os.path.join(os.path.dirname(os.getcwd()), 'html_dir', 'test_lxml.html')
    html = etree.parse(html_path, etree.HTMLParser()) #指定解析器HTMLParser会根据文件修复HTML文件中缺失的如声明信息
    result = etree.tostring(html)   # 解析成字节
    # result=etree.tostringlist(html) # 解析成列表
    print(type(html))
    print(type(result))
    print(result)
    
    获取节点
    • 获取所有子孙节点
    html = etree.parse(html_path, etree.HTMLParser())
    # 从 HTML 节点选取所有的子孙节点
    result = html.xpath('//*')
    print(f'选择 HTML 节点及所有子孙节点 返回的数据类型: {type(result)}, 返回节点长度: {len(result)}')
    print(f'选择 HTML 节点及所有子孙节点: {result}')
    
    • 获取所有直接子节点
    html = etree.parse(html_path, etree.HTMLParser())
    # 从 li 节点选取所有的直接子节点 a 节点
    result = html.xpath('//li/a')
    print(f'选择所有 li 节点下的 a 节点 返回的数据类型: {type(result)}, 返回节点长度: {len(result)}')
    print(f'选择所有 li 节点下的 a 节点 返回的: {result}')
    
    • 获取所有的直接父节点
    html = etree.parse(html_path, etree.HTMLParser())
    # 获取所有 li 节点节点的直接父节点
    result = html.xpath('//li/..')
    print(f'获取所有 li 节点的直接父节点 返回的数据类型: {type(result)}, 返回节点长度: {len(result)}')
    print(f'获取所有 li 节点的直接父节点 返回的: {result}')
    
    • 查找属性符合条件的节点
      • 在选取节点的时候我们还可以通过节点的属性进行匹配
    html = etree.parse(html_path, etree.HTMLParser())
    # 获取所有 属性class='item-1' 的所有 li 节点
    result = html.xpath("//li[@class='item-1']")
    print(f"获取所有 属性 class='item-1' 的所有 li 节点 返回的: {result}")
    
    # 获取所有 属性name='item0' 的所有 li 节点
    result = html.xpath("//li[@name='item0']")
    print(f"获取所有 属性 name='item0' 的所有 li 节点 返回的: {result}")
    
    获取文本或属性
    • 文本获取
    html = etree.parse(html_path, etree.HTMLParser())
    # 获取 p 节点文本
    result = html.xpath("//p[@class='story']/text()")
    print(f'属性 class="story" 的 p 节点的文本: {result}')
    
    • 属性获取
      • 使用 @ 符号即可获取节点的属性
    html = etree.parse(html_path, etree.HTMLParser())
    # 获取属性 class=story 的节点 p 下面所有节点 a 的 id 属性
    result = html.xpath("//p[@class='story']/a/@id")
    print(f'获取属性 class=story 的节点 p 下面所有节点 a 的 id 属性: {result}')
    
    • 属性多值匹配
      • 如果某个属性的值有多个时,我们可以使用contains()函数来获取
      • contains 方法单独使用时只会匹配到第一个节点, 如果有多个不会继续匹配后续的节点
    html = etree.parse(html_path, etree.HTMLParser())
    # 匹配 class 属性中包含 aaa 的 li 节点下的子节点 a 的文本
    result = html.xpath("//li[@class='aaa']/a/text()")     # 没有匹配到值
    print(f'匹配 class 属性中包含 aaa 的 li 节点下的子节点 a 的文本: {result}')
    
    result = html.xpath("//li[contains(@class, 'aaa')]/a/text()")     # 可以匹配到值
    print(f'匹配 class 属性中包含 aaa 的 li 节点下的子节点 a 的文本: {result}')
    
    • 多属性匹配
      • 我们还会遇到根据多个属性确定一个节点,这时就需要同时匹配多个属性,此时可用运用and运算符来连接使用
    html = etree.parse(html_path, etree.HTMLParser())
    # 匹配 class 属性中包含 li-first 且 name=item7 的 li 节点下的子节点 aa/span 的文本
    result = html.xpath("//li[contains(@class, 'li-first') and @name='item7']/a/span/text()")
    print(f'匹配 class 属性中包含 li-first 且 name=item7 的 li 节点下的子节点 a/span文本: {result}')
    
    • 按序选择
      • 有时候,我们在选择的时候某些属性可能同时匹配多个节点,但我们只想要其中的某个节点,如第二个节点或者最后一个节点,这时可以利用中括号引入索引的方法获取特定次序的节点
      • 在 xpath 中索引位置从1开始, 和 python 的索引起始位置不同
      • last(): 最后一个节点, 如果后面跟了子节点, 只有一个节点; 如果没有子节点, 而且有多个符合条件的节点, 返回多个节点
      • position(): 返回所有的节点, 不写效果一样
      • position() < 3: 返回索引位置小于 3 的节点, 即节点位置为 1 和 2
      • [index]: 使用这种方式时, 有可能会有多个元素;
        • 例如: 有两个同级节点 ul, 下面都有10的 li 子节点, 此时通过索引会同时取到两个 ul 下符合条件的 li 节点
    html = etree.parse(html_path, etree.HTMLParser())
    result = html.xpath('//li[1]/a/text()')
    print(f'两个符合条件的 ul 都匹配到了, 获取第一个 li 节点下 a 节点文本: {result}')
    result = html.xpath('//li[3]/a/text()')
    print(f'两个符合条件的 ul 都匹配到了, 获取第三个 li 节点下 a 节点文本: {result}')
    result = html.xpath('//li[last()]/a/text()')
    print(f'只匹配到了 li 最多的 ul , 获取最后一个 li 节点下 a 节点文本: {result}')
    result = html.xpath('//li[position() < 3]/a/text()')
    print(f'两个符合条件的 ul 都匹配到了, 获取位置小于3的 li 节点下 a 节点文本: {result}')
    result = html.xpath('//li[position()]/a/text()')
    print(f'两个符合条件的 ul 都匹配到了, 获取位置小于3的 li 节点下 a 节点文本: {result}')
    
    • 节点轴选择
    print('获取祖先节点')
    html = etree.parse(html_path, etree.HTMLParser())
    result = html.xpath('//li[1]/ancestor::*')
    print(f'获取 第一个 li 的所有祖先节点: {result}')
    result = html.xpath('//li[last()]/ancestor::*')
    print(f'获取 最后一个 li 所有祖先节点: {result}')
    result = html.xpath('//li[last()]/ancestor::div')
    print(f'获取 最后一个 li 所有 div 祖先节点: {result}')
    
    print('获取属性值')
    # 2个 ul 都被匹配到了
    result = html.xpath('//li[1]/attribute::*')
    print(f'获取 第一个 li 的所有属性值: {result}')
    # 2个 ul 都被匹配到了
    result = html.xpath('//li[last()]/attribute::*')
    print(f'获取 第一个 li 的所有属性值: {result}')
    
    print('获取所有的直接子节点')
    result = html.xpath('//li[1]/child::*')
    print(f'li[index] 获取所有的直接子节点: {result}')
    result = html.xpath('//li[last()]/child::*')
    print(f'li[last() ]获取所有的直接子节点: {result}')
    
    result = html.xpath('//li[1]/descendant::a/text()')
    print(f'第一个 li 节点, 获取所有的子孙节点的 a 节点的文本: {result}')
    result = html.xpath('//li[1]/following::*')
    print(f'获取当前子节点之后的所有节点【与当前节点同级并且包括其子节点】: {result}')
    result = html.xpath('//li[1]/following-sibling::*')
    print(f'获取当前子节点之后的所有节点【与当前节点同级不包括其子节点】: {result}')
    
  • 相关阅读:
    数据库基础
    Junit单元测试、反射与注解
    Stream流
    Java IO所用类
    字符集
    Lambda表达式
    【jdk1.8源码分析】LinkedHashMap
    线性表
    【jdk1.8源码分析】ArrayList
    java学习血泪史
  • 原文地址:https://www.cnblogs.com/gxfaxe/p/15255468.html
Copyright © 2020-2023  润新知