• Python网络爬虫之BeautifulSoup模块


    一.介绍:

    Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.你可能在寻找 Beautiful Soup3 的文档,Beautiful Soup 3 目前已经停止开发,官网推荐在现在的项目中使用Beautiful Soup 4, 移植到BS4。安装步骤如下:

    #安装 Beautiful Soup  我们在爬虫中一般推荐使用lxml解析器,因为其爬取效率比较高
    pip install beautifulsoup4
    
    #安装解析器
    Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml .根据操作系统不同,可以选择下列方法来安装lxml:
    
    $ apt-get install Python-lxml
    
    $ easy_install lxml
    
    $ pip install lxml
    
    另一个可供选择的解析器是纯Python实现的 html5lib , html5lib的解析方式与浏览器相同,可以选择下列方法来安装html5lib:
    
    $ apt-get install Python-html5lib
    
    $ easy_install html5lib
    
    $ pip install html5lib

    下表列出了主要的解析器,以及它们的优缺点,官网推荐使用lxml作为解析器,因为效率更高. 在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml或html5lib, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定.

    解析器使用方法优势劣势
    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格式的文档
    • 速度慢
    • 不依赖外部扩展

    中文文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html

    二.基本使用

    html_doc = """
    <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>
    """
    
    #基本使用:容错处理,文档的容错能力指的是在html代码不完整的情况下,使用该模块可以识别该错误。使用BeautifulSoup解析上述代码,能够得到一个 BeautifulSoup 的对象,并能按照标准的缩进格式的结构输出
    from bs4 import BeautifulSoup
    soup=BeautifulSoup(html_doc,'lxml') #具有容错功能
    res=soup.prettify() #处理好缩进,结构化显示
    print(res)

    1.通过标签寻定位,如果有多个标签,那么只返回第一个,看下面的例子:

    # 遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
    html_doc = """
    <html><head><title>The Dormouse's story</title></head>
    <body>
    <p id="my p" class="title"><b id="bbb" class="boldest">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>
    """
    
    # 1、用法
    from bs4 import BeautifulSoup
    
    soup = BeautifulSoup(html_doc, 'lxml')  # 第一个参数直接是一个标签文档字符串
    # soup=BeautifulSoup(open('a.html'),'lxml') # 第一个参数是一个文件句柄对象
    
    print(soup.p)  # 存在多个相同的标签则只返回第一个
    print(soup.a)  # 存在多个相同的标签则只返回第一个

    打印结果如下:

    print(soup.p) 上面有2个p标签,只返回找到的第一个p标签
    <p class="title" id="my p"><b class="boldest" id="bbb">The Dormouse's story</b></p>
    
    
    print(soup.a)  只返回找到的第一个a标签
    <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>

    2.获取标签的属性:

    # _*_ coding:utf-8 _*_
    # 遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
    html_doc = """
    <html><head><title>The Dormouse's story</title></head>
    <body>
    <p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b>111</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>
    """
    
    # 1、用法
    from bs4 import BeautifulSoup
    
    soup = BeautifulSoup(html_doc, 'lxml')  # 第一个参数直接是一个标签文档字符串
    # # soup=BeautifulSoup(open('a.html'),'lxml') # 第一个参数是一个文件句柄对象
    #
    # print(soup.p)  # 存在多个相同的标签则只返回第一个
    # # print(soup.a)  # 存在多个相同的标签则只返回第一个
    #
    # # 2、获取标签的名称
    # print(soup.p.name)  # 打印 p
    #
    # # 3、获取标签的属性
    # """
    # 打印找到的第一个p标签的属性,属性被封装为字典:  {'class': ['title'], 'id': 'my p'}
    # 原始找到的p标签为:<p class="title" id="my p"><b class="boldest" id="bbb">The Dormouse's story</b></p>
    # """
    # print(soup.p.attrs)
    
    # # 4、获取标签的内容
    """
    表示查看p标签下的文本,只寻找一个,如果里面有多个标签或者文本,则打印None
    当第一个p标签如下所示时:<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b></p>
    打印出The Dormouse's story
    当p标签如下所示时:<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b>
    </p>
    相当于多了一个换行符,那么就打印出None
    又或者当p标签如下所示时:<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b>111</p>
    此时有两个文本内容The Dormouse's story和111,此时soup不知道要找哪个,所以为None
    
    """
    # print(soup.p.string)  # p下的文本只有一个时,取到,否则为None
    # print(soup.p.strings)  # 拿到一个生成器对象, 取到p标签下下所有的文本内容
    # # 打印结果:<generator object _all_strings at 0x000002A324FAF3B8>
    # print(list(soup.p.strings))  # 打印结果: ["The Dormouse's story", '111']
    """
    查看soup文档对象中的所有标签文本: 使用stripped_strings去除很多空行:
    打印结果如下:
    The Dormouse's story
    The Dormouse's story
    111
    Once upon a time there were three little sisters; and their names were
    Elsie
    ,
    Lacie
    and
    Tillie
    ;
    and they lived at the bottom of a well.
    ...
    """
    # for line in soup.stripped_strings:  # 去掉空白
    #     print(line)
    
    
    '''
    如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None,如果只有一个子节点那么就输出该子节点的文本,比如下面的这种结构,soup.p.string 返回为None,但soup.p.strings就可以找到所有文本
    <p id='list-1'>
        哈哈哈哈
        <a class='sss'>
            <span>
                <h1>aaaa</h1>
            </span>
        </a>
        <b>bbbbb</b>
    </p>
    '''

    接着看下面的:

    # _*_ coding:utf-8 _*_
    # 遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
    html_doc = """
    <html><head><title>The Dormouse's story</title></head>
    <body>
    <p id="my p" class="title">
        aaa<b id="bbb" class="boldest">The Dormouse's story</b>ddd
        <ul>
            spark很重要
            <li>python</li>
        </ul>
    </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>
    """
    
    # 1、用法
    from bs4 import BeautifulSoup
    
    soup = BeautifulSoup(html_doc, 'lxml')  # 第一个参数直接是一个标签文档字符串
    # # soup=BeautifulSoup(open('a.html'),'lxml') # 第一个参数是一个文件句柄对象
    #
    # print(soup.p)  # 存在多个相同的标签则只返回第一个
    # # print(soup.a)  # 存在多个相同的标签则只返回第一个
    #
    # # 2、获取标签的名称
    # print(soup.p.name)  # 打印 p
    #
    # # 3、获取标签的属性
    # """
    # 打印找到的第一个p标签的属性,属性被封装为字典:  {'class': ['title'], 'id': 'my p'}
    # 原始找到的p标签为:<p class="title" id="my p"><b class="boldest" id="bbb">The Dormouse's story</b></p>
    # """
    # print(soup.p.attrs)
    
    # # 4、获取标签的内容
    """
    表示查看p标签下的文本,只寻找一个,如果里面有多个标签或者文本,则打印None
    当第一个p标签如下所示时:<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b></p>
    打印出The Dormouse's story
    当p标签如下所示时:<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b>
    </p>
    相当于多了一个换行符,那么就打印出None
    又或者当p标签如下所示时:<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b>111</p>
    此时有两个文本内容The Dormouse's story和111,此时soup不知道要找哪个,所以为None
    
    """
    # print(soup.p.string)  # p下的文本只有一个时,取到,否则为None
    # print(soup.p.strings)  # 拿到一个生成器对象, 取到p标签下下所有的文本内容
    # # 打印结果:<generator object _all_strings at 0x000002A324FAF3B8>
    # print(list(soup.p.strings))  # 打印结果: ["The Dormouse's story", '111']
    """
    查看soup文档对象中的所有标签文本: 使用stripped_strings去除很多空行:
    打印结果如下:
    The Dormouse's story
    The Dormouse's story
    111
    Once upon a time there were three little sisters; and their names were
    Elsie
    ,
    Lacie
    and
    Tillie
    ;
    and they lived at the bottom of a well.
    ...
    """
    # for line in soup.stripped_strings:  # 去掉空白
    #     print(line)
    
    
    '''
    如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None,如果只有一个子节点那么就输出该子节点的文本,比如下面的这种结构,soup.p.string 返回为None,但soup.p.strings就可以找到所有文本
    <p id='list-1'>
        哈哈哈哈
        <a class='sss'>
            <span>
                <h1>aaaa</h1>
            </span>
        </a>
        <b>bbbbb</b>
    </p>
    '''
    
    # 5、嵌套选择  打印结果:The Dormouse's story
    # print(soup.head.title.string)
    # 由于此时有三个a标签,但是这样嵌套寻找只找到第一个,所以只打印出Elsie
    # print(soup.body.a.string)
    
    
    # 6、子节点、子孙节点
    """
    打印结果如下所示:
    [<b class="boldest" id="bbb">The Dormouse's story</b>, '111']
    可以看到这样也只是定位到第一个p标签:
    <p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b>111</p>
    相当于打印的就是第一个p标签内部的所有元素,包括子节点和文本,我们再来测试下:
    <p id="my p" class="title">
        aaa<b id="bbb" class="boldest">The Dormouse's story</b>ddd
        <ul>
            spark很重要
            <li>python</li>
        </ul>
    </p>
    
    这是文档中的第一个p标签,那么print(soup.p.contents)的打印结果如下:
    ['
        aaa', <b class="boldest" id="bbb">The Dormouse's story</b>, 'ddd
        ']
    可以看到它找到的是第一个子标签和子文本
    """
    # print(soup.p.contents) #p下所有子节点
    # print(soup.p.children) #得到一个迭代器,包含p下所有子节点
    # print(list(soup.p.children))
    # <list_iterator object at 0x000001FD6AA47A20>
    # ['
        aaa', <b class="boldest" id="bbb">The Dormouse's story</b>, 'ddd
        ']
    """
    打印结果如下:
    0 
        aaa
    1 <b class="boldest" id="bbb">The Dormouse's story</b>
    2 ddd
    
    """
    # for i,child in enumerate(soup.p.children):
    #     print(i,child)
    
    """
    打印结果如下:
    <generator object descendants at 0x0000017D7863F3B8>
    ['
        aaa', <b class="boldest" id="bbb">The Dormouse's story</b>, "The Dormouse's story", 'ddd
        ']
    """
    # print(soup.p.descendants)  # 获取子孙节点,p下所有的标签都会选择出来
    # print(list(soup.p.descendants))
    # for i, child in enumerate(soup.p.descendants):
    #     print(i, child)
    #
    # #7、父节点、祖先节点
    # print(soup.a.parent)  # 获取a标签的父节点,其父节点是p标签,所以打印结果如下:
    """
    <p class="story">Once upon a time there were three little sisters; and their names were
    <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
    <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
    <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
    and they lived at the bottom of a well.</p>
    """
    # print(soup.a.parents)  # 找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...
    # print(list(soup.a.parents))
    
    # 8、兄弟节点
    # print('=====>')
    # print(soup.a.next_sibling) #下一个兄弟
    # print(soup.a.previous_sibling) #上一个兄弟
    #
    print(list(soup.a.next_siblings)) #下面的兄弟们=>生成器对象
    print(soup.a.previous_siblings) #上面的兄弟们=>生成器对象

    继续看bs中的五种过滤器

    # _*_ coding:utf-8 _*_
    
    # 搜索文档树:BeautifulSoup定义了很多搜索方法,这里着重介绍2个: find() 和 find_all() .其它方法的参数和用法类似
    html_doc = """
    <html><head><title>The Dormouse's story</title></head>
    <body>
    <p id="my p" class="title"><b id="bbb" class="boldest">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>
    """
    
    from bs4 import BeautifulSoup
    
    soup = BeautifulSoup(html_doc, 'lxml')
    
    # 1、五种过滤器: 字符串、正则表达式、列表、True、方法
    # 1.1、字符串:即标签名
    # print(soup.find_all('b'))
    """
    结果是一个列表:
    [<b class="boldest" id="bbb">The Dormouse's story</b>]
    """
    
    # 1.2、正则表达式
    import re
    #
    # print(soup.find_all(re.compile("^b")))  # 找出b开头的标签,结果有body和b标签
    """
    打印结果如下:
    [<body>
    <p class="title" id="my p"><b class="boldest" id="bbb">The Dormouse's story</b>
    </p>
    <p class="story">Once upon a time there were three little sisters; and their names were
    <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
    <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
    <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
    and they lived at the bottom of a well.</p>
    <p class="story">...</p>
    </body>, <b class="boldest" id="bbb">The Dormouse's story</b>]
    
    
    """
    #
    # # 1.3、列表:如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签:
    # print(soup.find_all(['a', 'b']))
    """
    返回结果仍然是一个列表:
    [<b class="boldest" id="bbb">The Dormouse's story</b>,
     <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
      <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, 
    <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
    """
    #
    # # 1.4、True:可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
    # print(soup.find_all(True))
    # 例如我再需要查找到文档中所有包含id的标签:
    # print(soup.find_all(id=True))
    """
    返回结果如下,是一个列表:
    [<p class="title" id="my p"><b class="boldest" id="bbb">The Dormouse's story</b>
    </p>, <b class="boldest" id="bbb">The Dormouse's story</b>, 
    <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, 
    <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, 
    <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
    """
    """
    找到文档中的所有标签:
    
    """
    # for tag in soup.find_all(True):
    #     print(tag.name)
    #
    #
    # # 1.5、方法:如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
    """
    下面方法表示寻找到标签中有class属性,但是没有id属性的标签,打印结果如下:
    [<p class="story">Once upon a time there were three little sisters; and their names were
    <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
    <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
    <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
    and they lived at the bottom of a well.</p>, <p class="story">...</p>]
    可以看到两个p标签只有class属性,但是没有id属性
    """
    def has_class_but_no_id(tag):
        return tag.has_attr('class') and not tag.has_attr('id')
    
    print(soup.find_all(has_class_but_no_id))

    重点方法之find_all( name , attrs , recursive , text , **kwargs )



  • 相关阅读:
    react 编写日历组件
    腾讯地图打开地图选取位置 withMap
    react 动态修改 document.title
    axios 中断请求
    react 使用 react-loadable分包
    http Authorization
    @media screen媒体查询
    CSS规范
    布局和WEB性能
    CSS 标签的分类,及显示模式
  • 原文地址:https://www.cnblogs.com/pyspark/p/8277231.html
Copyright © 2020-2023  润新知