美文网首页
爬虫基础:Beautiful Soup

爬虫基础:Beautiful Soup

作者: 田小田txt | 来源:发表于2018-12-30 17:35 被阅读0次

    Beautiful Soup 是一个可以从HTML和XML文件中提取数据的Python。它可以实现文档的增删改查操作,我们侧重点是它的查询操作。

    安装 Beautiful Soup

    你可以根据自己的系统选择下面的安装代码进行安装操作:

    $ apt-get install Python-bs4
    $ easy_install beautifulsoup4
    $ pip install beautifulsoup4
    
    

    安装解析器

    Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml。根据操作系统不同,你可以选择下面方法来安装 lxml:

    $ apt-get install Python-lxml
    $ easy_install lxml
    $ pip install lxml
    
    

    另外一个可供选择的解析器是纯Python实现的 html5lib,解析方式和浏览器相同,你可以选择下面的方法来安装 html5lib:

    $ apt-get install Python-html5lib
    $ easy_install html5lib
    $ pip install html5lib
    
    

    几种解析器的优缺点

    解析器 使用方法 优势 劣势
    Python标准库 BeautifulSoup(markup, "html.parser") 执行速度适中,文档容错能力强 Python2.7.3 or 3.2.3前的版本文档容错能力差
    lxml HTML 解析器 BeautifulSoup(markup, "lxml") 速度快,文档容错能力强 需要安装C语言库
    lxml XML 解析器 BeautifulSoup(markup, "xml") 速度快,唯一支持XML的解析器 需要安装C语言库
    html5lib BeautifulSoup(markup, "html5lib")
    最高的容错性,以浏览器的方式解析文档,生成HTML5格式的文档 数独慢,不依赖外部扩展

    推荐使用 lxml 作为解析器,因为效率更高。在Python2.7.3之前的版本和Python3.2.3之前的版本,必须安装 lxml 或 html5lib,因为Python版本的标准库中的内置的 HTML 解析方法不够稳定。

    如何使用

    将一段文档传入 Beautiful Soup 的构造方法,就能得到一个文档的对象,可以传入一段文字或一个文件句柄。

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(open("index.html"))    // 文件句柄
    soup = BeautifulSoup("<html>data</html>")   // 文档
    
    

    文档被转换成 Unicode,并且HTML的实例都被转换成 Unicode 编码,然后,Beautiful Soup选择最合适的解析器来解析这段文档,如果手动指定解析器,那么 Beautiful Soup 会选择指定的解析器来解析文档。

    对象的种类

    Beautiful Soup 将复杂 HTML 文档转换成一个复杂的树形结构,每隔节点都是Python对象,多有对象可以归纳为4种:TagNavigableStringBeautifulSoupComment

    Tag对象

    Tag 对象与 XML 或 HTML 原生文档中的 tag 相同。Tag 有很多方法和属性,其中最重要的属性:name 和 attributes。

    from bs4 import BeautifulSoup
    soup = BeautifulSoup('<b class="boldest">Extremely bold</b>','lxml')
    tag = soup.b            // 一个 Tag 对象
    print(type(tag))        // <class 'bs4.element.Tag'>
    print(tag.name)         // tag都有自己的名字:b
    print(tag.attrs)        // tag的属性字典:{'class': ['boldest']}
    print(tag['class'])     // 属性字典中的'class'值:['boldest']
    
    // tag 的属性可以被添加,删除或修改,属性操作和字典一样
    tag['class'] = 'myClsss'
    print(tag)              // <b class="myClsss">Extremely bold</b>
    tag['id'] = 'custemId'
    print(tag)              // <b class="myClsss" id="custemId">Extremely bold</b>
    
    

    NavigableString对象

    字符串常被包含在 tag 内。Beautiful Soup 用 NavigableString 类来包装 tag 中的字符串。

    print(type(tag.string))     // <class 'bs4.element.NavigableString'>
    print(tag.string)           // Extremely bold
    
    

    字符串不支持 .contents 或 .string 属性或 find() 方法。

    BeautifulSoup对象

    该对象表示的是一个文档的全部内容,大部分的适合可以把它当作 Tag 对象。因为 BeautifulSoup 对象并不是真正的 HTML 或 XML 的tag,所以它没有name和attributes属性。但有时查看它时,.name 属性还是可以的。

    print(type(soup))   // <class 'bs4.BeautifulSoup'>
    print(soup.name)    // [document]
    
    

    Comment对象

    Tag , NavigableString , BeautifulSoup 几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象.容易让人担心的内容是文档的注释部分。

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

    Comment 对象是一个特殊类型的 NavigableString 对象:

    print(comment)  // Hey, buddy. Want to buy a used parser?
    
    

    但是当它出现在HTML文档中时, Comment 对象会使用特殊的格式输出:

    print(soup.b.prettify())
    <b>
     <!--Hey, buddy. Want to buy a used parser?-->
    </b>
    
    

    搜索文档树

    说完 Beautiful Soup 中的四种对象,接下来介绍一下如何搜索内容。 Beautiful Soup 提供了很多搜索方法,这里着重介绍其中的两个:find()find_all() ,其他的方法和参数以及用法都类似。

    过滤器

    过滤器作为搜索文档的参数,贯穿整个搜索的 API。过滤器可以被用在 tag 中的 name 中,节点的属性中,字符串中或者他们的混合中。过滤器可以是字符串、正则表达式、列表、True值甚至是方法。

    1. 字符串
    soup.find_all('b')  // 查找文档中所有的<b>标签
    
    
    1. 正则表达式
    import re
    // 查找 b 开头的标签
    for tag in soup.find_all(re.compile("^b")):
        print(tag.name)
    
    // body
    // b
    
    
    1. 列表
    markup = '''
        <b>Hey, buddy. Want to buy a used parser?</b>
        <p class="title">The Dormouse's story</p>
        <a>Once upon a time there were three little sisters</a>
    '''
    soup = BeautifulSoup(markup,'lxml')
    
    for tag in soup.find_all(['a','p']):
        print(tag)
    
    // <p class="title">The Dormouse's story</p>
    // <a>Once upon a time there were three little sisters</a>
    
    
    1. True

    True 可以匹配任何值

    for tag in soup.find_all(True):
        print(tag.name)
    
    // html
    // body
    // b
    // p
    // a
    
    
    1. 方法

    如果没有合适的过滤器,那么还可以定一个方法,方法只接收一个参数,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则返回 False

    def has_class(tag):
        return tag.has_attr('class')
    
    for tag in soup.find_all(has_class):
        print(tag)
    
    # <p class="title">The Dormouse's story</p>
    
    

    find_all()

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

    • name 参数

    name 参数可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉。其中,name 参数的值可以是任一类型的过滤器:字符串、正则表达式、列表、Ture或是方法。

    markup = '''
        <b>Hey, buddy. Want to buy a used parser?</b>
        <p class="title">The Dormouse's story</p>
        <a>Once upon a time there were three little sisters</a>
    '''
    soup = BeautifulSoup(markup,'lxml')
    print(soup.find_all('p'))   
    # [<p class="title">The Dormouse's story</p>]
    
    
    • keyword 参数

    如果一个指定名字的参数不是搜索内置的参数名称,搜索时会把该参数当作指定名字tag的属性来搜索。

    markup = '''
        <b id='link'>Hey, buddy. Want to buy a used parser?</b>
        <p class="title">The Dormouse's story</p>
        <a>Once upon a time there were three little sisters</a>
        <a class="sister" href="http://example.com/elsie" id="link1">three</a>
        <div data-foo="value">foo!</div>
    '''
    soup = BeautifulSoup(markup,'lxml')
    
    print(soup.find_all(id='link')) 
    # [<b id="link">Hey, buddy. Want to buy a used parser?</b>]
    
    # class是Python的保留关键字,这里是 class_
    print(soup.find_all(class_='title'))
    # [<p class="title">The Dormouse's story</p>]
    
    # 可以使用多个指定名字的参数来过滤多个tag的属性
    import re
    print(soup.find_all(id='link1', href=re.compile('example')))
    # [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]
    
    
    • attrs 参数

    有些tag属性在搜索中不能使用,如HTML5中的 data-* 属性,我们可以使用 attrs 参数定一个字典参数来搜索包含特殊属性的tag。

    print(soup.find_all(attrs={'data-foo':'value'}))
    # [<div data-foo="value">foo!</div>]
    
    
    • text参数

    通过 text 参数可以搜索文档中的字符串内容,和 name 参数一样,接受参数有:字符串、正则表达式、列表、True。

    print(soup.find_all(text='three'))
    # ['three']
    import re
    print(soup.find_all(text=re.compile('^H.*?$')))
    # ['Hey, buddy. Want to buy a used parser?']
    
    
    • recursive 参数

    Beautiful Soup 会检索当前tag的所有子孙节点,如果你只想搜索tag的直接子节点,可以使用参数 recursive = False。

    markup = '''
    <html>
     <head>
      <title>
       The Dormouse's story
      </title>
     </head>
    </html>
    '''
    soup = BeautifulSoup(markup, 'lxml')
    
    print(soup.html.find_all('title'))
    # [<title>
    #    The Dormouse's story
    #   </title>]
    
    print(soup.html.find_all('title', recursive=False))
    # []
    
    
    • limit 参数
    markup = '''
        <b id='link'>Hey, buddy. Want to buy a used parser?</b>
        <p class="title">The Dormouse's story</p>
        <a>Once upon a time there were three little sisters</a>
        <a class="sister" href="http://example.com/elsie" id="link1">three</a>
        <div data-foo="value">foo!</div>
    '''
    soup = BeautifulSoup(markup,'lxml')
    
    import re
    for str in soup.find_all(text=re.compile('o')):
        print(str)
    # Hey, buddy. Want to buy a used parser?
    # The Dormouse's story
    # Once upon a time there were three little sisters
    # foo!
    
    for str in soup.find_all(text=re.compile('o'), limit=1):
        print(str)
    
    # Hey, buddy. Want to buy a used parser?
    
    
    • 说明

    find_all() 是 Beautiful Soup中最常用的搜索方法,该库还提供了它的简写方法。BeautifulSoup 对象和 tag 对象都可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all() 方法相同,下面代码是等价的:

    soup.find_all("a")
    soup("a")
    
    soup.title.find_all(text=True)
    soup.title(text=True)
    
    

    find()

    find_all()方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果。比如文档中只有一个<body>标签,那么使用 find_all() 方法来查找显然不太合适,如果使用 limit=1 参数不如使用 find()方法。

    soup.find_all('title', limit=1)
    # [<title>The Dormouse's story</title>]
    
    soup.find('title')
    # <title>The Dormouse's story</title>
    
    

    唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果。
    find_all() 方法没有找到目标是返回空列表,find() 方法找不到目标时,返回 None 。

    soup.head.title 是 tag的名字 方法的简写。这个简写的原理就是多次调用当前tag的 find() 方法:

    soup.head.title
    # <title>The Dormouse's story</title>
    
    soup.find("head").find("title")
    # <title>The Dormouse's story</title>
    
    

    输出

    prettify() 方法将Beautiful Soup的文档树格式化后以Unicode编码输出,每个XML/HTML标签都独占一行。

    markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
    soup = BeautifulSoup(markup, 'lxml')
    print(soup.prettify())
    # <html>
    #  <body>
    #   <a href="http://example.com/">
    #    I linked to
    #    <i>
    #     example.com
    #    </i>
    #   </a>
    #  </body>
    # </html>
    
    

    Beautiful Soup 功能强大,除了上面提到的 搜索文档树 功能,更有遍历文档树,修改文档树等等功能。更多详细介绍请查阅官方文档 4.2.0

    作者:繁梦三千
    链接:https://www.jianshu.com/p/35b3616c74c1
    來源:简书

    相关文章

      网友评论

          本文标题:爬虫基础:Beautiful Soup

          本文链接:https://www.haomeiwen.com/subject/qwgvlqtx.html