21.8 Python 使用BeautifulSoup庫

lyshark發表於2023-10-27

BeautifulSoup庫用於從HTML或XML檔案中提取資料。它可以自動將複雜的HTML文件轉換為樹形結構,並提供簡單的方法來搜尋文件中的節點,使得我們可以輕鬆地遍歷和修改HTML文件的內容。廣泛用於Web爬蟲和資料抽取應用程式中。

讀者如果需要使用這個庫,同樣需要執行pip命令用以安裝:

21.8.1 屬性定位連結

透過HTML屬性我們可以輕鬆的實現對特定頁面特定元素的提取,如下程式碼我們首先封裝兩個函式,其中get_page_attrs函式用於一次性解析需求,函式search_page則用於多次對頁面進行解析,這兩個函式如果傳入attribute屬性則用於提取屬性內的引數,而傳入text則用於提取屬性自身文字。

import requests
from bs4 import BeautifulSoup

header = {"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98"}

# 引數1: 解析頁面URL
# 引數2: 需要解析的頁面定位
# 引數3: 提取標籤屬性
# 引數4:設定超時時間
# 引數5:設定返回型別(attribute 返回屬性欄位,text 返回文字欄位)
def get_page_attrs(url,regx,attrs,timeout,type):
    respon_page = []
    try:
        respon = requests.get(url=url, headers=header, timeout=timeout)
        if respon.status_code == 200:
            if respon != None:
                soup = BeautifulSoup(respon.text, "html.parser")
                ret = soup.select(regx)
                for item in ret:
                    if type == "attribute":
                        respon_page.append( str(item.attrs[attrs] ))
                    if type == "text":
                        respon_page.append(str(item.get_text()))

            return respon_page
        else:
            return None
    except Exception:
        return None
    return None

# 對頁面多次搜尋
# 引數1: 需要解析的html文字
# 引數2: 需要解析的頁面定位
# 引數3: 提取標籤屬性
# 引數5:設定返回型別(attribute 返回屬性欄位,text 返回文字欄位)
def search_page(data,regx,attrs,type):
    respon_page = []
    if data != None:
        soup = BeautifulSoup(data, "html.parser")
        ret = soup.select(regx)
        for item in ret:
            if type == "attribute":
                respon_page.append( str(item.attrs[attrs] ))
            if type == "text":
                respon_page.append(str(item.get_text()))
    return respon_page

透過使用上述兩個封裝函式,讀者就可以輕鬆的實現對特定網頁頁面元素的定位,首先我們透過CSS屬性定位一篇文章中的圖片連結,這段程式碼如下;

if __name__ == "__main__":
    # 透過CSS屬性定點陣圖片
    ref = get_page_attrs("https://www.cnblogs.com/LyShark/p/15914868.html",
                   "#cnblogs_post_body > p > img",
                   "src",
                   5,
                   "attribute"
                   )
    print(ref)

當上述程式碼執行後,即可提取出特定網址連結內,屬性#cnblogs_post_body > p > img中圖片的src屬性,並提取出圖片屬性attribute自身引數。

接著我們繼續使用該函式實現定位文章列表功能,文章列表的定位同理,此處第二個引數應修改為href屬性,如下程式碼分別使用兩種方式實現對文章列表的定位功能;

if __name__ == "__main__":
    # 定位文章列表,兩種方式均可
    ref = get_page_attrs("https://www.cnblogs.com/lyshark",
                   "#mainContent > div > div > div.postTitle > a",
                   "href",
                   5,
                   "attribute"
                   )
    print(ref)

    ref = get_page_attrs("https://www.cnblogs.com/lyshark",
                   "div[class='day'] div[class='postCon'] div a",
                   "href",
                   5,
                   "attribute"
                   )
    print(ref)

程式碼執行後即可輸出lyshark網站中主頁所有的文章地址資訊,輸出如下圖所示;

當需要定位文章內容時,我們只需要將第二個屬性更改為空格,並將第四個屬性修改為text此時則代表只提取屬性內的文字。

if __name__ == "__main__":
    # 定位文章文字欄位
    ref = get_page_attrs("https://www.cnblogs.com/lyshark",
                   "div[class='day'] div[class='postCon'] div[class='c_b_p_desc']",
                   "",
                   5,
                   "text"
                   )

    for index in ref:
        print(index)

執行上述程式碼片段,即可提取出主頁中所有的文字資訊,如下圖所示;

如果需要在同一個頁面中多次定位那麼就需要使用search_page函式了,如下程式碼中我們需要在一個頁面內尋找兩個元素,此時就需要定位兩次;

if __name__ == "__main__":
    respon = requests.get(url="https://yiyuan.9939.com/yyk_47122/", headers=header, timeout=5)

    ref = search_page(respon.text,
                      "body > div.hos_top > div > div.info > div.detail.word-break > h1 > a",
                      "",
                      "text"
                      )
    print(ref)

    ref = search_page(respon.text,
                      "body > div.hos_top > div > div.info > div.detail.word-break > div.tel > span",
                      "",
                      "text"
                      )
    print(ref)

程式碼執行後,即可透過依次請求,分別輸出該頁面中的兩個元素,如下圖所示;

21.8.2 查詢所有標籤

使用find_all函式,可實現從HTMLXML文件中查詢所有符合指定標籤和屬性的元素,返回一個列表,該函式從用於精確過濾,可同時將該頁中符合條件的資料一次性全部篩選出來。

其基本語法為:

find_all(name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
  • name:標籤名或列表,用於查詢指定標籤名的元素,如果為 True 或 None,則查詢所有標籤元素
  • attrs:字典,用於指定屬性名和屬性值,用於查詢具有指定屬性名和屬性值的元素
  • recursive:布林值,表示是否遞迴查詢子標籤,預設為 True
  • text:字串或正規表示式,用於匹配元素的文字內容
  • limit:整數,限制返回的匹配元素的數量
  • kwargs:可變引數,用於查詢指定屬性名和屬性值的元素

我們以輸出CVE漏洞列表為例,透過使用find_all查詢頁面中所有的a標籤,並返回一個列表,透過對列表元素的解析,依次輸出該漏洞的序號,網址,以及所對應的編號資訊。

import re
import requests
from bs4 import BeautifulSoup

header = {"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98"}

# 查詢文中 所有a標籤 且類名是c_b_p_desc_readmore的 並提取出其href欄位
# print(bs.find_all('a',class_='c_b_p_desc_readmore')[0]['href'])

# 提取 所有a標籤 且id等於blog_nav_admin 類等於menu 並提取出其href欄位
# print(bs.find_all('a',id='blog_nav_admin',class_='menu')[0]['href'])
# print(bs.find_all('a',id='blog_nav_admin',class_='menu')[0].attrs['href'])

if __name__ == "__main__":
    url = "https://cassandra.cerias.purdue.edu/CVE_changes/today.html"
    new_cve = []
    ret = requests.get(url=url, headers=header, timeout=5)
    soup = BeautifulSoup(ret.text, 'html.parser')
    for index in soup.find_all('a'):
        href = index.get('href')
        text = index.get_text()
        cve_number = re.findall("[0-9]{1,}-.*",index.get_text())
        print("序號: {:20} 地址: {} CVE-{}".format(text,href,cve_number[0]))

讀者可自行執行上述程式碼,即可匹配出當前頁面中所有的CVE漏洞編號等,如下圖所示;

21.8.3 取字串返回列表

在BeautifulSoup4中,stripped_strings是一個生成器物件,用於獲取HTML標籤內所有文字內容的迭代器。它會自動去除每個文字的前後空格和換行符,只返回純文字字串。stripped_strings可以用於處理HTML文件中的多行文字、空格等特殊符號,也可用於將元素下面的所有字串以列表的形式返回。

import requests
from bs4 import BeautifulSoup

header = {"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98"}

if __name__ == "__main__":
    ret = requests.get(url="https://www.cnblogs.com/lyshark", headers=header, timeout=3)
    text = str(ret.content.decode('utf-8'))

    bs = BeautifulSoup(text, "html.parser")
    ret = bs.select('#mainContent > div > div > div.postTitle > a > span')

    for i in ret:
        # 提取出字串並以列表的形式返回
        string_ = list(i.stripped_strings)
        print(string_)

執行後即可獲取選中元素的字串內容,並透過list將其轉換為列表格式,如下圖所示;

透過find_all以及stripped_strings屬性我們實現一個簡單的抓取天氣的程式碼,以讓讀者可以更好的理解該屬性是如何被使用的,如下程式碼所示;

from bs4 import BeautifulSoup
import requests

head = {'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
ret = requests.get(url="http://www.weather.com.cn/textFC/beijing.shtml", headers=head, timeout=3)
text = str(ret.content.decode('utf-8'))

bs = BeautifulSoup(text,"html.parser")

# 定位到第一個標籤上
bs.find_all('div',class_='conMidtab')[1]

# 在conMidtab裡面找tr標籤並從第3個標籤開始儲存
tr = bs.find_all('tr')[2:]

for i in tr:
    # 迴圈找程式碼中的所有td標籤
    td = i.find_all('td')
    # 找所有的td標籤,並找出第一個td標籤
    city_td = td[0]
    # 獲取目標路徑下所有的子孫非標籤字串,自動去掉空字串
    city = list(city_td.stripped_strings)[0]
    # 取出度數的標籤
    temp = td[-5]
    temperature = list(temp.stripped_strings)[0]
    print('城市:{}   溫度:{}'.format(city,temperature))

我們以提取北京天氣為案例,當執行程式碼後即可取出北京市所有地區的氣溫資料,如下圖所示;

本文作者: 王瑞
本文連結: https://www.lyshark.com/post/ac89ee84.html
版權宣告: 本部落格所有文章除特別宣告外,均採用 BY-NC-SA 許可協議。轉載請註明出處!

相關文章