zoukankan      html  css  js  c++  java
  • 爬虫解析库beautifulsoup

    一、介绍

    Beautiful Soup是一个可以从HTML或XML文件中提取数据的python库。

    #安装Beautiful Soup
    pip install beautifulsoup4
    
    #安装解析器
    Beatiful Soup支持python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是lxml,安装lxml:
    pip install lxml   #这个使用率最高的
    
    另外一个可供选择的解析器是纯python实现的html5lib,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格式的文档
    • 速度慢
    • 不依赖外部扩展

    二、基本使用

    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)

    三、遍历文档树

    遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签只返回第一个

    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>
    """
    
    from bs4 import BeautifulSoup
    soup=BeautifulSoup(html_doc,'lxml') #具有容错功能
    res=soup.prettify() #处理好缩进,结构化显示
    
    #1.用法
    print(soup.p)   #存在多个相同的标签只取出第一个p标签
                          #<p class="title"><b>The Dormouse's story</b></p>
    #2.获取标签的名称
    print(soup.p.name)    #p
    
    #3.获取标签的属性
    print(soup.p.attrs)    #{'class': ['title']}   以字典形式打印
    
    #4.获取标签的内容
    print(soup.p.string)  #The Dormouse's story  #p下的文本只有一个时才能取到,否则为None
    print(soup.p.strings)  #拿到的是生成器对象,取到p下所有的文本内容
    print(soup.p.text)  #取到p下所有的文本内容
    for line in soup.stripped_strings:   #去掉空白
        print(line)   
    #5.子节点、子孙节点
    print(soup.p.contents)  #p下所有子节点 
                                      #[<b>The Dormouse's story</b>]
    
    print(soup.p.children)  #得到一个迭代器,包含p下所有的子节点   <list_iterator object at 0x00000054B207C780>
    for i,child in enumerate(soup.p.children):   #循环取出
        print(i,child)    #0 <b>The Dormouse's story</b>
    
    
    print(soup.p.descendants)  #获取p下面所有的子孙节点   <generator object descendants at 0x00000054B203C258>
    for i,child in enumerate(soup.p.descendants):  #文本也会显示出来
        print(i,child)    #0 <b>The Dormouse's story</b>   1 The Dormouse's story
     
    #6.父节点、祖先节点
    print(soup.a.parent)  #获取a标签的父节点
    print(soup.a.parents)  #找到a标签所有的祖父节点,父亲的父亲。。。

    四、搜索文档树

    1.五种过滤器

    搜索文档树:Beautiful Soup定义了很多搜索方法,这里着重介绍2个:find()和find_all()

    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>
    """
    
    from bs4 import BeautifulSoup
    soup=BeautifulSoup(html_doc,'lxml') 
    res=soup.prettify() #处理好缩进,结构化显示
    
    #1.五种过滤器:字符串、正则、列表、True、方法
    #1.1 字符串:即标签名
    print(soup.find_all('a'))  #写标签名,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>]
    
    #1.2 正则
    import re
    print(soup.find_all(re.compile('^b')))  #找到b开头的标签,结果有body和b标签
    
    #1.3列表:如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的结果返回
    print(soup.find_all(['a',['b']]))   #包含a和b标签
    
    [<b>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))   #返回所有标签
    for tag in soup.find_all(True):
        print(tag.name)   #返回所有节点:html/p/a/head...
    
    #1.5方法:如果没有合适的过滤器,可以自定义一个方法,方法接受一个元素参数,如果方法返回True表示当前元素匹配成功,如果没找到返回False
    def has_class_but_no_id(tag):
        return tag.has_attr('class') and not tag.attr('id')
    
    print(soup.find_all(has_class_but_no_id))

    2.find_all(name,attrs,recursive,text,**kwargs)   查询所有

    #2.1 name:搜索name参数的值可以是任一类型的过滤器:字符串、正则、列表、True、方法
    import re
    print(soup.find_all(name=re.compile('^b')))  #这里加不加name都可以
    
    #2.2 keyword:   key=value的形式,value可以是过滤器:字符串、正则、列表、True
    print(soup.find_all(class_=re.compile('title')))   #[<p class="title"><b>The Dormouse's story</b></p>]   匹配class是title的标签,注意类用class_
    print(soup.find_all(id=True))  #查找有id属性的标签
    
    #2.3按照类名查找,注意关键字是class_,class_=value,value可以是五种选择器之一
    print(soup.find_all('a',class_='sister'))    #查找类为sister的a标签
    print(soup.find_all('a',class_='sister sss'))    #查找类为sister和sss的a标签
    
    #2.4 attrs
    print(soup.find_all('p',attrs={'class':'story'}))   #查找class=story的p标签
    
    #2.5 text 值可以是:字符串,列表,True,正则
    print(soup.find_all(text='Elsie'))      #Elsie  文本
    print(soup.find_all('a',text='Elsie'))  #获取Elsie文本的a标签
    
    #2.6 limit参数:限制取值个数
    print(soup.find_all('a',limit=2))   #取两条a标签
    
    #2.7 recursive:
    print(soup.find_all('a'))  #取出a标签的
    print(soup.find_all('a',recursive=False))  #取出a标签的子节点  []

    3.find(name,attrs,recursive,text,**kwargs)  查询第一个符合条件的标签

    #3.find(name,attrs,recursive,text,**kwargs)
    find_all() 方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果。比如文档中只有一个a标签,那么可以直接使用find()方法来查找。
    
    唯一区别就是find_all()查询到的结果返回的是包含元素的列表,而find()方法直接返回结果。
    find_all()没有找到符合元素返回空列表,find()方法找不到目标时返回None.

    4.css选择器  (******)使用select获取

    #使用select方法来查询
    
    #1.css选择器
    print(soup.select('.sister'))  #查询class=sister的标签
    
    print(soup.select('#link1'))
    print(soup.select('#link1')[0])
    print(soup.select('#link1')[0].text)   #只要下面可以取值就可以一直. 下去
    
    #2.获取属性
    print(soup.select('#link2')[0].attrs)   #{'href': 'http://example.com/lacie', 'class': ['sister'], 'id': 'link2'}
    
    #3.获取内容
    print(soup.select('#link1')[0].get_text())或者
    print(soup.select('#link1')[0].text)

    总结:

    1.推荐使用lxml解析库
    2.讲了三种选择器:标签选择器,find和find_all,css选择器
        1.标签选择器筛选功能弱,但是速度快
        2.建议使用find,find_all查询匹配单个结果或者多个结果
        3.如果对css选择器非常熟悉建议使用select
    
    3.获取属性attrs和获取文本get_text()方法
  • 相关阅读:
    url传递参数带 + ,解决办法
    操作系统——内存地址重定位
    算法——二分查找变形题
    Java——代码性能优化
    maven——添加插件和添加依赖有什么区别?
    JavaWeb——Servlet如何调用线程池中的线程?
    「ZJOI2016」小星星
    [十二省联考2019]字符串问题
    [十二省联考2019]春节十二响
    [十二省联考2019]异或粽子
  • 原文地址:https://www.cnblogs.com/wangcuican/p/11937956.html
Copyright © 2011-2022 走看看