• python爬虫之解析库Beautiful Soup


     

    为何要用Beautiful Soup

      Beautiful Soup是一个可以从HTMLXML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式,

      是一个标签的形式,来进行查找的,有点像jquery的形式。提升效率,我们在进行爬虫开发的时候,进程会用到正则来进行查找过滤的操作,纯手动会及其浪费时间。

    Beautiful Soup示例摘自官网

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

    这里先简单说明Beautiful Soup的查找方式,是一个标签树的形式。

    在使用的时候实例化一个对象,这个对象就相当于整个html文件,将标签封装成对象的属性,查找的时候使用“.”

    简单操作

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(open("html_doc.html"),"lxml")
    #简单的操作
    #打印html文件的title属性
    #print(soup.title)
    #<title>The Dormouse's story</title>
    
    #打印标签的名字
    # print(soup.title.name)
    #title
    
    # 打印标签的内容
    # print(soup.title.string)
    #The Dormouse's story
    
    #打印soup中的p标签,但是这里是能找到的第一个
    # print(soup.p)
    # <p class="title"><b>The Dormouse's story</b></p>
    
    #打印soup中的p标签class名字,但是这里是能找到的第一个
    # print(soup.p['class'],type(soup.p['class']))
    # ['title'] <class 'list'> #类型是个列表
    
    # 打印soup中的a标签,但是这里是能找到的第一个
    # print(soup.a)
    # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
    
    #打印所有的a标签
    # print(soup.find_all('a'))
    # [<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>]
    
    #打印id=link3的标签
    # print(soup.find(id="link3"))
    # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
    
    #从文档中找到所有<a>标签的链接:
    # for link in soup.find_all('a'):
    #     print(link.get('href'))
        # http://example.com/elsie
        # http://example.com/lacie
        # http://example.com/tillie
    
    # 从文档中获取所有文字内容:
    # print(soup.get_text())
    # The Dormouse's story
    #
    # The Dormouse's story
    #
    # 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.
    #
    # ...

    Tag

    soup1 = BeautifulSoup('<b class="boldest">Extremely bold</b>',"lxml")
    tag = soup1.b  
    # print(type(tag))
    # <class 'bs4.element.Tag'>

    Tag的Name属性

    # print(tag.name)
    # b
    # 如果改变了tag的name,那将影响所有通过当前Beautiful Soup对象生成的HTML文档:
    
    # tag.name = "blockquote"
    # print(tag)
    # <blockquote class="boldest">Extremely bold</blockquote>

    Tag的Attributes属性

    一个tag可能有很多个属性. tag <b class="boldest"> 有一个 “class” 的属性,值为 “boldest” . tag的属性的操作方法与字典相同:
    # print(tag['class'])
    # ['boldest']
    
    # 也可以直接”点”取属性, 比如: .attrs :
    # print(tag.attrs)
    # {'class': ['boldest']}
    # print(soup.a.attrs['class'])
    # ['sister']
    # tag的属性可以被添加,删除或修改. 再说一次, tag的属性操作方法与字典一样

    # tag['class'] = 'verybold'
    # tag['id'] = 1
    # print(tag)
    # <blockquote class="verybold" id="1">Extremely bold</blockquote>

    # del tag['class']
    # del tag['id']
    # print(tag)
    # <blockquote>Extremely bold</blockquote>

    # tag['class']
    # KeyError: 'class'
    # print(tag.get('class'))
    # None
     

    子节点操作:

    .contents属性

    #.contents
    # tag的 .contents 属性可以将tag的子节点以列表的方式输出:
    # print(soup)
    #print(soup.contents) #这里打印的是整个html标签
    #print("________")
    #print(soup.head.contents) #打印出来的是head下的列表,可以借助元组去重
    ##['
    ', <meta charset="utf-8"/>, '
    ', <title>The Dormouse's story</title>, '
    ']
    #print(len(soup.head.contents))
    ##5
    #print(soup.head.contents[1].name)
    ##meta


     descendants属性

    #descendants属性
    # .contents 和 .children 属性仅包含tag的直接子节点.例如,<head>标签只有一个直接子节点<title>
    #但如果我们需要递归循环的话, .descendants 属性可以对所有tag的子孙节点进行递归循环 [5] :
    # des_tag = soup.head
    # print(des_tag)
    # for child in des_tag.descendants:
    #     print(child,end="")
    
    # 输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容:(代替.string)

    父节点:

    parent

    #parent
    # title_tag = soup.title
    # print(title_tag)
    ## <title>The Dormouse's story</title>
    # print(title_tag.parent)
    ## <head>
    ## <meta charset="utf-8"/>
    ## <title>The Dormouse's story</title>
    ##</head>

    parents

    #通过元素的 .parents 属性可以递归得到元素的所有父辈节点,下面的例子使用了 .parents 方法遍历了<a>标签到根节点的所有节点.
    #link = soup.a
    # link
    ## <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
    # for parent in link.parents:
    #     if parent is None:
    #         print(parent)
    #     else:
    #         print(parent.name)
    ##p
    ## body
    ##html
    ## [document]
    ## None

    兄弟节点:

    .next_sibling 和 .previous_sibling 属性

    # link2 = soup.find(id='link2')
    # print(link2)
    # # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
    # #实际文档中的tag的 .next_sibling 和 .previous_sibling 属性通常是字符串或空白. 看看“爱丽丝”文档:
    # print(link2.next_sibling) #由于本文是以爱丽丝为版本,所以这里下一个是“and”
    # print(link2.previous_sibling) #上一个是一个",".

    .next_siblings 和 .previous_siblings属性

    # 通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出:
    # link2 = soup.find(id='link2')
    # for sibling in link2.next_siblings:
    #     print(sibling)
    #
    # # and
    #
    # # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
    # # ;
    # # and they lived at the bottom of a well.
    # link2 = soup.find(id='link2')
    # for sibling in link2.previous_siblings:
    #     print(sibling)
    #
    # # ,
    # #
    # # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
    # # Once upon a time there were three little sisters; and their names were

     搜索文档树:

    # #find 和 findall() 唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果.
    # find = soup.find('a')
    # print(find)
    # # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
    # print("----------------")
    # findall = soup.find_all('a')
    # print(findall) #这个是个列表,可以索引取值
    # #[<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>]
    # findall2 = soup.find_all(attrs={'id':'link2'}) #后面可以加属性来进行过滤
    # print("---------")
    # print(findall2)
    # # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

    正则表达式

    #如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match()
    # 来匹配内容.下面例子中找出所有以b开头的标签,这表示<body>和<b>标签都应该被找到:
    
    # import re
    # for tag in soup.find_all(re.compile("^b")):
    #     print(tag.name)
    # #b
    # #b

    判断是否有那个属性

    # findall可以传入函数作为过滤的参数
    # 下面方法校验了当前元素,如果包含 class 属性却不包含 id 属性,那么将返回 True:
    #
    # def has_class_but_no_id(tag):
    #     return tag.has_attr('class') and not tag.has_attr('id')
    # 将这个方法作为参数传入 find_all() 方法,将得到所有<p>标签:
    #
    # soup.find_all(has_class_but_no_id)
    # [<p class="title"><b>The Dormouse's story</b></p>,
    #  <p class="story">Once upon a time there were...</p>,
    #  <p class="story">...</p>]

    findall的属性(find和findall差不多)

    # find_all( name , attrs , recursive , string , **kwargs )
    #
    # soup.find_all("title")
    # # [<title>The Dormouse's story</title>]
    #
    # soup.find_all("p", "title")
    # # [<p class="title"><b>The Dormouse's story</b></p>]
    #
    # soup.find_all("a")
    # # [<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>]
    #
    # soup.find_all(id="link2")
    # # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
    #
    # import re
    # soup.find(string=re.compile("sisters"))
    # # u'Once upon a time there were three little sisters; and their names were
    '

    findall中 name参数

    简单的用法如下:
    
    soup.find_all("title")
    # [<title>The Dormouse's story</title>]
    
    重申: 搜索 name 参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True .
    keyword 参数
    
    如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性.
    
    soup.find_all(id='link2')
    # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
    
    如果传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性:
    
    soup.find_all(href=re.compile("elsie"))
    # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
    
    搜索指定名字的属性时可以使用的参数值包括 字符串 , 正则表达式 , 列表, True .
    
    下面的例子在文档树中查找所有包含 id 属性的tag,无论 id 的值是什么:
    
    soup.find_all(id=True)
    # [<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>]
    
    使用多个指定名字的参数可以同时过滤tag的多个属性:
    
    soup.find_all(href=re.compile("elsie"), id='link1')
    # [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]
    
    有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:
    
    data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')
    data_soup.find_all(data-foo="value")
    # SyntaxError: keyword can't be an expression
    
    但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:
    
    data_soup.find_all(attrs={"data-foo": "value"})
    # [<div data-foo="value">foo!</div>]

    其他

    find_parents()返回所有祖先节点,find_parent()返回直接父节点。
    find_next_siblings()返回后面所有兄弟节点,find_next_sibling()返回后面第一个兄弟节点。
    find_previous_siblings()返回前面所有兄弟节点,find_previous_sibling()返回前面第一个兄弟节点。
    find_all_next()返回节点后所有符合条件的节点, find_next()返回第一个符合条件的节点
    find_all_previous()返回节点后所有符合条件的节点, find_previous()返回第一个符合条件的节点

    CSS选择器

    #Beautiful Soup支持大部分的CSS选择器 http://www.w3.org/TR/CSS2/selector.html [6] , 在 Tag 或 BeautifulSoup 对象的 .select() 方法中传入字符串参数, 即可使用CSS选择器的语法找到tag:
    
    soup.select("title")
    # [<title>The Dormouse's story</title>]
    
    soup.select("p nth-of-type(3)")
    # [<p class="story">...</p>]
    
    通过tag标签逐层查找:
    
    soup.select("body a")
    # [<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>]
    
    soup.select("html head title")
    # [<title>The Dormouse's story</title>]
    
    找到某个tag标签下的直接子标签 [6] :
    
    soup.select("head > title")
    # [<title>The Dormouse's story</title>]
    
    soup.select("p > a")
    # [<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>]
    
    soup.select("p > a:nth-of-type(2)")
    # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
    
    soup.select("p > #link1")
    # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
    
    soup.select("body > a")
    # []
    
    找到兄弟节点标签:
    
    soup.select("#link1 ~ .sister")
    # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
    #  <a class="sister" href="http://example.com/tillie"  id="link3">Tillie</a>]
    
    soup.select("#link1 + .sister")
    # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
    
    通过CSS的类名查找:
    
    soup.select(".sister")
    # [<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>]
    
    soup.select("[class~=sister]")
    # [<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>]
    
    通过tag的id查找:
    
    soup.select("#link1")
    # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
    
    soup.select("a#link2")
    # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
    
    同时用多种CSS选择器查询元素:
    
    soup.select("#link1,#link2")
    # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
    #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
    
    通过是否存在某个属性来查找:
    
    soup.select('a[href]')
    # [<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>]
    
    通过属性的值来查找:
    
    soup.select('a[href="http://example.com/elsie"]')
    # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
    
    soup.select('a[href^="http://example.com/"]')
    # [<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>]
    
    soup.select('a[href$="tillie"]')
    # [<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
    
    soup.select('a[href*=".com/el"]')
    # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
    
    通过语言设置来查找:
    
    multilingual_markup = """
     <p lang="en">Hello</p>
     <p lang="en-us">Howdy, y'all</p>
     <p lang="en-gb">Pip-pip, old fruit</p>
     <p lang="fr">Bonjour mes amis</p>
    """
    multilingual_soup = BeautifulSoup(multilingual_markup)
    multilingual_soup.select('p[lang|=en]')
    # [<p lang="en">Hello</p>,
    #  <p lang="en-us">Howdy, y'all</p>,
    #  <p lang="en-gb">Pip-pip, old fruit</p>]
    
    返回查找到的元素的第一个
    
    soup.select_one(".sister")
    # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
    
    对于熟悉CSS选择器语法的人来说这是个非常方便的方法.Beautiful Soup也支持CSS选择器API,
    如果你仅仅需要CSS选择器的功能,那么直接使用 lxml 也可以, 而且速度更快,支持更多的CSS选择器语法,
    但Beautiful Soup整合了CSS选择器的语法和自身方便使用API.

    解释器种类:

      

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

      

      

  • 相关阅读:
    NGINX下配置404错误页面的方法分享
    mysql 统计
    nginx日志中访问最多的100个ip及访问次数
    ubuntu下完全安装mcrypt
    ngxtop:在命令行实时监控 Nginx 的神器
    AngularJs 返回上一页
    nginx 报错 upstream timed out (110: Connection timed out)解决方案
    IAP 破解漏洞验证
    AceAdmin-v1.4.0 下载
    TP QQ 微信 微博登录
  • 原文地址:https://www.cnblogs.com/taozizainali/p/9295117.html
Copyright © 2020-2023  润新知