zoukankan      html  css  js  c++  java
  • Beautiful Soup库

    一.Beautiful Soup的基本概念

    Beautiful Soup库是一个强大的基于Python语言的XML和HTML解析库,可以同它来方便的从网页中提取数据。
    Beautiful Soup提供了一些简单的函数来处理导航、搜索、修改分析树等功能,他是一个工具箱,通过解析文档为用户提供需要抓取的数据。
    Beautiful Soup自动将输入的文档转化为Unicode编码,输出文档转化为UTF-8编码,所以在使用的过程不需要考虑编码的问题,除非文档没有指定编码方式,这时只需要指定输入文档的编码方式即可。

    二.安装Beautiful Soup库

    Beautiful Soup的HTML和XML解析器依赖于lxml库,所以在安装Beautiful Soup库之前,确保本机安装了lxml库。

    1.windows在线安装

    pip install beautifulsoup4
    

    2.离线安装

    网址中找到Beautiful Soup的whl文件,在cmd命令行中cd到该解压的文件夹中;执行如下命令

    pip install beautifulsoup4‑4.9.3‑py3‑none‑any.whl
    

    三.Beautiful Soup库的基本使用方法

    3.1首先创建BeautifulSoup对象,BeautifulSoup类的第一个参数需要指定待分析的HTML代码,第二个参数指定lxml解析器(第二个参数加引号(''))。在解析html的过程中,BeautifulSoup对象会将HTML中的各个级别的标签映射成BeautifulSoup对象中同级别的属性,所以就可以直接通过BeautifulSoup对象的属性提取HTML代码中的内容。

    from bs4 import BeautifulSoup
    
    html = '''
    <html>
        <head><title>这是一个演示页面</title></head>
        <body>
            <a href='a.html'>第一页</a>
            <p>
            <a href='b.html'>第二页</a>
        </body>
    </html>
    '''
    # 创建BeautifulSoup对象,BeautifulSoup类的第一个参数需要指定待分析的HTML代码,第二个参数指定lxml解析器(第二个参数加引号(''))
    soup = BeautifulSoup(html,'lxml')
    # 获取<title>标签的文本
    print('<' + soup.title.string + '>')
    # 获取第一个<a>标签的href属性
    print('[' + soup.a["href"]+ ']')
    # 以格式化后的格式输出这段HTML代码
    print(soup.prettify())
    
    

    四.节点选择器

    节点选择器通过节点的名称选取节点,然后再用string属性就可以得到节点内的文本,这种选择方式非常快。

    4.1这里的知识点是:

    获取节点的名称:name
    获取节点的属性:attrs
    获取节点的内容:string

    from bs4 import BeautifulSoup
    
    html = '''
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Beautiful Soup演示</title>
    </head>
    <body>
    <div>
        <ul>
            <li class="item1" value1="1234" value2 = "hello world"><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>
            <li class="item4" ><a href="https://www.microsoft.com">微软</a></li>
            <li class="item5"><a href="https://www.google.com">谷歌</a></li>
        </ul>
    </div>
    </body>
    </html>
    '''
    
    soup = BeautifulSoup(html,'lxml')
    # 获取节点tite的名称
    print(soup.title.name)
    # 获取第一个节点的所有属性名和属性值
    print(soup.li.attrs)
    # 获取第一个li节点的value2属性的值
    print(soup.li.attrs["value2"])
    print(soup.li["value1"])
    # 获取第一个a节点的href属性的值
    print(soup.a['href'])
    # 获取第一个a标签的文本内容
    print(soup.a.string)
    

    4.2嵌套选择节点

    通过BeautifulSoup对象的属性获得的每一个节点都是一个bs4.element.Tag对象。在该对象的基础上同样可以使用节点选择器进行下一步的选择。也就是继续选择Tag对应的节点的子节点,这也可以称为嵌套选择

    from bs4 import BeautifulSoup
    
    html = '''
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Beautiful Soup演示</title>
    </head>
    <body>
    <div>
        <ul>
            <li class="item1"><a href="https://www.jd.com"> 京东商城</a></li>        
        </ul>
    </div>
    </body>
    </html>
    '''
    
    soup = BeautifulSoup(html,'lxml')
    print(soup.head)
    print(type(soup.head))
    head = soup.head
    print(head.title.string)
    print(soup.body.div.ul.li.a['href'])
    

    4.3选择子节点

    1.直接获取子节点
    通过contents和children数学,其中contents属性返回一个列表(list类的实例),children属性返回list_iterator类的实例,这是一个可迭代对象,该对象可以使用for循环进行迭代。总之,他们都返回一个列表(contents是列表对象,children是可迭代的对象,本质上是一个列表,只是不能直接输出)。每一个列表元素是一个字符串形成的子节点。
    2.获取所有子孙节点
    如果想要获取所有的子孙节点,需要使用descendants属性,该属性返回一个产生器(generator),需要使用for循环进行迭代才可以输出产生器的值。

    from bs4 import BeautifulSoup
    
    html = '''
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Beautiful Soup演示</title>
        <tag1><a><b></b></a></tag1>
    </head>
    <body>
    <div>
        <ul>
            <li class="item1" value = "hello world">
                <a href="https://geekori.com"> 
                    geekori.com
                </a>
            </li>
            <li class="item2"><a href="https://www.jd.com"> 京东商城</a></li>
    
        </ul>
    </div>
    </body>
    </html>
    '''
    
    soup = BeautifulSoup(html,'lxml')
    # 输出head的所有直接子节点
    print(soup.head.contents)
    print(soup.head.children)
    print(type(soup.head.contents))
    print(type(soup.body.div.ul.children))
    print(type(soup.head.descendants))
    # 对ul中的子节点进行迭代,并以文本形式输出子节点的内容
    for i, child in enumerate(soup.body.div.ul.contents):
        print(i,child)
    print('----------')
    i = 1
    for child in soup.body.div.ul.children:
        print('<', i, '>',child, end=" ")
        i += 1
    print('----------')
    # 对ul中的子孙节点进行迭代,并以文本形式输出子节点的内容
    for i, child in enumerate(soup.body.div.ul.descendants):
        print('[',i,']',child)
    

    4.4选择父节点

    如果要选取某一个节点的直接父节点,需要使用parent属性,如果要选取某个节点的所有父节点,需要使用parents属性。parent属性返回当前节点的父节点的Tag对象,而parents属性会返回一个可迭代对象,通过for循环可以对该对象进行迭代,并获得当前节点所有的父节点对应的Tag对象。

    from bs4 import BeautifulSoup
    
    html = '''
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Beautiful Soup演示</title>
        <tag1><xyz><b></b></xyz></tag1>
    </head>
    <body>
    <div>
        <ul>
            <li class="item1" value = "hello world">
                <a href="https://geekori.com"> 
                    geekori.com
                </a>
            </li>
            <li class="item2"><a href="https://www.jd.com"> 京东商城</a></li>
    
        </ul>
    </div>
    </body>
    </html>
    '''
    
    soup = BeautifulSoup(html,'lxml')
    print(soup.a.parent)
    print(soup.a.parent['class'])
    
    print(soup.a.parents)
    for parent in soup.a.parents:
        print('<',parent.name,'>')
    

    4.5选择兄弟节点

    同级节点也称兄弟节点,可以通过next_sibling属性获得当前节点的下一个兄弟节点,通过previous_sibling属性获得当前节点的上一个兄弟节点。通过next_siblings属性获得当前节点的后面所有的个兄弟节点(返回一个可迭代对象),通过previous_sibling属性获得当前节点前面所有的兄弟节点(返回一个可迭代对象)。
    注意:如果两个节点之间有换行符或其他文本,那么这些属性也同样会返回这些文本节点,节点之间的文本将作为一个文本节点处理。

    from bs4 import BeautifulSoup
    
    html = '''
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Beautiful Soup演示</title>
    </head>
    <body>
    <div>
        <ul>
            <li class="item1" value1="1234" value2 = "hello world">
                <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>
            <li class="item4" ><a href="https://www.microsoft.com">微软</a></li>
            <li class="item5"><a href="https://www.google.com">谷歌</a></li>
        </ul>
    </div>
    </body>
    </html>
    '''
    
    soup = BeautifulSoup(html,'lxml')
    secondli = soup.li.next_sibling.next_sibling
    print('第1个li节点的下一个li节点:',secondli)
    print('第2个li节点的上一个li节点的class属性值:', secondli.previous_sibling.previous_sibling['class'])
    for sibling in secondli.next_siblings:
        print(type(sibling))
        if str.strip(sibling.string) == "":
            print('换行')
        else:
            print(sibling)
    

    五.方法选择器

    前面讲的方法都是通过属性来选择节点的,对于比较简单的选择,这种方法使用起来非常方便快捷,但对于比较复杂的选择,这种方法就显得比较笨拙,不够灵活。Beautiful Soup还提供了一些查询方法,如find_all、find等,调用这些方法,然后传入相应的参数,就可以灵活选择节点了。

    5.1 find_all方法

    find_all方法用于根据节点名、属性、文本内容等选择符合要求的节点。
    find_all方法的原型如下:

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

    常用的参数包括name、attrs和text,下面分别介绍这几个参数的用法

    #5.1.1name参数

    name参数用于指定节点名,find_all方法会选取所有节点名与name参数值相同的节点,find_all方法返回一个bs4.element.ResultSet对象,该对象是可迭代的,可以通过迭代
    获取每一个符合条件的节点(Tag对象);find_all方法属于Tag对象,由于BeautifulSoup是Tag的子类,所以find_all方法在beautifulSoup对象上
    也是可以调用的。因此,对ResultSet对象迭代未获得的每一个Tag对象仍然可以继续使用find_all方法以当前Tag对象的节点作为根开始继续选取节点
    这种方式称为嵌套查询
    本例通过find_all方法的name参数选择所有的ul节点,然后对每个ul节点继续使用find_all方法搜索当前ul节点下的所有li节点

    from bs4 import BeautifulSoup
    
    html = '''
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Beautiful Soup演示</title>
    </head>
    <body>
    <div>
        <ul>
            <li class="item1" value1="1234" value2 = "hello world"><a href="https://geekori.com"> geekori.com</a></li>
            <li class="item2"><a href="https://www.jd.com"> 京东商城</a></li>        
        </ul>
        <ul>
        <li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
            <li class="item4" ><a href="https://www.microsoft.com">微软</a></li>
            <li class="item5"><a href="https://www.google.com">谷歌</a></li>
        </ul>
    </div>
    </body>
    </html>
    '''
    
    soup = BeautifulSoup(html,'lxml')
    ulTags = soup.find_all(name='ul')
    
    print(type(ulTags))
    for ulTag in ulTags:
        print(ulTag)
    print('---------------------')
    for ulTag in ulTags:
        liTags = ulTag.find_all(name='li')
        for liTag in liTags:
            print(liTag)
    

    5.1.2attrs参数

    除了可以根据节点名查询外,还可以使用attrs参数通过节点的属性查找。attrs参数是一个字典类型,key是节点属性名,value是节点属性值。下面是一个使用attrs参数的例子

    soup.find_all(attrs={'id':'button1'}) # 选择所有id属性值为button1的节点
    

    如果是常用的属性,如,id和class,可以直接将这些属性作为命名参数传入find_all方法,而不必使用attrs参数。
    在指定class属性值时,由于class是Python的关键字,所以在为find_all方法指定class参数时,需要在class后面加一个下画线(),也就是指定"class"参数

    from bs4 import BeautifulSoup
    
    html = '''
    <div>
        <ul>
            <li class="item1" value1="1234" value2 = "hello world"><a href="https://geekori.com"> geekori.com</a></li>
            <li class="item"><a href="https://www.jd.com"> 京东商城</a></li>        
        </ul>
        <button id="button1">确定</button>
        <ul>
            <li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
            <li class="item" ><a href="https://www.microsoft.com">微软</a></li>
            <li class="item2"><a href="https://www.google.com">谷歌</a></li>
        </ul>
    </div>
    
    '''
    
    soup = BeautifulSoup(html,'lxml')
    tags = soup.find_all(attrs={"class":"item"})
    for tag in tags:
        print(tag)
    
    tags = soup.find_all(class_='item2')
    print(tags)
    tags = soup.find_all(id='button1')
    print(tags)
    

    5.1.3text参数

    通过text参数可以搜索匹配的文本节点,传入的参数可以是字符串,也可以是正则表达式对象

    from bs4 import BeautifulSoup
    import re
    html = '''
    <div>
        <xyz>Hello World, what's this?</xyz>
        <button>Hello, my button. </button>
        <a href='https://geekori.com'>geekori.com</a>
    </div>
    
    '''
    
    soup = BeautifulSoup(html,'lxml')
    tags = soup.find_all(text='geekori.com')
    print(tags)
    tags = soup.find_all(text=re.compile('Hello'))
    print(tags)
    

    5.2find方法

    find方法与find_all方法有如下几点不同
    1.find方法用于查询满足条件的第1个节点,而find_all方法用于查询所有满足条件的节点
    2.find_all方法返回bs4.element.ResultSet对象,而find方法返回的是bs4.element.Tag对象。
    find方法和find_all方法的参数和使用方法完全相同
    本例同时使用find_all方法和find方法根据相同的查询条件查询节点,并输出各自的查询结果

    from bs4 import BeautifulSoup
    
    html = '''
    <div>
        <ul>
            <li class="item1" value1="1234" value2 = "hello world">
                  <a href="https://geekori.com"> geekori.com</a>
            </li>
            <li class="item"><a href="https://www.jd.com"> 京东商城</a></li>        
        </ul>
        <ul>
            <li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
            <li class="item" ><a href="https://www.microsoft.com">微软</a></li>
            <li class="item2"><a href="https://www.google.com">谷歌</a></li>
        </ul>
    </div>
    
    '''
    
    soup = BeautifulSoup(html,'lxml')
    tags = soup.find(attrs={"class":"item"})
    print(type(tags))
    print(tags)
    print('----------------')
    tags = soup.find_all(attrs={"class":"item"})
    print(type(tags))
    for tag in tags:
        print(tag)
    

    六.CSS选择器(select()方法在我的环境下失效)

    6.1基本用法

    使用CSS选择器需要使用Tag对象的select方法,该方法接收一个字符串类型的CSS选择器。常用的CSS选择器有如下几个:
    (1).classname:选取样式值为classname的节点,也就是class属性值是classname的节点
    (2)nodename:选取节点名为nodename的节点
    (3)#idname:选取id属性值为idname的节点
    本例使用CSS选择器根据class属性、节点名称和id属性查询特定的节点

    from bs4 import BeautifulSoup
    
    html = '''
    <div>
        <ul>
            <li class="item1" value1="1234" value2 = "hello world"><a href="https://geekori.com"> geekori.com</a></li>
            <li class="item"><a href="https://www.jd.com"> 京东商城</a></li>        
        </ul>
        <button id="button1">确定</button>
        <ul>
            <li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
            <li class="item" ><a href="https://www.microsoft.com">微软</a></li>
            <li class="item2"><a href="https://www.google.com">谷歌</a></li>
        </ul>
    </div>
    
    '''
    
    soup = BeautifulSoup(html,'lxml')
    tags = soup.select('.item')
    for tag in tags:
        print(tag)
    
    tags = soup.select('#button1')
    print(tags)
    tags = soup.select('a')[2:]
    for tag in tags:
        print(tag)
    

    6.2嵌套选择器

    CSS选择器与节点选择器一样,同样可以嵌套调用。也就是在通过CSS选择器选取一些节点后,可以在这些节点的基础上继续使用CSS选择器。
    本例将CSS选择器与方法选择器混合使用来选取特定的节点

    from bs4 import BeautifulSoup
    html = '''
    <div>
        <ul>
            <li class="item1" value1="1234" value2 = "hello world"><a href="https://geekori.com"> geekori.com</a></li>
            <li class="item">
               <a href="https://www.jd.com"> 京东商城</a>
               <a href="https://www.google.com">谷歌</a>
            </li>        
        </ul>
        <ul>
            <li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
            <li class="item" ><a href="https://www.microsoft.com">微软</a></li>
        </ul>
    </div>
    
    '''
    
    soup = BeautifulSoup(html,'lxml')
    tags = soup.select('.item')
    print(type(tags))
    for tag in tags:
        aTags = tag.select('a')
        for aTag in aTags:
            print(aTag)
    
    print('---------')
    for tag in tags:
        aTags = tag.find_all(name='a')
        for aTag in aTags:
            print(aTag)
    

    6.3获取属性值和文本

    由于select方法同样会返回Tag对象的集合,所以可以使用Tag对象的方式获取节点属性值和文本内容。获取属性值可以使用attrs,也可以直接使用[...]方式引用节点的属性。获取节点的文本内容可以使用get_text方法,也可以使用string属性
    本例使用CSS选择器选取特定的a节点,并获取a节点的href属性值和文本内容。

    from bs4 import BeautifulSoup
    html = '''
    <div>
        <ul>
            <li class="item1" value1="1234" value2 = "hello world">
                <a href="https://geekori.com"> geekori.com</a>
            </li>
            <li class="item">
               <a href="https://www.jd.com"> 京东商城</a>
               <a href="https://www.google.com">谷歌</a>
            </li>        
        </ul>
        <ul>
            <li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
            <li class="item" ><a href="https://www.microsoft.com">微软</a></li>
        </ul>
    </div>
    
    '''
    
    soup = BeautifulSoup(html,'lxml')
    tags = soup.select('.item')
    print(type(tags))
    for tag in tags:
        aTags = tag.select('a')
        for aTag in aTags:
            print(aTag['href'],aTag.get_text())
    
    print('---------')
    for tag in tags:
        aTags = tag.find_all(name='a')
        for aTag in aTags:
            print(aTag.attrs['href'],aTag.string)
    

    6.4通过浏览器获取CSS选择器代码

    在网页上,单击右键菜单中的"检查"命令,会显示开发者工具,在Elements选项卡中定位到要获取CSS选择器代码的节点。
    本例使用requests库抓取京东商城首页的HTML代码,并使用CSS选择器获取导航条的链接文本。

    import requests
    from bs4 import BeautifulSoup
    result = requests.get('https://www.jd.com')
    soup = BeautifulSoup(result.text,'lxml')
    
    aTag = soup.select('#navitems-group1 > li.fore1 > a')
    print(aTag)
    print(aTag[0].string,aTag[0]['href'])
    print('---------------')
    group1 = soup.select('#navitems-group1')
    group2 = soup.select('#navitems-group2')
    group3 = soup.select('#navitems-group3')
    
    
    for value in group1:
        aTags = value.find_all(name="a")
        for aTag in aTags:
            print(aTag.string)
    
    for value in group2:
        aTags = value.find_all(name="a")
        for aTag in aTags:
            print(aTag.string)
    for value in group3:
        aTags = value.find_all(name="a")
        for aTag in aTags:
            print(aTag.string)
    

    补充

    上面只介绍了bs4对象中四种中的一种tag对象,且介绍了它的name,attrs属性和方法,还有select选择器(支持大部分的CSS选择器)
    还有其他3种对象分别是
    1.NavigableString(使用.string来提取字符串数据;使用replace_with()方法替换成其他的字符串)
    2.BeautifulSoup(Beautiful对象是一个文档的全部内容,可以把他当作tag对象,它的.name属性是很有用的,烦恼会一个'docunment'字符串)
    3.Comment(用于判断输入的字符串是不是注释部分的内容,注释内容的字符串类型返回一个bs4.element.Comment对象,这个对象等于type(comment))##comment为返回的字符串对象

    多节点的情况可以使用.strings属性循环获取内容

    for string in soup.strings:
      print(repr(string))
    

    repr() 函数将对象转化为供解释器读取的形式。

    >>>s = 'RUNOOB'
    >>> repr(s)
    "'RUNOOB'"
    
    努力拼搏吧,不要害怕,不要去规划,不要迷茫。但你一定要在路上一直的走下去,尽管可能停滞不前,但也要走。
  • 相关阅读:
    HashTable源码浅析(基于jdk1.8.0_231)
    LinkedHashMap源码浅析(基于jdk1.8.0_231)
    SortedSet接口源码浅析(基于jdk1.8.0_231)
    NavigableSet接口源码浅析(基于jdk1.8.0_231)
    TreeSet源码浅析(基于jdk1.8.0_231)
    TreeMap源码浅析(基于jdk1.8.0_231)
    Map接口源码解析(基于jdk1.8.0_231)
    Arrays工具类源码详解(基于jdk1.8.0_231)
    Collections源码详解(基于jdk1.8.0_231)
    BitSet源码详解 (基于jdk1.8.0.231)
  • 原文地址:https://www.cnblogs.com/wkhzwmr/p/15230635.html
Copyright © 2011-2022 走看看