zoukankan      html  css  js  c++  java
  • 数据解析

    lxml库

    lxml 是 一个HTML/XML的解析器,其是由C语言来实现的,主要的功能是如何解析和提取 HTML/XML 数据。

    基本使用

    我们可以利用它来解析HTML代码,并且在解析HTML代码的时候,如果HTML代码不规范,它会自动的进行补全。

    from lxml import etree
    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> # 注意,此处缺少一个 </li> 闭合标签
         </ul>
     </div>
     """
    ##实例化一个html对象,将字符串解析为HTML文档
    html = etree.HTML(text)
    ##按字符串序列化HTML文档
    res = etree.tostring(html)
    print(res)
    

    读取HTML的文档

    我们先建立一个叫做pacong.html的HTML文档,里面放入以下内容:

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

    然后我们读取该文档的代码如下:

    from lxml import etree
    ##用parse()函数读取该文档
    html = etree.parse("pacong.html")
    res=etree.tostring(html,encoding="utf-8").decode("utf-8")
    print(res)
    

    爬取豆瓣热门电影实例

    from lxml import etree
    import requests
    headers = {
        "Referer":"https://movie.douban.com/explore",
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
    }
    html = requests.get("https://movie.douban.com/chart",headers=headers)
    text = html.text
    html_texts=etree.HTML(text)
    movies_names_list = html_texts.xpath("//table//@title")
    movie_info=[]
    for movie_name in movies_names_list:
        base_info = html_texts.xpath("//table//p/text()")[0]
        stars = html_texts.xpath("""//table//span[@class="rating_nums"]/text()""")[0]
        url = html_texts.xpath("//table//@src")[0]
        info_movie={
            "name":movie_name,
            "base_info":base_info,
            "stars":stars,
            "poster":url
        }
        movie_info.append(info_movie)
    print(movie_info)
    

    爬取电影天堂资源实例

    from lxml import etree
    import requests
    
    HEADERS={
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
    }
    url = "https://www.dy2018.com/html/bikan/index.html"
    scrapy_url_list = []
    scrapy_url_list.append(url)
    for i in range(2,3):
        scrapy_url_list.append("https://www.dy2018.com/html/bikan/index_{}.html".format(i))
    for i in scrapy_url_list:
        html = requests.get(i, headers=HEADERS)
        html = html.text
        html_text = etree.HTML(html)
        moive_lists = html_text.xpath("//table//b/a[2]/@href")
        for i_1 in moive_lists:
            movie_actors = []
            movie_infos = {}
            details_movie = etree.HTML(requests.get("https://www.dy2018.com"+i_1,headers=HEADERS).content.decode("gbk"))
            movie_infos["海报"] = details_movie.xpath("//p//@src")[0]
            movie_infos["译名"] = details_movie.xpath("//p[2]/text()")[0].replace("◎译  名","").strip()
            movie_infos["片名"] = details_movie.xpath("//p[3]/text()")[0].replace("◎片  名","").strip()
            movie_infos["类别"] = details_movie.xpath("//p[6]/text()")[0].replace("◎类  别","").strip()
            movie_infos["豆瓣评分"] = details_movie.xpath("//p[10]/text()")[0].replace("◎豆瓣评分","").strip()
            movie_infos["片长"] = details_movie.xpath("//p[15]/text()")[0].replace("◎片  长","").strip()
            movie_infos["导演"] = details_movie.xpath("//p[16]/text()")[0].replace("◎导  演","").strip()
            for i in range(17,100):
                if  details_movie.xpath("//p[{}]/text()".format(i))[0].endswith("◎简  介"):
                    break
                elif i == 17:
                    movie_actors.append(details_movie.xpath("//p[{}]/text()".format(i))[0].replace("◎主  演","").strip())
                elif details_movie.xpath("//p[{}]/text()".format(i))[0].startswith("◎简  介"):
                    movie_infos["简介"] = movie_profilo = details_movie.xpath("//p[{}]/text()".format(i+1))[0]
                else:
                    movie_actors.append(details_movie.xpath("//p[{}]/text()".format(i))[0].strip())
            movie_infos["主演"] = movie_actors
            print(movie_infos)
    

    BeautifulSoup4

    中文文档
    BeautifulSoup4也是一个HTML/XMl解析器,不过与lxml只会局部遍历不同,BeautifulSoup4是基于HTML DOM(Document Object Model)的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。

    解析工具与使用

    BeautifulSoup4第一个参数是被解析的文档字符串或是文件句柄,第二个参数用来标识怎样解析文档。如果如果第二个参数为空,那么Beautiful Soup根据当前系统安装的库自动选择解析器,解析器的优先数序: lxml,html5lib, Python标准库。而目前只有 lxml 解析器支持XML文档的解析

    不同解析工具的一些区别

    解析器 使用方法 优势 劣势
    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格式的文档 速度慢、不依赖外部扩展
    ##我们同text = "<a><b /></a>"为例子
    
    ################################################################
    BeautifulSoup("<a><b /></a>")
    # <html><head></head><body><a><b></b></a></body></html>
    #因为<b />不符合HTMl的标准,所以被解析为<b>标签
    BeautifulSoup("<a><b /></a>", "xml")
    # <?xml version="1.0" encoding="utf-8"?>
    # <a><b/></a>
    而xml可以解析xml,所以解析出来完全一样
    #################################################################
    #如果HTML文档是标准的,那么所有的HTML解析器,解析出来的都是一样的。
    BeautifulSoup("<a></p>", "lxml")
    # <html><body><a></a></body></html>
    BeautifulSoup("<a></p>", "html5lib")
    # <html><head></head><body><a><p></p></a></body></html>
    BeautifulSoup("<a></p>", "html.parser")
    # <a></a>
    

    使用

    from bs4 import BeautifulSoup
    text = """
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p>
    <p class="story">Once upon a time there were three little sisters; and their names were
    <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
    <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
    <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
    and they lived at the bottom of a well.</p>
    <p class="story">...</p>
    """
    ##我们也可以使用open()函数来获取文件句柄传入
    ########具体过程############
    #该文档会被编码从Unicode类型#
    ###########################
    soup = BeautifulSoup(text,"lxml") #实例化
    print(soup.prettify())  #prettify()方法是取得内容
    

    类型

    Tag

    Tag对象与XML或HTML原生文档中的tag相同,我们可以简单地把Tag理解为 HTML 中的一个个标签。那么,它自然就有了NameAttributes两个非常重要的属性:
    Name:每个tag都有自己的名字,我们通过tag.name来获取,例如:

    soup = BeautifulSoup(text,"lxml")
    print(soup.p)
    '''
    result:
    <p class="title"><b>The Dormouse's story</b></p>
    '''
    

    当然我们也可以使用tag.name = "text"来改变文档内容,但是,我们不建议。
    Attributes:我们可以使用tag["class"]tag.attrs来获取标签属性的值,例如:

    print(soup.p["class"])
    #['title']
    print(soup.p.attrs)
    #{'class': ['title']}
    

    另外我们可以像操作字典一样添加、修改甚至删除tag的属性,但是,我们不建议。
    另外有一些标签具有多重属性,我们获取的时候会返回一个数组,例如:

    css_soup = BeautifulSoup('<p class="body strikeout"></p>')
    css_soup.p['class']
    # ["body", "strikeout"]
    

    如果某个属性看起来好像有多个值,但在任何版本的HTML定义中都没有被定义为多值属性,那么Beautiful Soup会将这个属性作为字符串返回,例如:

    id_soup = BeautifulSoup('<p id="my id"></p>')
    id_soup.p['id']
    # 'my id'
    

    如果拿到标签后,还想获取标签中的内容,那么可以通过tag.string等来获取标签中的文字。一个NavigableString字符串与Python中的Unicode字符串相同,并且还支持包含在遍历文档树和搜索文档树中的一些特性。

    soup = BeautifulSoup(text,"lxml")
    print(type(soup.strings))
    

    BeautifulSoup

    BeautifulSoup对象表示的是一个文档的全部内容。大部分时候,可以把它当作Tag对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法。因为 BeautifulSoup对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的属性是很方便的,所以BeautifulSoup 对象包含了一个值为"[document]"的特殊属性.name

    Comment

    markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>"
    soup = BeautifulSoup(markup)
    comment = soup.b.string
    type(comment)
    # <class 'bs4.element.Comment'>
    

    子节点

    一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点。Beautiful Soup提供了许多操作和遍历子节点的属性。

    使用tag的名字

    soup = BeautifulSoup(text,"lxml")
    print(soup.p.b)
    #<b>The Dormouse's story</b>
    

    .contents.children

    .contents属性可以将tag的子节点以列表的方式输出,另外字符串没有该属性:

    e_text = """
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p>
    """
    soup = BeautifulSoup(e_text,"lxml")
    print(soup.contents)
    '''
    result:
    [<html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p>
    </body></html>]
    '''
    

    通过tag的.children生成器,可以对tag的子节点进行循环:

    e_text = """
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p>
    """
    soup = BeautifulSoup(e_text,"lxml")
    for s in soup.children:
        print(s)
    '''
    result:
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p>
    </body></html>
    '''
    

    .descendants

    .descendants属性可以对所有tag的子孙节点进行递归循环。

    .string

    获取该标签的内容,但是只能有一个文本内容,例如:

    e_text = """
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p></html>
    """
    soup = BeautifulSoup(e_text,"lxml")
    print(soup.head.string)
    '''
    result:
    The Dormouse's story
    '''
    

    .stringsstripped_strings

    .strings可以获取所有的内容,stripped_strings不仅可以获取所有的内容,还可以去掉所有的空格,它们都返回数组:

    e_text = """
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p></html>
    """
    soup = BeautifulSoup(e_text,"lxml")
    print(list(soup.strings))
    '''
    result:["The Dormouse's story", '
    ', "The Dormouse's story", '
    ']
    '''
    
    e_text = """
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p></html>
    """
    soup = BeautifulSoup(e_text,"lxml")
    print(list(soup.stripped_strings))
    '''
    ["The Dormouse's story", "The Dormouse's story"]
    '''
    

    父节点

    我们可以通过.parent.parents获取某个元素的父节点,第一个只能获取第一层的,而第二个可以获取所有的父节点:

    e_text = """
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p></html>
    """
    soup = BeautifulSoup(e_text,"lxml")
    child_node = soup.b
    print(child_node,child_node.parent)
    '''
    result:<b>The Dormouse's story</b> <p class="title"><b>The Dormouse's story</b></p>
    '''
    
    e_text = """
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p></html>
    """
    soup = BeautifulSoup(e_text,"lxml")
    child_node = soup.b
    print(child_node)
    for c_n in child_node.parents:
        print(c_n)
    '''
    result:
    <b>The Dormouse's story</b>
    <p class="title"><b>The Dormouse's story</b></p>
    <body>
    <p class="title"><b>The Dormouse's story</b></p></body>
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p></body></html>
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p></body></html>
    '''
    

    搜索文档树

    findfind_all方法

    find方法找到第一个满足条件的标签后就会返回,find_all方法会找寻所有满足条件的标签,然后全部返回。find_all()方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量。我们仅以find_all方法为例:

    e_text = """
    <html><head><title>The Dormouse's story</title></head><body>
    <p class="title"><b>The Dormouse's story</b></p></html>
    """
    soup = BeautifulSoup(e_text,"lxml")
    res = soup.find_all("p",class_ = "title") #当遇到class时,使用"class_"
    #res = soup.find_all("p",attrs = {"class":"title"})
    print(res)
    #[<p class="title"><b>The Dormouse's story</b></p>]
    

    CSS选择器

    有时候使用css选择器的方式可以更加的方便。使用css选择器的语法,应该使用select方法。

    1.通过标签名查找:

    print(soup.select('a'))
    

    2.通过类名查找:
    通过类名,则应该在类的前面加一个.。比如要查找class=sister的标签

    print(soup.select('.sister'))
    

    3.通过id查找:
    通过id查找,应该在id的名字前面加一个#号。

    print(soup.select("#link1")
    

    4.组合查找:
    组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的。

    print(soup.select("p #link1"))
    

    直接子标签查找,则使用 > 分隔:

    print(soup.select("head > title"))
    

    5.通过属性查找:
    查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。

    print(soup.select('a[href="http://example.com/elsie"]'))
    

    6.以上的select方法返回的结果都是列表形式,可以遍历形式输出,然后用get_text()方法来获取它的内容。

    soup = BeautifulSoup(html, 'lxml')
    print(type(soup.select('title')))
    print(soup.select('title')[0].get_text())
    
    for title in soup.select('title'):
        print(title.get_text())
    
  • 相关阅读:
    173. Binary Search Tree Iterator
    199. Binary Tree Right Side View
    230. Kth Smallest Element in a BST
    236. Lowest Common Ancestor of a Binary Tree
    337. House Robber III
    449. Serialize and Deserialize BST
    508. Most Frequent Subtree Sum
    513. Find Bottom Left Tree Value
    129. Sum Root to Leaf Numbers
    652. Find Duplicate Subtrees
  • 原文地址:https://www.cnblogs.com/MingleYuan/p/10701123.html
Copyright © 2011-2022 走看看