Python之BeautifulSoup库详解
创始人
2024-05-28 19:30:29

一、简介

BeautifulSoup是一个灵活方便的网页解析库,处理高效,能够自动的将输入文档转换为Unicode编码,输出文档转换为utf-8编码,且支持多种解析器。其最主要的功能是从网页抓取数据。

二、解析器

解析器使用方法优势劣势
Python标准库BeautifulSoup(markup, ‘html.parser’)python内置的标准库,执行速度适中Python3.2.2之前的版本容错能力差
lxml HTML解析器BeautifulSoup(markup, ‘lxml’)速度快、文档容错能力强需要安装C语言库
lxml XML解析器BeautifulSoup(markup ‘xml’)速度快,唯一支持XML的解析器需要安装C语言库
html5libBeautifulSoup(markup, ‘html5lib’)最好的容错性、以浏览器的方式解析文档、生成HTML5格式的文档速度慢,不依赖外部拓展

三、基本使用步骤

3.1 获取网页源码也可以通过字符串自己构建一个网页的源码

# 给请求指定一个请求头来模拟chrome浏览器
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'}
# 爬图地址
addr = 'https://sc.chinaz.com/tupian/'def getHtmlSource():# 使用请求头来模拟chrome浏览器访问网页,获取页面响应结果res = requests.get(addr, headers=headers).textprint(res)

requests.get().text返回的是Unicode型的数据,requests.get().content返回的是bytes型的数据。如果只是使用.text来获取页面源码的话,获取的源码中中文会乱码。可以使用一下方法解决中文乱码:
(1)手动指定页面编码

    res = requests.get(addr, headers=headers)res.encoding = 'UTF-8'html_doc = res.text

(2)使用.content方法

    html_doc = str(requests.get(addr, headers=headers).content, 'UTF-8')

3.2 使用解析器解析页面响应结果

# 使用自带的html.parser解析页面响应结果
soup = BeautifulSoup(html_doc, 'html.parser')
# 使用lxml HTML解析器解析页面响应结果
soup = BeautifulSoup(html_doc, 'lxml')
# 使用lxml XML解析器解析页面响应结果
soup = BeautifulSoup(html_doc, 'xml')
# 使用html5lib解析页面响应结果
soup = BeautifulSoup(html_doc, 'html5lib')

四、四大对象种类

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

  • Tag
  • NavigableString
  • BeautifulSoup
  • Comment

4.1 Tag

Tag 通俗点讲就是 HTML 中的一个个标签,例如:titleheadap等等 HTML 标签加上里面包括的内容就是 Tag。但是注意,它查找的是在所有内容中的第一个符合要求的标签。

# 创建BeautifulSoup对象
soup = BeautifulSoup(html_doc, 'html.parser')
# 通过Tag对象获取title标签信息
print(soup.title)
## 图片、图片下载、高清图片材# 通过Tag对象获取a标签信息
print(soup.a)
## 

Tag 它有两个重要的属性,是 name 和 attrs。
(1) name: 输出标签的标签类型名:

print(soup.title.name)
# title
print(soup.p.name)
# p

(2) attrs: 以字典的形式获取标签的属性:

 # 获取标签的所有属性soup.p.attrs# 获取标签的某个属性soup.p.attrs['js-do']soup.p.get('js-do')	# 修改标签的属性soup.p.attrs['js-do'] = 'newContenct'# 删除标签的属性del soup.p.attrs['js-do']

4.2 NavigableString

注意作用是为了获取标签内部的文字。

# 获取标签内部文字
print(soup.title.string)

4.3 BeautifulSoup

BeautifulSoup 对象表示的是一个文档的内容。大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们也可以获取它的属性及名称。

# 获取BeautifulSoup对象名称
print(soup.name)
# 获取BeautifulSoup对象属性
print(soup.attr)

4.4 Comment

Comment 对象是一个特殊类型的 NavigableString 对象,如果标签内部的内容是注释,其输出的内容不包括注释符号。

print(soup.a)
## 
print(soup.a.string)
## zhushi
print(type(soup.a.string))
## 

五、搜索文档树

find(name, attrs, recursive, string, **kwargs):获取匹配的第一个标签;
find_all(name, attrs, recursive, string, limit, **kwargs) :返回结果是值包含一个元素的列表;

  • name:是根据标签的名称进行匹配,name的值相当于过滤条件,可以是一个具体的标签名,多个标签名组成的列表,或者是一个正在表达式,甚至是函数方法等等。
  • attrs:是根据标签的属性进行匹配。
  • recursive:是否递归搜索,默认为True,会搜索当前tag的所有子孙节点,设置为False,则只搜索儿子节点。
  • string:是根据标签的文本内容去匹配。
  • limit:设置查询的结果数量。
  • kwargs:也是根据标签的属性进行匹配,与attrs的区别在于写法不一样,且属性的key不能是保留字,也不能与其他参数名相同。

5.1 使用name进行匹配

# 查找所有的标签
soup.find_all(name="a") # 可以简写成 soup.find_all("a")# 查找所有的标签或者<link>标签
soup.find_all(name={<!-- -->'title', 'link'}) # 可以简写成 soup.find_all(['title', 'link']) # 查找所有以a开头的标签
soup.find_all(name=re.compile("^a")) # 可以简写成 soup.find_all(re.compile("^a")) # 查找有class属性并且没有id属性的节点
soup.find_all(hasClassNoId)
def hasClassNoId(tag):return tag.has_attr('class') and not tag.has_attr('id')# 查找body里面所有标签
soup.find('body').find_all(True)
</code></pre> 
<h4>5.2 使用attrs进行匹配</h4> 
<pre><code class="prism language-bash"># 查找所有name属性值为tb_name的标签
soup.find_all(attrs={<!-- -->"name": "tb_name"})# 查找所有id属性值为id_attr1或者id_attr2的标签
soup.find_all(attrs={<!-- -->'id': ['id_attr1', 'id_attr2']})# 查找id属性值中含有id_的所有标签
soup.find_all(attrs={<!-- -->'id':re.compiles('id_')})# 查找含有id属性的所有tag
soup.find_all(attrs={<!-- -->'id':True})# 查找href属性值中以.html结尾的所有标签
soup.find_all(attrs={<!-- -->'href': search_html})
def search_html(attr):return attr and attr.lower().endswith('.html')
</code></pre> 
<h4>5.3 使用kwargs进行匹配</h4> 
<p><code>也是通过标签的属性进行匹配,需要特别注意的是name属性以及class属性,name属于与find_all方法的第一个参数名相同,这里使用name='属性值'进行查询的话,如果是一个参数会与标签名进行匹配,如果是多个参数,则方法会报错;而class是python的保留字,使用class属性进行匹配的时候需要写成class_='属性值'的方式</code></p> 
<pre><code class="prism language-bash"># 查找<div>中name属性值是backpage的所有标签
soup.find_all('div', name='backpage')  ## 会报错# 查找class属性值是backpage的所有标签
soup.find_all(class_='backpage')# 查找所有id属性值为id_attr1或者id_attr2的标签
soup.find_all(id=['id_attr1', 'id_attr2'])# 查找href的属性值包含.html的所有的标签
soup.find_all(href=re.compile('.html'))# 查找含有id属性的所有tag
soup.find_all(id=True)# 查找href属性值中以.html结尾的所有标签
soup.find_all(href= search_html)
def search_html(attr):return attr and attr.lower().endswith('.html')
</code></pre> 
<h4>5.4 使用string进行匹配</h4> 
<p><code>需要注意的是这里返回标签的值,如果需要获取到对应的标签,可以使用previous_element属性来获得</code></p> 
<pre><code class="prism language-bash"># 查找标签的value是'上一页'的所有value值
soup.find_all(string='上一页')# 查找标签的value是'上一页'的所有标签
[value.previous_element for value in soup.find_all(string='上一页')]# 查找value是'上一页'或者'下一页'的所有value值
soup.find_all(string=['上一页','下一页'])# 查找value中存在'页'的所有value值
soup.find_all(string=re.compile('页'))# 查找在value值的所有的string
soup.find_all(string=True)# 查找所有value值是以'页'为结尾的value值
soup.find_all(string=search_string)
def search_string(string):return string and string.lower().endswith('页')
</code></pre> 
<h3>六、遍历文档树</h3> 
<ul><li><code>contents</code>:返回的是一个包含所有儿子节点的列表。</li><li><code>children</code>:返回的是一个包含所有儿子节点的迭代器。</li><li><code>descendants</code>:返回的是一个包含所有子孙节点的生成器。</li></ul> 
<p><strong>contents、children只包含直接儿子节点,descendants既包含儿子节点还包含孙子节点。</strong></p> 
<h4>6.1 通过contents获取目标节点的所有子节点</h4> 
<pre><code class="prism language-bash">tag_soup = soup.find('div', class_='container').contents
print(type(tag_soup))
for t in tag_soup:if t != '\n':  # 去掉换行符print(t)
</code></pre> 
<p><img src="/uploadfile/202405/c3a3a759ff1e3f5.png" alt="在这里插入图片描述" /></p> 
<h4>6.2 通过children获取目标节点的所有子节点</h4> 
<pre><code class="prism language-bash">tag_soup = soup.find('div', class_='container').children
print(type(tag_soup))
for t in tag_soup:if t != '\n':  # 去掉换行符print(t)
</code></pre> 
<p><img src="/uploadfile/202405/d74e2fc4d260ca6.png" alt="在这里插入图片描述" /></p> 
<h4>6.3 通过descendants获取目标节点的所有子孙节点</h4> 
<pre><code class="prism language-bash">tag_soup = soup.find('div', class_='container').descendants
print(type(tag_soup))
for t in tag_soup:if t != '\n':  # 去掉换行符print(t)
</code></pre> 
<p><img src="/uploadfile/202405/d536b4b515a1e7c.png" alt="在这里插入图片描述" /></p> 
<h4>6.4 通过parents获取目标节点的所有祖先节点</h4> 
<pre><code class="prism language-bash">tag_soup = soup.find('div', class_='container').parents
print(type(tag_soup))
for t in tag_soup:if t != '\n':  # 去掉换行符print(t)
</code></pre> 
<h4>6.5 获取目标节点相关联的其他节点</h4> 
<pre><code class="prism language-bash">a_soup = soup.find('div', class_='container').a  # 获取div里面的第一个<a>标签print(a_soup.parent)  # 获取<a>标签的父节点print(a_soup.next_sibling)  # 获取<a>标签的下一个兄弟节点print(a_soup.previous_sibling)  # 获取<a>标签的上一个兄弟节点print(a_soup.next_siblings)  # 获取<a>标签下面的所有兄弟节点print(a_soup.previous_siblings)  # 获取<a>标签上面的所有兄弟节点
</code></pre> 
<h3>七、css选择器</h3> 
<h4>7.1 通过标签名查找</h4> 
<pre><code class="prism language-bash"># 查找所有title标签
soup.select('title')# 查找div下的所有input标签
soup.select('div input')# 查找html节点下的head节点下的title标签
soup.select("html head title")
</code></pre> 
<h4>7.2 通过id查找</h4> 
<pre><code class="prism language-bash"># 查找id为id_text的标签
soup.select("#id_text")# 查找id为id_text1、id_text2的标签
soup.select("#id_text1, #id_text2")# 查找id为id_text1的input标签
soup.select('input#id_text1')
</code></pre> 
<h4>7.3 通过类名查找</h4> 
<pre><code class="prism language-bash"># 查找类名为nextpage的标签
soup.select(".nextpage")# 查找类名为nextpage、active的标签
soup.select('.nextpage, .active')# 查找类名为nextpage的a标签
soup.select('a.nextpage')
</code></pre> 
<h4>7.4 通过属性查找</h4> 
<pre><code class="prism language-bash"># 选择有href属性的a标签
soup.select('a[href]')# 选择href属性为index_2.html的a标签
soup.select('a[href="index_2.html"]')# 选择href以index开头的a标签
soup.select('a[href^="index"]')# 选择href以html结尾的a标签
soup.select('a[href$="html"]')# 选择href属性包含index的a标签
soup.select('a[href*="index"]')
</code></pre> 
<h4>7.5 其他选择器</h4> 
<pre><code class="prism language-bash"># 查找div标签下的a标签
soup.select("div > a")# 父节点中的第3个a标签
soup.select("a:nth-of-type(3)")# a标签之后的input标签(a和input有共同父节点)
soup.select("a~input")
</code></pre><link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/markdown_views-0407448025.css" rel="stylesheet"><link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/style-bb308a51ed.css" rel="stylesheet">                <!--end::Text-->
            </div>
            <!--end::Description-->
            <div class="mt-5">
                <!--关键词搜索-->
                
                <a href="/index.php?s=news&c=search&keyword=%E8%AF%8D%E5%BA%93%E5%8A%A0%E8%BD%BD%E9%94%99%E8%AF%AF%3A%E6%9C%AA%E8%83%BD%E6%89%BE%E5%88%B0%E6%96%87%E4%BB%B6%E2%80%9CE%3A%5Chighferrum_mysql%5CConfiguration%5CDict_Stopwords.txt%E2%80%9D%E3%80%82" class="badge badge-light-primary fw-bold my-2" target="_blank">词库加载错误:未能找到文件“E:\highferrum_mysql\Configuration\Dict_Stopwords.txt”。</a>
                            </div>
            <div class="mt-5">
                <p class="fc-show-prev-next">
                    <strong>上一篇:</strong><a href="/life/3461342.html">【SQLAlchemy】第二篇——连接失效及连接池</a><br>
                </p>
                <p class="fc-show-prev-next">
                    <strong>下一篇:</strong><a href="/life/3461356.html">maxWell数据迁移</a>                </p>
            </div>
            <!--begin::Block-->
            <div class="d-flex flex-stack mb-2 mt-10">
                <!--begin::Title-->
                <h3 class="text-dark fs-5 fw-bold text-gray-800">相关内容</h3>
                <!--end::Title-->
            </div>
            <div class="separator separator-dashed mb-9"></div>
            <!--end::Block-->
            <div class="row g-10">
                

            </div>


        </div>
        <!--end::Table widget 14-->
    </div>
    <!--end::Col-->

    <!--begin::Col-->
    <div class="col-xl-4 mt-0">
        <!--begin::Chart Widget 35-->
        <div class="card card-flush h-md-100">
            <!--begin::Header-->
            <div class="card-header pt-5 ">
                <!--begin::Title-->
                <h3 class="card-title align-items-start flex-column">
                    <!--begin::Statistics-->
                    <div class="d-flex align-items-center mb-2">
                        <!--begin::Currency-->
                        <span class="fs-5 fw-bold text-gray-800 ">热门资讯</span>
                        <!--end::Currency-->
                    </div>
                    <!--end::Statistics-->
                </h3>
                <!--end::Title-->
            </div>
            <!--end::Header-->
            <!--begin::Body-->
            <div class="card-body pt-3">

                                <!--begin::Item-->
                <div class="d-flex flex-stack mb-7">
                    <!--begin::Symbol-->
                    <div class="symbol symbol-60px symbol-2by3 me-4">
                        <div class="symbol-label" style="background-image: url('https://p3.toutiaoimg.com/origin/tos-cn-i-qvj2lq49k0/002f770423f6444da282785960702fa7?from=pc')"></div>
                    </div>
                    <!--end::Symbol-->
                    <!--begin::Title-->
                    <div class="m-0">
                        <a href="/changshi/4311200.html" class="text-dark fw-bold text-hover-primary fs-6">北京的名胜古迹 北京最著名的景...</a>
                        <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...</span>
                    </div>
                    <!--end::Title-->
                </div>
                                <!--begin::Item-->
                <div class="d-flex flex-stack mb-7">
                    <!--begin::Symbol-->
                    <div class="symbol symbol-60px symbol-2by3 me-4">
                        <div class="symbol-label" style="background-image: url('https://p6.toutiaoimg.com/origin/tos-cn-i-qvj2lq49k0/0be6682557a84445b7ee741d4acdf41f?from=pc')"></div>
                    </div>
                    <!--end::Symbol-->
                    <!--begin::Title-->
                    <div class="m-0">
                        <a href="/changshi/4311199.html" class="text-dark fw-bold text-hover-primary fs-6">世界上最漂亮的人 世界上最漂亮...</a>
                        <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...</span>
                    </div>
                    <!--end::Title-->
                </div>
                                <!--begin::Item-->
                <div class="d-flex flex-stack mb-7">
                    <!--begin::Symbol-->
                    <div class="symbol symbol-60px symbol-2by3 me-4">
                        <div class="symbol-label" style="background-image: url('https://p1-tt.byteimg.com/origin/pgc-image/8f50304f5bc04b2aa216387809669921?from=pc')"></div>
                    </div>
                    <!--end::Symbol-->
                    <!--begin::Title-->
                    <div class="m-0">
                        <a href="/changshi/4311198.html" class="text-dark fw-bold text-hover-primary fs-6">苗族的传统节日 贵州苗族节日有...</a>
                        <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...</span>
                    </div>
                    <!--end::Title-->
                </div>
                                <!--begin::Item-->
                <div class="d-flex flex-stack mb-7">
                    <!--begin::Symbol-->
                    <div class="symbol symbol-60px symbol-2by3 me-4">
                        <div class="symbol-label" style="background-image: url('https://p3.toutiaoimg.com/origin/pgc-image/5d7b1f820a4b403aa136bac748c8be9f?from=pc')"></div>
                    </div>
                    <!--end::Symbol-->
                    <!--begin::Title-->
                    <div class="m-0">
                        <a href="/changshi/4311197.html" class="text-dark fw-bold text-hover-primary fs-6">长白山自助游攻略 吉林长白山游...</a>
                        <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...</span>
                    </div>
                    <!--end::Title-->
                </div>
                                <!--begin::Item-->
                <div class="d-flex flex-stack mb-7">
                    <!--begin::Symbol-->
                    <div class="symbol symbol-60px symbol-2by3 me-4">
                        <div class="symbol-label" style="background-image: url('https://p26.toutiaoimg.com/origin/pgc-image/e2ed2b3a08484bf1be3e029f3cd7d763?from=pc')"></div>
                    </div>
                    <!--end::Symbol-->
                    <!--begin::Title-->
                    <div class="m-0">
                        <a href="/changshi/4311196.html" class="text-dark fw-bold text-hover-primary fs-6">应用未安装解决办法 平板应用未...</a>
                        <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...</span>
                    </div>
                    <!--end::Title-->
                </div>
                
            </div>
            <!--end::Body-->
        </div>
        <!--end::Chart Widget 35-->
    </div>
    <!--end::Col-->
</div>



</div>
<!--end::Content container-->
</div>
<!--end::Content-->
</div>
<!--end::Content wrapper-->
<!--begin::Footer-->
<div id="kt_app_footer" class="app-footer">
    <!--begin::Footer container-->
    <div class="app-container container-xxl d-flex flex-column flex-md-row flex-center flex-md-stack py-3">
        <!--begin::Copyright-->
        <div class="text-dark order-2 order-md-1">
            <span class="text-muted fw-semibold me-1">2025 ©</span>
            办公百科网<a href="http://www.zzldhg.com/">零点化工网</a><a href="http://www.hcygmm.com/">汇川网</a><a href="http://learn.office369.com">学习</a><a href="http://www.zzdfzj.cn/">东方游戏</a><a href="http://share.office369.com">办公分享</a><div style="display:none"><a href="http://www.80hlw.com">八零互联网</a> <a href="http://ask.kcwzh.com">开创问答网</a> <a href="http://www.office369.com">办公应用网</a><a href="https://www.mingxing100.com">明星一百</a><a href="http://tousu.huashangw.com">商务投诉网</a></div><a href="https://www.bitekongjian.com/">比特空间</a><a href="http://lishi.fadeduo.com/">历史</a><a href="http://tiyu.fadeduo.com/">体育</a><a href="http://tansuo.caiding5.cn/">财丁</a><a href="http://che.yuansudz.com/">元素汽车</a><a href="http://cn.yuansudz.com/">元素科技</a><a href="http://www.kthtea.cn/">太和茶叶网</a><a href="http://cn.yexian114.com/">野仙生活网</a>
<a href="http://cn.gangyiku.com/">港易生活网</a><a href="http://www.bgjj.cc/">布谷家居网</a><a href="http://health.office369.com/">办公家居网</a><a href="http://travel.office369.com">办公旅游网</a><a href="http://ask.bgjj.cc/">家居问答网</a><a href="https://tuku.caiding5.net/">彩盯图库网</a><a href="https://www.picsok.com/">PICSOK</a><a href="http://xiaofei.caiding5.cn/">才丁消费网</a>        </div>
        <!--end::Copyright-->
        <!--begin::Menu-->
        <ul class="menu menu-gray-600 menu-hover-primary fw-semibold order-1">
                        <li class="menu-item">
                <a href="/changshi/" target="_blank" class="menu-link px-2">办公常识</a>
            </li>
                        <li class="menu-item">
                <a href="/baike/" target="_blank" class="menu-link px-2">办公百科</a>
            </li>
                        <li class="menu-item">
                <a href="/life/" target="_blank" class="menu-link px-2">办公生活</a>
            </li>
                    </ul>
        <!--end::Menu-->
    </div>
    <!--end::Footer container-->
</div>
<!--end::Footer-->
</div>
<!--end:::Main-->
</div>
<!--end::Wrapper-->
</div>
<!--end::Page-->
</div>
<!--end::App-->
<div id="kt_scrolltop" class="scrolltop" data-kt-scrolltop="true">
    <!--begin::Svg Icon | path: icons/duotune/arrows/arr066.svg-->
    <span class="svg-icon">
        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
            <rect opacity="0.5" x="13" y="6" width="13" height="2" rx="1" transform="rotate(90 13 6)" fill="currentColor"></rect>
            <path d="M12.5657 8.56569L16.75 12.75C17.1642 13.1642 17.8358 13.1642 18.25 12.75C18.6642 12.3358 18.6642 11.6642 18.25 11.25L12.7071 5.70711C12.3166 5.31658 11.6834 5.31658 11.2929 5.70711L5.75 11.25C5.33579 11.6642 5.33579 12.3358 5.75 12.75C6.16421 13.1642 6.83579 13.1642 7.25 12.75L11.4343 8.56569C11.7467 8.25327 12.2533 8.25327 12.5657 8.56569Z" fill="currentColor"></path>
        </svg>
    </span>
    <!--end::Svg Icon-->
</div>
<!--begin::Javascript-->
<script>var hostUrl = "/static/default/pc/";</script>
<!--begin::Global Javascript Bundle(mandatory for all pages)-->
<script src="/static/default/pc/plugins/global/plugins.bundle.js"></script>
<script src="/static/default/pc/js/scripts.bundle.js"></script>
<!--end::Global Javascript Bundle-->

<!--end::Javascript-->
</body>
<!--end::Body-->
</html>