python爬蟲之BeautifulSoup4使用

来源:https://www.cnblogs.com/jiba/archive/2022/08/22/16613724.html
-Advertisement-
Play Games

鋼鐵知識庫,一個學習python爬蟲、數據分析的知識庫。人生苦短,快用python。 上一章我們講解針對結構化的html、xml數據,使用Xpath實現網頁內容爬取。本章我們再來聊另一個高效的神器:Beautiful Soup4。相比於傳統正則表達方式去解析網頁源代碼,這個就簡單得多,實踐是檢驗真理 ...


鋼鐵知識庫,一個學習python爬蟲、數據分析的知識庫。人生苦短,快用python。

上一章我們講解針對結構化的htmlxml數據,使用Xpath實現網頁內容爬取。本章我們再來聊另一個高效的神器:Beautiful Soup4。相比於傳統正則表達方式去解析網頁源代碼,這個就簡單得多,實踐是檢驗真理的唯一標準,話不多說直接上號開搞驗證。

Beautiful Soup 簡介

首先說說BeautifulSoup是什麼。簡單來說,這是Python的一個HTML或XML的解析庫,我們可以用它方便從網頁中提取數據,官方解釋如下:

BeautifulSoup 提供一些簡單的、Python 式的函數用來處理導航、搜索、修改分析樹等功能。它是一個工具箱,通過解析文檔為用戶提供需要抓取的數據,因為簡單,所以不需要多少代碼就可以寫出一個完整的應用程式。 BeautifulSoup 自動將輸入文檔轉換為 Unicode 編碼,輸出文檔轉換為 utf-8 編碼。你不需要考慮編碼方式,除非文檔沒有指定一個編碼方式,這時你僅僅需要說明一下原始編碼方式就可以了。 BeautifulSoup 已成為和 lxml、html5lib 一樣出色的 Python 解釋器,為用戶靈活地提供不同的解析策略或強勁的速度。

所以,利用它可以省去很多繁瑣的提取工作,提高解析效率。

BeautifulSoup 安裝

BeautifulSoup3 目前已經停止開發,推薦使用 BeautifulSoup4,不過它也被移植到bs4了,也就是說導入時我們需要import bs4

在開始之前,請確保已經正確安裝beautifulsoup4lxml,使用pip安裝命令如下:

pip install beautifulsoup4
pip install lxml

解析器

BeautifulSoup在解析時實際上依賴解析器。除了支持Python標準庫中的HTML解析器,還支持一些第三方的解析器,如果不安裝它,則Python會使用預設的解析器。

下麵列出BeautifulSoup支持的解析器

解析器 使用方法 優勢 劣勢
Python 標準庫 BeautifulSoup(markup, "html.parser") Python 的內置標準庫、執行速度適中 、文檔容錯能力強 Python 2.7.3 or 3.2.2) 前的版本中文容錯能力差
LXML HTML 解析器 BeautifulSoup(markup, "lxml") 速度快、文檔容錯能力強 需要安裝 C 語言庫
LXML XML 解析器 BeautifulSoup(markup, "xml") 速度快、唯一支持 XML 的解析器 需要安裝 C 語言庫
html5lib BeautifulSoup(markup, "html5lib") 最好的容錯性、以瀏覽器的方式解析文檔、生成 HTML5 格式的文檔 速度慢、不依賴外部擴展

通過上面可以看出,lxml 有解析HTML和XML的功能,相比預設的HTML解析器更加強大,速度,容錯能力強。

推薦使用它,下麵統一使用lxml進行演示。使用時只需在初始化時第二個參數改為 lxml 即可。

from bs4 import BeautifulSoup
soup = BeautifulSoup('<p>Hello</p>', 'lxml')
print(soup.p.string)
'''
Hello
'''

基本使用

下麵舉個實例來看看BeautifulSoup的基本用法:

html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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, 'lxml')  # 初始化
print(soup.prettify())
print(soup.title.string)

運行結果,你們也可以將上面代碼複製到編輯器執行看看:

<html>
 <head>
  <title>
   The Dormouse's story
  </title>
 </head>
 <body>
  <p class="title" name="dromouse">
   <b>
    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>
</html>
The Dormouse's story

首先聲明一個html變數,它是一個HTML字元串,註意html和body標簽都沒有閉合。

經過初始化,使用prettify()方法把要解析的字元串以標準縮進格式輸出,發現結果中自動補全了html和body標簽。這一步不是prettify()方法做的,而是在初始化BeautifulSoup時就完成了。然後調用soup.title.string拿到title裡面的文本內容。

通過簡單調用幾個屬性完成文本提取,是不是非常方便呢?

節點選擇器

直接調用節點的名稱就可以選擇節點元素,再調用 string 屬性就可以得到節點內的文本了,這種選擇方式速度非常快。如果單個節點結構層次非常清晰,可以選用這種方式來解析。

選擇元素

還是以上面的HTML代碼為例,詳細說明選擇元素的方法:

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.title)
print(type(soup.title))
print(soup.title.string)
print(soup.head)
print(soup.p)
'''
<title>The Dormouse's story</title>
<class 'bs4.element.Tag'>
The Dormouse's story
<head><title>The Dormouse's story</title></head>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
'''

首先輸出title節點的選擇結果,包含標簽。

接下來輸出它的類型,是一個bs4.element.Tag類型,Tag具有一些屬性,比如string。

調用string屬性可以看到輸出節點的文本內容。

繼續嘗試head、p節點。發現p只取了第一個匹配的節點。說明當有多個節點時只取一個。

獲取屬性

每個節點可能有多個屬性比如id 、class等,選擇元素後可以調用attrs獲取所有屬性:

print(soup.p.attrs)
print(soup.p.attrs['name'])
'''
{'class': ['title'], 'name': 'dromouse'}
dromouse
'''

可以看到attrs返回結果是字典,它把選擇節點所有屬性都組合成一個字典。取值直接按字典方式即可。

當然還有一種更簡單的獲取方式:不寫attrs,直接在元素後面中括弧取值也行:

print(soup.p['name'])
print(soup.p['class'])
'''
dromouse
['title']
'''

但是註意區分:有的返回字元串、有的返回字元串組成的列表。

對於class,一個節點元素可能有多個class,所以返回的是列表。

子節點和子孫節點

選取節點元素之後,如果想要獲取它的直接子節點,可以調用 contents 屬性,示例如下:

html4 = """
<html>
    <head>
        <title>The Dormouse's story</title>
    </head>
    <body>
        <p class="story">
            鋼鐵知識庫
            <a href="http://a.com" class="鋼鐵學數據分析" id="link1">
                <span>Elsie</span>
            </a>
            <a href="http://b.com" class="鋼鐵學自動化" id="link2">Lacie</a> 
            and
            <a href="http://example.com" class="cccc" id="link3">Tillie</a>
            鋼鐵學爬蟲.
        </p>
        <p class="story">...</p>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html4, 'lxml')
print(soup.p.contents)
'''
['\n            鋼鐵知識庫\n            ', <a class="鋼鐵學數據分析" href="http://a.com" id="link1">
<span>Elsie</span>
</a>, '\n', <a class="鋼鐵學自動化" href="http://b.com" id="link2">Lacie</a>, ' \n            and\n            ', <a class="cccc" href="http://example.com" id="link3">Tillie</a>, '\n            鋼鐵學爬蟲.\n        ']

'''

可以看到返回結果是列表形式。p 節點里既包含節點,又包含文本,最後統一返回列表。

需要註意,列表中的每個元素都是 p 節點的直接子節點。比如第一個 a 節點裡面的span節點,這相當於子孫節點了,但返回結果並沒有單獨把span節點列出來。所以說,contents屬性得到的結果是直接子節點的列表。

同樣,我們可以調用children屬性得到相應的結果:

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.p.children)
for i, child in enumerate(soup.p.children):
    print(i, child)
'''
<list_iterator object at 0x0000000001D9A1C0>
0 
            鋼鐵知識庫
            
1 <a class="鋼鐵學數據分析" href="http://a.com" id="link1">
<span>Elsie</span>
</a>
2 

3 <a class="鋼鐵學自動化" href="http://b.com" id="link2">Lacie</a>
4  
            and
            
5 <a class="cccc" href="http://example.com" id="link3">Tillie</a>
6 
            鋼鐵學爬蟲.
'''

還是同樣的 HTML 文本,這裡調用了 children 屬性來選擇,返回結果是生成器類型。接下來,我們用 for 迴圈輸出相應的內容。

如果要得到所有的子孫節點的話,可以調用 descendants 屬性:

<generator object Tag.descendants at 0x000001D77A90E570>
0 
            鋼鐵知識庫
            
1 <a class="鋼鐵學數據分析" href="http://a.com" id="link1">
<span>Elsie</span>
</a>
2 

3 <span>Elsie</span>
4 Elsie
5 

6 

7 <a class="鋼鐵學自動化" href="http://b.com" id="link2">Lacie</a>
8 Lacie
9  
            and
            
10 <a class="cccc" href="http://example.com" id="link3">Tillie</a>
11 Tillie
12 
            鋼鐵學爬蟲.

此時返回結果還是生成器。遍歷輸出一下可以看到,這次的輸出結果就包含了 span 節點。descendants 會遞歸查詢所有子節點,得到所有的子孫節點。

除此之外,還有父節點parent 和祖先節點parents,兄弟節點next_siblingprevious_siblings 日常用得少不再演示,後續需要自行查官方文檔即可。

方法選擇器

前面聊的通過屬性選擇節點,但如果進行比較複雜的話還是比較繁瑣。幸好BeautifulSoup還為我們提供另外一些查詢方法,比如find_all 和 find ,調用他們傳入相應參數就可以靈活查詢。

find_all

顧名思義,就是查詢所有符合條件的元素,可以給它傳入一些屬性或文本來得到符合條件的元素,功能十分強大。

它的 API 如下:

find_all(name , attrs , recursive , text , **kwargs)

我們可以根據節點名來查詢元素,下麵我們用一個實例來感受一下:

html5='''
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1">
            <li class="element">鋼鐵</li>
            <li class="element">知識</li>
            <li class="element">倉庫</li>
        </ul>
        <ul class="list list-small" id="list-2">
            <li class="element">python</li>
            <li class="element">java</li>
        </ul>
    </div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html5, 'lxml')
print(soup.find_all(name='ul'))
print(type(soup.find_all(name='ul')[0]))
'''
[<ul class="list" id="list-1">
<li class="element">鋼鐵</li>
<li class="element">知識</li>
<li class="element">倉庫</li>
</ul>, <ul class="list list-small" id="list-2">
<li class="element">python</li>
<li class="element">java</li>
</ul>]
<class 'bs4.element.Tag'>
'''

可以看到返回了一個列表,分別是兩個ul長度為2,且類型依然是bs4.element.Tag類型。

因為都是Tag類型,所以依然可以繼續嵌套查詢,還是同樣文本,查詢ul節點後再繼續查詢內部li節點。

from bs4 import BeautifulSoup
soup = BeautifulSoup(html5, 'lxml')
for ul in soup.find_all(name='ul'):
    print(ul.find_all(name='li'))
'''
[<li class="element">鋼鐵</li>, <li class="element">知識</li>, <li class="element">倉庫</li>]
[<li class="element">python</li>, <li class="element">java</li>]
'''

返回結果是列表類型,元素依然是Tag類型。

接下來我們可以遍歷每個li獲取它的文本:

for ul in soup.find_all(name='ul'):
    print(ul.find_all(name='li'))
    for li in ul.find_all(name='li'):
        print(li.string)
'''
[<li class="element">鋼鐵</li>, <li class="element">知識</li>, <li class="element">倉庫</li>]
鋼鐵
知識
倉庫
[<li class="element">python</li>, <li class="element">java</li>]
python
java
'''

find

除了 find_all 方法,還有 find 方法,不過 find 方法返回的是單個元素,也就是第一個匹配的元素,而 find_all 返回的是所有匹配的元素組成的列表。示例如下:

html5='''
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1">
            <li class="element">鋼鐵</li>
            <li class="element">知識</li>
            <li class="element">倉庫</li>
        </ul>
        <ul class="list list-small" id="list-2">
            <li class="element">python</li>
            <li class="element">java</li>
        </ul>
    </div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html5, 'lxml')
print(soup.find(name='ul'))
print(type(soup.find(name='ul')))
print(soup.find(class_='list'))


'''
<ul class="list" id="list-1">
<li class="element">鋼鐵</li>
<li class="element">知識</li>
<li class="element">倉庫</li>
</ul>
<class 'bs4.element.Tag'>
<ul class="list" id="list-1">
<li class="element">鋼鐵</li>
<li class="element">知識</li>
<li class="element">倉庫</li>
</ul>
'''

返回結果不再是列表形式,而是第一個匹配的節點元素,類型依然是 Tag 類型。

其它方法

另外還有許多的查詢方法,用法與前面介紹的 find_all、find 方法完全相同,只不過查詢範圍不同,在此做一下簡單的說明。

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選擇器

BeautifulSoup還提供了另外一種選擇器,CSS選擇器。如果對 Web 開發熟悉的話,那麼對 CSS 選擇器肯定也不陌生。如果不熟悉的話,可以參考 http://www.w3school.com.cn/cssref/css_selectors.asp 瞭解。

使用 CSS 選擇器,只需要調用 select 方法,傳入相應的 CSS 選擇器即可,我們用一個實例來感受一下:

html5='''
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1">
            <li class="element">鋼鐵</li>
            <li class="element">知識</li>
            <li class="element">倉庫</li>
        </ul>
        <ul class="list list-small" id="list-2">
            <li class="element">python</li>
            <li class="element">java</li>
        </ul>
    </div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html5, 'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select('#list-2 .element'))
print(type(soup.select('ul')[0]))
'''
[<div class="panel-heading">
<h4>Hello</h4>
</div>]
[<li class="element">鋼鐵</li>, <li class="element">知識</li>, <li class="element">倉庫</li>, <li class="element">python</li>, <li class="element">java</li>]
[<li class="element">python</li>, <li class="element">java</li>]
<class 'bs4.element.Tag'>
'''

結果為所有匹配的節點。例如select('ul li')則是所有ul節點下麵的所有li節點,返回結果是列表。

select 方法同樣支持嵌套選擇(soup.select('ul'))、屬性獲取(ul['id']),以及文本獲取(li.string/li.get_text())

---- 鋼鐵知識庫 2022.08.22

結語

到此 BeautifulSoup 的使用介紹基本就結束了,最後鋼鐵知識庫做一下簡單的總結:

  • 推薦使用 LXML 解析庫,速度快、容錯能力強。
  • 建議使用 find、find_all 方法查詢匹配單個結果或者多個結果。
  • 如果對 CSS 選擇器熟悉的話可以使用 select 匹配,可以像Xpath一樣匹配所有。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 同步、非同步,併發、並行、串列,這些名詞在我們的開發中會經常遇到,這裡對非同步編程做一個詳細的歸納總結,希望可以對這方面的開發有一些幫助。 ...
  • 我們從應用的視角出發整理抽象了我們在訪問、使用資料庫時場景的一些穩定性治理、性能優化、提效等方面的實戰經驗,對於每一個後端應用來說,資料庫無疑是重中之重,我們希望通過我們的資料庫治理能力,可以幫助到大家更好地使用資料庫服務。 本文將詳細介紹 MSE 資料庫治理的熱點功能,動態讀寫分離的設計與實現。 ...
  • 設計原則為提高可維護性和可復用性而生,每一種設計模式都符合一個或多個設計原則,因此設計原則也是評價一個設計模式使用效果的重要指標之一。 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 關於安裝 進入pom.xml文件目錄下,使用命令mvn install 當然也可以使用類似idea這類本身已經集成好maven插件按鈕的編輯器進行安裝 安裝過程會輸出安裝jar包的目錄信息,同樣的,跟class編譯後的目標文件一起,放在target目錄下 註意:開發編譯過程中,如果只 編譯 mvn ...
  • 編碼問題,誰不想避其鋒芒; 一、業務背景 在搜索引擎的功能上,曾經遇到過這樣一個問題,資料庫中某個公司名稱中存在特殊編碼,儘管數據已經正常同步到索引中,但是系統中關鍵詞始終也無法匹配到該公司; 然後在庫中模糊匹配,將公司名稱複製到搜索框中,這樣就可以正常命中索引,那麼問題也就很清楚了,這種數據"隱身 ...
  • Python小游戲——外星人入侵(保姆級教程) 第一章:武裝飛船 06:讓飛船移動 下麵來讓玩家能夠左右移動飛船。我們將編寫代碼,在用戶按左或右箭頭鍵時做出響應。我們將首先專註於向右移動,再使用同樣的原理來控制向左移動。通過這樣做,你將學會如何控制屏幕圖像的移動。 ...
  • 哈嘍,大家好,今天咱們試試只用20行代碼來實現批量獲取網抑雲文件保存本地,炒雞簡單! 悄悄的告訴你,其實不到20行代碼~ 你需要準備 本次使用的環境是Python3.8,編輯器是pycharm 模塊使用的是requests、re、os 三個,其中requests是第三方模塊,需要手動安裝一下,re、 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...