• xpath 和 jsonpath 解析


    XPath 是一门在 XML 文档中查找信息的语言,可用来在 XML 文档中对元素和属性进行遍历,快速提取xml文档中的的信息,详细的xpath教程参见:https://www.w3school.com.cn/xpath/index.asp

    xpath用法

    //任意层次下,/ 根或下一层

    //@id

    任意层次下有id的节点的属性值

    //*[@id]      //book[@id]

    所有含有id属性的节点     含有id属性的book节点

    /bookstore/*      /bookstore//*

    一层的所有节点              递归下去每一层节点

    //*   //@*   // *[@*]   

    *表示所有,@是取属性值

    //book[position()=3]   //book[positon()<last()-2]

    限制book节点的位置范围

    //book[price>400]

    price是子节点,其值大于40的book节点

    /book/text()   /book//text()

    下的所有文本节点,/子节点和 // 子孙节点

    //*[local-name()="book"]

    所有节点中名称为book的

    //book/child::node()  

    node()表示节点,child::表示子节点

    //*[self::title or self::price]

    任意层次下的节点,这些节点自身是title或者price

    //book[contains(@class, class_name)]

    contains() 包含,class属性中包含class_name的节点

    //*[contains(local-name(), book)]

    标签名包含book的节点

    其他还提供了如local-name()等函数,可查看w3cSchool获取详细用法。

    lxml

    它是python的一个库,用于对xml和html文档进行解析,性能非常好,最新版支持的2.6+,python3支持到3.6。

    centos安装

    在centos下需要编译安装两个依赖:libxml2-devel 和 libxslt-devel

    yum install libxml2-devel 

    不同的平台可能以来不同,详见官网说明https://lxml.de/installation.html

    依赖安装完成后,使用pip安装即可

    pip install lxml

    lxml的使用

    lxml是使用C语言的开发的对xml及html文本的解析库,主要基于C语言的计算速度快的有点,python的lxml只是封装了调用接口,方便python使用lxml解析xml文本。

    etree.HTML 

    from lxml import etree
    
    html_text = """<html><body><div>
        <ul>
             <li class="l1"><a href="link1.html">第一行</a></li>
             <li class="l2"><a href="link2.html">第二行</a></li>
             <li class="l3"><a href="link5.html">第三行</a></li>
        </ul>
     </div>
    </body></html>"""
    # 我们获取的一个网页的html文本数据,字符串或者字节序列均可,
    
    root = etree.HTML(html_text)       # 返回一个root节点对象,相当于html的document全局根对象
    print(root, type(root))            # <Element html>  <class 'lxml.etree._Element'>

    也可以通过fromstring方法解析,获取根_Element对象。获取root对象后,可以通过root对象找到其所有的子节点也就是html中的所有子标签。常用的方式就是通过xpath语法解析及即可。如果我们直接读取一个html文件中的html 内容,使用html=etree.parse('test.html',etree.HTMLParser())的方式即可,同时会自动修复html中的一些标签的问题。每一个_Element元素对象中都包含了其直接子_Element,使用容器化的方式关联了直接子_Element,例如在div标签下有一个直接子标签ul。我们可以通过xpath的解析方式去获得div的_Element对象。

    div_element = root.xpath("//div")  # 任意层次下的ul标签,只有一个
    print(div_element)                 # <Element div at 0x23a32345>
    
    print(len(div_element))            # 1,长度为一,只有一个ul子标签
    ul_element = div_element[0]        # 容器的方式直接取第一个子标签,及ul标签
    
    # 也可直接对其进行遍历
    for li in ul_element:
        print(li)                      # 3个li对应的_Element对象 

    etree._Element

    每一个_Element对象对应了html中的标签,类似于一棵DOM树的结构,我们可以手动创建单个节点,并可以将多个_Element合并到这个dom 树上的指定位置,与的其他_Element对象产生关系。

    每一个_Element对象的操作方式和jQuery的对dom树中节点的操作极其相似。

    from lxml.etree import Element   # Element是一个工厂函数,返回一个指定名的_Element对象
    
    root = Element("root", attrib={"abc":"hello"}, id="2123")   # 两种方式添加属性,
    print(etree.tostring(root, pretty_print=True))       # tostring函数打印节点源码 b'<root id="2123" abc="hello"/>'
    print(root.tag)                   # 标签名 root

    属性值得创建可以通过attrib参数来指定,也可以使用关键字传参得方式指定,未定义得关键字都将收集到**extra可变关键字参数中,与attrib字典值合并作为属性。_Element通过字典得方式管理属性一个标签的属性信息,并提供了访问接口

    print(etree.tostring(root))       # tostring函数打印节点源码 b'<root id="2123" abc="hello"/>'
    
    root.get("id")                    # 获取一个属性值,2123
    root.set("name", "title")         # 添加一个属性,name=title

    两个_Element对象之间可以建立关系,可以成为兄弟节点(同级)和父子节点得关系。

    root = Element("root")      # <root />
    body = Element("body")      # <body />
    
    etree.SubElement(root, body)   # <root> <body /> </root>     body成为root得子节点
    root.append(body)              # 或者调用节点的append方法

    BeautifulSoup

    BeautifulSoup同样使用xpath语法在文本中提取数据,并在其基础上做了更加方便使用者的封装,并且内部解析解可以使用多种解析引擎,其中包括lxml, 是一个效率较高的开源解析库,而BeautifulSoup由此也获得更过使用者的青睐。

    解析库包括以下,初始化时根据需要指定不同的features参数即可指定不同的解析器,各个解析器有自己的优缺点。根据需要选择即可。

    解析器 使用方法 优势 劣势
    Python标准库 BeautifulSoup(markup, "html.parser")
    • Python的内置标准库
    • 执行速度适中
    • 文档容错能力强
    • Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差

    lxml

    HTML 解析器

    BeautifulSoup(markup, "lxml")
    • 速度快
    • 文档容错能力强
    • 需要安装C语言库

    lxml

    XML析器

    BeautifulSoup(markup, ["lxml-xml"])

    BeautifulSoup(markup, "xml")

    • 速度快
    • 唯一支持XML的解析器
    • 需要安装C语言库
    html5lib BeautifulSoup(markup, "html5lib")
    • 最好的容错性
    • 浏览器的方式解析文档
    • 生成HTML5格式的文档
    • 速度慢
    • 不依赖外部扩展

    安装

    直接使用pip 安装即可 pip install bs4该安装只是安装了bs4库,如果需要使用lxml作为xpath解析引擎需要自行安装lxml库,才能使用lxml,如果没有安装lxml,也可以使用内部bs4内部自带的

    使用

    官方文档有详细的使用介绍:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/

    from bs4 import BeautifulSoup
    import request
    
    html_text = """<html><body><div>
        <ul>1234
             <li class="l1"><a href="link1.html">第一行</a></li>
             <li class="l2"><a href="link2.html">第二行</a></li>
             <li class="l3"><a href="link5.html">第三行</a></li>
        </ul>
     </div>
    </body></html>"""
    
    soup = BeautifulSoup(markup=html_text, features="lxml")  
    # markup指定需要解析的文档,string或者类文件对象,features指定解析器,这里使用lxml, 返回的
    print(soup, type(soup))
    
    ## 输出结果------------
    <html><body><div>
    <ul>1234
    <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>    <class 'bs4.BeautifulSoup'>

    BeautifulSoup对象

    初始化后返回的是一个Beautifulsoup对象,代表了整个文档,也是我们进行数据提取的起点。

    常用的方法

    soup.name
    soup.prettify()  # 格式输出
    soup.builder     #  解析引擎,这里是lxml
    soup.find_all()
    soup.find()      # 默认任意层寻找指定标签,且深度优先遍历。
    soup.tag_name    # 根据tag名寻找

    tag对象

    文档下直接存在div标签,这些标签对应一个tag对象。

    div = soup.div   # soup 下的任意层次div标签
    print(div.name, div.attrs, div.text) # 标签名div,标签中的属性,None,其中文本内容,子标签中有全部显示

    tag类型可以继续向下寻找节点,使用属性访问的方式及soup.div的方式只会寻找到soup对象下的第一个div标签,这实际是电泳find方法找寻一个节点。如果需要找寻所有div节点需要使用find_all方法,传入寻找的标签名即可,他会返回节点的列表,遍历即可逐一获取。

    # find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
    # 查询到所有的li标签并遍历
    for li in div.find_all("li"):
        print(li.text)

    tag.name
    tag.attrs   # 属性字典
    tag.get("id"), tag["id"], tag.attrs["id"], tag.attr.get("id")      # 取一个属性值

    NavigableString

    如果只需要标签内部的文本内容,而不在意标签,调用tag的string属性即可。

    # div为一个Tag 对象
    div.string       # div 下必须没有子标签,直接为文本内容,则会返回文本,否则为None
    div.strings      # 返回迭代器,div标签下所有的文本内容
    div.stripped_strings  # strings的内容去除了两端空白
    
    print(div.string)                  # None
    print(list(div.strings))           # ['1234
        ', '
    ', '第一个', '
    ', 'second item', '
    ', 'a属性', '
    ', '
    ']
    print(list(div.stripped_strings))  # ['1234', '第一个', 'second item', 'a属性']

    注释对象

    如果源html文档中含有注释内容,经过解析bs4会将其封装为注释对象。隶属于某个标签的注释可以使用该标签的tag.comment获取

    遍历文档树

    遍历子节点
    soup.div.contents           # div的直接子节点们,返回一个列表。
    soup.div.children           # div返回迭代器,iter(content)
    soup.div.descendants        # div 节点的所有子孙节点, 一层层递归,且深度优先。
    遍历祖先节点
    tag.parent        # 当前tag的第一个父节点,同样是tag对象
    tag.parents       # 父节点们,返回迭代器,从近到远
    
    soup.li.parent    # 得到ul节点
    遍历兄弟节点
    tag.next_sibling         # 下一个兄弟节点
    tag.previous_sibling     # 上一个兄弟节点
    tag.next_siblings        # 兄弟节点们
    li
    soup.li.next_sibling        # "
    "   li标签后存在一个文本节点,这里是换行符,所以下一个不是li。
    list(soup.li.next_siblings) #  ['
    ', <li>second item</li>, '
    ', <li>a属性</li>, '
    ']
    其他节点
    tag.next_element      # 当前节点的下一个节点(包括标签和文本),按照深度优先的原则
    tag.next_elements     # tag同级的节点和其所有的子节点的迭代器
    
    soup.li.next_element  #  <a></a>   第一个li中内部的a标签节点
    soup.li.next_elements # 按照顺序,遍历li同级极其子标签(包括文本节点),按照深度优先规则。返回迭代器

    find_all

    搜索文档内部所有满足条件的节点,返回一个列表。

    def find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
    参数说明:
      name: 字符串 | re.compile("^a")正则对象 | 列表多个 | True(所有节点) | 接受标签名的函数,返回True 
      attr: 属性字典,指定标签名和值,值可以使用字符串,正则对象,列表,True,函数,匹配时结果True表示匹配
      **kwargs:接受没有被其他关键字参数接受的传参,并合并到attr中作为属性删选,class属性应该写为class_
      recursive:默认True递归查找,是否递归相当于使用 // 和 / 的区别
      text: 文本匹配来过滤标签,同name 使用标签一样,参数可以使用正则对象,函数等
      limit:结果的个数

    find_all 方法可以简写,tag或者soup对象是可调用对象,其__call__方法直接调用find_all,方便使用

    soup = Beautifulful(open("index.html"), "lxml")
    soup.find_all("div", id="item")     # 等价于 soup("div", id="item")
    soup.div.findall("li")              # soup.div("li")

    find

    find函数是使用深度优先的方式找到第一指定的节点。与属性访问的方式相同。

    def find(self, name=None, attrs={}, recursive=True, text=None, **kwargs):pass

    soup.div # 等价于 soup.find("div")

    获取文本

    获得节点对象后,可以使用text和get_text()方法获取该节点的文本内容,text等价于默认的get_text方法,结果不会去除两端的空白字符,如果需要去除,可以使用get_text(strip=True)

    tag.text        # 等价tag.get_text()  
    tag.get_text(strip=True)         # 去除空白字符 

    string 和 strings 方法也可以提取文本内容,但是标签后的空白字符也会算作一个元素被筛选。

    CSS选择器

    css选择器是通过节点得特殊属性值,例如id, class,标签名,来查找html节点

    # 使用select方法,即可指定css选择器规则
    
    tag.select("p")        # 元素选择
    tag.select(".title")   # 类选择,根据class名
    tag.select("#id-1")    # 根据id值
    
    tag.select("div p")    # div下所有p
    tag.select("div > p")  # div下第一层的p,其余不要
    tag.select("div.content > p:nth-of-type(2)")  # 使用了伪类,class为content的div标签下第二个p标签
    
    tag.select(div p)
    
    # 属性选择器
    tag.select("[src]")      # 有src属性的
    tag.select("[src="/"]")  # [src^="/"] 以开头     [src$="/"] 以结尾   [src*="http"] 包含有
    tag.select("[class=title light]")   # 完全匹配,两个同时满足
    tag.select("[class~=title]")        # 标签可以有多个属性,其中有一个是title即可

    JsonPath

    jsonpath是对一些多层嵌套结构的json字符串进行信息提取的方式,与xpath解决的问题是相同的,即从一个较为特殊的大字符串中去快速提取的想要的信息。json格式的数据作为服务进程之间的交互已经受到了相当流行,常常需要从某些json结构中提取数据。

    使用

    jsonpath的使用方式和xpath使用方式相似,只是使用了不同的元字符表示,可以对比xpath的语法进行使用。

    语法

    JSONPATH 描述
    $ 根对象,例如$.name
    . 或者 [ ] 子节点,例如$.name
    .. 子孙节点访问,例如$..name

    @

    当前对象自身

    * 通配符,所有,例如$.leader.*
    ['key0','key1'] 多个节点访问。例如$['id','name']
    [num] 数组索引访问,可以是负数。例如$[0].leader.departments[-1].name
    [num0,num1,num2...] 数组多个元素访问,可以是负数,返回数组中的多个元素。例如$[0,3,-2,5]
    [start:end :step] 数组范围访问,可以是负数;step是步长,返回数组中的多个元素。例如$[0:5:2]
    [?(key)] 对象属性非空过滤,例如$.departs[?(name)],存在指定属性的departs
    [key > 123] 数值类型对象属性比较过滤,例如$[id >= 123],支持=,!=,>,>=,<,<=
    [key = '123'] 字符串类型对象属性比较过滤,例如$[name = '123'],支持=,!=,>,>=,<,<=
    [key like 'aa%'] 字符串类型like过滤,例如$[name like 'sz*'],通配符只支持%,支持not like
    [key rlike 'regexpr'] 字符串类型正则匹配过滤,指定正则字符串
    例如departs[name like 'aa(.)*'],
    正则语法为jdk的正则语法,支持not rlike
    [key in ('v0', 'v1')] IN过滤, 支持字符串和数值类型
    例如:
    $.departs[name in ('wenshao','Yako')]
    $.departs[id not in (101,102)]
    [key between 234 and 456] BETWEEN过滤, 支持数值类型,删选数值范围,支持not between
    例如:
    $.departs[id between 101 and 201]
    $.departs[id not between 101 and 201]
    length() 或者 size() 数组长度。例如$.values.size()
    支持类型java.util.Map和java.util.Collection和数组
    keySet() 获取Map的keySet或者对象的非空属性名称。例如$.val.keySet()
    支持类型:Map和普通对象
    不支持:Collection和数组(返回null)

    xpath对比示例

    XPath JSONPath Result
    /store/book/author $.store.book[*].author the authors of all books in the store
    //author $..author all authors
    /store/* $.store.* all things in store, which are some books and a red bicycle.
    /store//price $.store..price the price of everything in the store.
    //book[3] $..book[2] the third book
    //book[last()] $..book[(@.length-1)]
    $..book[-1:]
    the last book in order.
    //book[position()<3] $..book[0,1]
    $..book[:2]
    the first two books
    //book[isbn] $..book[?(@.isbn)] filter all books with isbn number
    //book[price<10] $..book[?(@.price<10)] filter all books cheapier than 10
    //* $..* all Elements in XML document. All members of JSON structure.



  • 相关阅读:
    现代操作系统-读者/写者问题
    现代操作系统-进程互斥
    关于网页强制被跳转到wpkg.org的解决
    Leetcode Count Prime
    Leetcode Add Two Numbers
    Leetcode Two Sum
    can't find -lsocket的解决办法
    删除Windows右键不用的选项
    Linux下的另一个词典GoldenDict
    spark执行例子eclipse maven打包jar
  • 原文地址:https://www.cnblogs.com/k5210202/p/13079738.html
Copyright © 2020-2023  润新知