Python每日一練(9)-批量爬取B站小視頻

1. 批量爬取B站小視頻

嗶哩嗶哩網站(英文名稱: bilibili),是年輕人的文化社區,被粉絲們親切的稱爲B站。該網站中擁有動畫、番劇、國創、音樂、舞蹈、遊戲、科技、生活、鬼畜、娛樂、時尚等多個內容分區。那麼我們能不能爬取一些視頻以後離線觀看呢?答案是肯定的。本任務要求使用Python語言中的爬蟲技術,實現批量爬取B站小視頻的爬蟲程序。爬取後的效果下圖所示。
在這裏插入圖片描述
分析過程如下:
在這裏插入圖片描述
在這裏插入圖片描述
點擊上面的排行榜之後,按F12調出瀏覽器控制檯調試界面,點擊Network,第一次進入Network可能是空的,按F5或者點擊瀏覽器左上角刷新一下即可,最後如圖所示。
在這裏插入圖片描述
但是需要注意的是,不是所有的數據都是根據一個固定的url返回的,如圖所示。
在這裏插入圖片描述
接下來就可以去完成代碼了,注意,因爲爬蟲需要使用第三方模塊requests,所以讀者需要使用如下命令進行安裝。

pip install --user  -i http://pypi.douban.com/simple --trusted-host pypi.douban.com requests

如圖所示:
在這裏插入圖片描述
示例代碼如下:

import requests  # 網絡請求模塊
import os  # 系統模塊
import time  # 時間模塊
import re  # 正則模塊
import random  # 隨機模塊

json_url = "https://api.vc.bilibili.com/board/v1/ranking/top?page_size=10&next_offset={}1&tag=%E4%BB%8A%E6%97%A5%E7%83%AD%E9%97%A8&platform=pc"  # 嗶哩嗶哩小視頻json地址


class MySpider(object):  # 定義一個spider類
    # 初始化
    def __init__(self):
        # 構造請求頭
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit"
                          "/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"}

    # 請求json數據
    def get_json(self, url):
        response = requests.get(url, headers=self.headers)
        # 根據返回的狀態碼判斷是否請求成功
        if response.status_code == 200:
            return response.json()  # 返回json信息
        else:
            print("獲取json信息的請求沒有成功~")

    #  下載視頻
    def download_video(self, url, video_name):
        # 下載視頻的網絡請求
        response = requests.get(url, headers=self.headers, stream=True)
        if not os.path.exists("video"):  # 判斷本地是否存在video文件夾 不存在則創建
            os.mkdir("video")
        # 根據返回的狀態碼判斷是否請求成功
        if response.status_code == 200:
            with open("video/" + video_name + ".mp4", "wb") as file:  # 非純文本都以字節的方式寫入
                for data in response.iter_content(chunk_size=1024):  # 循環寫入
                    file.write(data)  # 寫入視頻文件
                    file.flush()  # 刷新緩存
                print("視頻下載完成~")
        else:
            print("視頻下載失敗~")


if __name__ == '__main__':  # 程序的入口
    spider = MySpider()
    for i in range(10):  # 100條數據 需要發送10次循環 所以需要循環10次
        json = spider.get_json(json_url.format(i))
        video_infos = json["data"]["items"]  # 信息集 一個列表
        # 遍歷 獲取每一個video的信息
        for video_info in video_infos:
            # 視頻的名字只保留標題中英文、數字與漢字 便於寫入文件
            title = video_info["item"]["description"]
            comp = re.compile("[^A-Z^a-z^0-9^\u4e00-\u9fa5]")
            title = comp.sub("", title)  # 其他字符一律替換爲空
            video_url = video_info["item"]["video_playurl"]  # 視頻地址
            print(title, video_url)  # 打印提取的視頻標題與視頻地址
            spider.download_video(video_url, title)  # 下載視頻 視頻標題作爲視頻的名字
        time.sleep(random.randint(3, 6))  # 避免頻繁發送請求 ip被封

2. 獲取動態請求的JSON數據

在上面的批量爬取B站小視頻任務中,我們已經通過發送動態請求的方式,獲取到視頻的標題與視頻地址。爲了更好的掌握JSON數據的提取技術,此次任務要求獲取JSON中視頻發佈時間用戶名稱以及觀看人數並打印。在PyCharm控制檯輸出的結果如圖所示。
在這裏插入圖片描述
這個任務的話其實就是在之前代碼的基礎上修改了一小部分,如圖所示:
在這裏插入圖片描述

3. 隨機生成瀏覽器的頭部信息

有時在請求一個網頁內容時,如果頻繁地使用一個固定的瀏覽器頭部信息發送網絡請求時,可能會出現403錯誤。產生這種錯誤是由於該網頁爲了防止惡意採集信息而使用了反爬蟲設置,從而拒絕了用戶的訪問。所以本任務要求實現每發送一個網絡請求,就更換一個瀏覽器的頭部信息,避免使用固定的瀏覽器頭部信息。在PyCharm控制檯輸出的結果如圖所示。
在這裏插入圖片描述
安裝fake_useragent模塊並初步瞭解其基本應用,使用pip命令安裝fake_useragent模塊的命令如下:

pip install --user  -i http://pypi.douban.com/simple --trusted-host pypi.douban.com  fake_useragent

如圖所示:
在這裏插入圖片描述
在這次的任務中,還需要一個json格式的文件,下載地址爲:

鏈接:https://pan.baidu.com/s/1IeW70k6pd1HMZqOQ0jw1FQ  密碼:t67s

示例代碼如下:

import requests  # 網絡請求模塊
import os  # 系統模塊
import time  # 時間模塊
import re  # 正則模塊
import random  # 隨機模塊
from fake_useragent import UserAgent  # 導入僞造頭部信息的模塊

json_url = "https://api.vc.bilibili.com/board/v1/ranking/top?page_size=10&ne" \
           "xt_offset={}1&tag=%E4%BB%8A%E6%97%A5%E7%83%AD%E9%97%A8&platform=pc"  # 嗶哩嗶哩小視頻json地址


class MySpider(object):  # 定義一個spider類
    def get_json(self, url):  # 請求json數據
        headers = {"User-Agent": UserAgent(path="fake_useragent.json").random}  # 創建隨機生成的頭部信息
        print(f"當前下載請求的瀏覽器頭部信息爲: {headers}")
        response = requests.get(url, headers=headers)
        # 根據返回的狀態碼判斷是否請求成功
        if response.status_code == 200:
            return response.json()  # 返回json信息
        else:
            print("獲取json信息的請求沒有成功~")

    #  下載視頻
    def download_video(self, url, video_name):
        headers = {"User-Agent": UserAgent(path="fake_useragent.json").random}  # 創建隨機生成的頭部信息
        # 下載視頻的網絡請求
        response = requests.get(url, headers, stream=True)
        if not os.path.exists("video"):  # 判斷本地是否存在video文件夾 不存在則創建
            os.mkdir("video")
        # 根據返回的狀態碼判斷是否請求成功
        if response.status_code == 200:
            with open("video/" + video_name + ".mp4", "wb") as file:  # 非純文本都以字節的方式寫入
                for data in response.iter_content(chunk_size=1024):  # 循環寫入
                    file.write(data)  # 寫入視頻文件
                    file.flush()  # 刷新緩存
                print("視頻下載完成~")
        else:
            print("視頻下載失敗~")


if __name__ == '__main__':  # 程序的入口
    spider = MySpider()
    for i in range(10):  # 100條數據 需要發送10次循環 所以需要循環10次
        json = spider.get_json(json_url.format(i))
        video_infos = json["data"]["items"]  # 信息集 一個列表
        # 遍歷 獲取每一個video的信息
        for video_info in video_infos:
            # 視頻的名字只保留標題中英文、數字與漢字 便於寫入文件
            title = video_info["item"]["description"]
            comp = re.compile("[^A-Z^a-z^0-9^\u4e00-\u9fa5]")
            title = comp.sub("", title)  # 其他字符一律替換爲空
            video_url = video_info["item"]["video_playurl"]  # 視頻地址
            upload_time = video_info["item"]["upload_time"]  # 視頻發佈日期
            user_name = video_info["user"]["name"]  # 用戶名字
            watched_num = video_info["item"]["watched_num"]  # 觀看人數
            print(f"視頻標題爲: {title}")
            print(f"發佈時間爲: {upload_time}")
            print(f"視頻地址爲: {video_url}")
            print(f"觀看人數爲: {watched_num}")
            # spider.download_video(video_url, title)  # 下載視頻 視頻標題作爲視頻的名字
        time.sleep(random.randint(3, 6))  # 避免頻繁發送請求 ip被封

4. 獲取要下載視頻的大小

在多數網站中下載視頻、音樂以及文本文件時,都可以看見當前文件的大小,如下圖所示。
在這裏插入圖片描述
本任務要求通過requests模塊下載指定視頻內容時,獲取其視頻的文件大小。在PyCharm控制檯輸出的結果如下圖所示。
在這裏插入圖片描述
示例代碼如下:

import requests  # 網絡請求模塊
import os  # 系統模塊
import time  # 時間模塊
import re  # 正則模塊
import random  # 隨機模塊
from fake_useragent import UserAgent  # 導入僞造頭部信息的模塊

json_url = "https://api.vc.bilibili.com/board/v1/ranking/top?page_size=10&ne" \
           "xt_offset={}1&tag=%E4%BB%8A%E6%97%A5%E7%83%AD%E9%97%A8&platform=pc"  # 嗶哩嗶哩小視頻json地址


class MySpider(object):  # 定義一個spider類
    def get_json(self, url):  # 請求json數據
        headers = {"User-Agent": UserAgent(path="fake_useragent.json").random}  # 創建隨機生成的頭部信息
        response = requests.get(url, headers=headers)
        # 根據返回的狀態碼判斷是否請求成功
        if response.status_code == 200:
            return response.json()  # 返回json信息
        else:
            print("獲取json信息的請求沒有成功~")

    #  下載視頻
    def download_video(self, url, video_name):
        headers = {"User-Agent": UserAgent(path="fake_useragent.json").random}  # 創建隨機生成的頭部信息
        # 下載視頻的網絡請求
        response = requests.get(url, headers=headers, stream=True)
        content_size = int(response.headers["content-length"])  # 視頻內容的總大小
        if not os.path.exists("video"):  # 判斷本地是否存在video文件夾 不存在則創建
            os.mkdir("video")
        # 根據返回的狀態碼判斷是否請求成功
        if response.status_code == 200:
            # 1MB=1024KB 1KB=1024B 我們返回的是多少B 推出==>KB==>MB
            print("視頻文件大小: %0.2fMB" % (content_size / 1024 / 1024))  # 換算單位
            with open("video/" + video_name + ".mp4", "wb") as file:  # 非純文本都以字節的方式寫入
                for data in response.iter_content(chunk_size=1024):  # 循環寫入
                    file.write(data)  # 寫入視頻文件
                    file.flush()  # 刷新緩存
                print("視頻下載完成~")
        else:
            print("視頻下載失敗~")


if __name__ == '__main__':  # 程序的入口
    spider = MySpider()
    ranking = 0  # 排名
    for i in range(10):  # 100條數據 需要發送10次循環 所以需要循環10次
        json = spider.get_json(json_url.format(i))
        video_infos = json["data"]["items"]  # 信息集 一個列表
        # 遍歷 獲取每一個video的信息
        for video_info in video_infos:
            ranking += 1
            print(f"正在下載排名第 {ranking} 的視頻")
            # 視頻的名字只保留標題中英文、數字與漢字 便於寫入文件
            title = video_info["item"]["description"]
            comp = re.compile("[^A-Z^a-z^0-9^\u4e00-\u9fa5]")
            title = comp.sub("", title)  # 其他字符一律替換爲空
            video_url = video_info["item"]["video_playurl"]  # 視頻地址
            print(f"視頻標題爲: {title}")
            print(f"視頻地址爲: {video_url}")
            spider.download_video(video_url, title)  # 下載視頻 視頻標題作爲視頻的名字
        time.sleep(random.randint(3, 6))  # 避免頻繁發送請求 ip被封

5. 實時打印文件下載進度

在多數網站中下載視頻、音樂以及文本文件時,都可以看見當前文件的大小以及已經下載的大小,如下圖所示。
在這裏插入圖片描述
在之前的任務中,我們已經實現了顯示當前文件的大小。本任務要求將下載文件的實時進度打印出來。在PyCharm控制檯輸出的結果如下圖所示。
在這裏插入圖片描述
示例代碼如下:

import requests  # 網絡請求模塊
import os  # 系統模塊
import time  # 時間模塊
import re  # 正則模塊
import random  # 隨機模塊
from fake_useragent import UserAgent  # 導入僞造頭部信息的模塊

json_url = "https://api.vc.bilibili.com/board/v1/ranking/top?page_size=10&ne" \
           "xt_offset={}1&tag=%E4%BB%8A%E6%97%A5%E7%83%AD%E9%97%A8&platform=pc"  # 嗶哩嗶哩小視頻json地址


class MySpider(object):  # 定義一個spider類
    def get_json(self, url):  # 請求json數據
        headers = {"User-Agent": UserAgent(path="fake_useragent.json").random}  # 創建隨機生成的頭部信息
        response = requests.get(url, headers=headers)
        # 根據返回的狀態碼判斷是否請求成功
        if response.status_code == 200:
            return response.json()  # 返回json信息
        else:
            print("獲取json信息的請求沒有成功~")

    #  下載視頻
    def download_video(self, url, video_name):
        size = 0  # 記錄疊加每次寫入的大小
        headers = {"User-Agent": UserAgent(path="fake_useragent.json").random}  # 創建隨機生成的頭部信息
        # 下載視頻的網絡請求
        response = requests.get(url, headers=headers, stream=True)
        content_size = int(response.headers["content-length"])  # 視頻內容的總大小
        if not os.path.exists("video"):  # 判斷本地是否存在video文件夾 不存在則創建
            os.mkdir("video")
        # 根據返回的狀態碼判斷是否請求成功
        if response.status_code == 200:
            # 1MB=1024KB 1KB=1024B 我們返回的是多少B 推出==>KB==>MB
            print("視頻文件大小: %0.2fMB" % (content_size / 1024 / 1024))  # 換算單位
            with open("video/" + video_name + ".mp4", "wb") as file:  # 非純文本都以字節的方式寫入
                for data in response.iter_content(chunk_size=1024):  # 循環寫入
                    file.write(data)  # 寫入視頻文件
                    file.flush()  # 刷新緩存
                    size += len(data)  # 疊加每次寫入的大小
                    # 打印下載進度
                    print("\r 文件下載進度:%d%%(%0.2fMB/%0.2fMB)" % (
                        float(size / content_size * 100), (size / 1024 / 1024),
                        (content_size / 1024 / 1024)),
                          end=" ")
        else:
            print("視頻下載失敗~")


if __name__ == '__main__':  # 程序的入口
    spider = MySpider()
    ranking = 0  # 排名
    for i in range(10):  # 100條數據 需要發送10次循環 所以需要循環10次
        json = spider.get_json(json_url.format(i))
        video_infos = json["data"]["items"]  # 信息集 一個列表
        # 遍歷 獲取每一個video的信息
        for video_info in video_infos:
            ranking += 1
            print(f"正在下載排名第 {ranking} 的視頻")
            # 視頻的名字只保留標題中英文、數字與漢字 便於寫入文件
            title = video_info["item"]["description"]
            comp = re.compile("[^A-Z^a-z^0-9^\u4e00-\u9fa5]")
            title = comp.sub("", title)  # 其他字符一律替換爲空
            video_url = video_info["item"]["video_playurl"]  # 視頻地址
            print(f"視頻標題爲: {title}")
            print(f"視頻地址爲: {video_url}")
            spider.download_video(video_url, title)  # 下載視頻 視頻標題作爲視頻的名字
        time.sleep(random.randint(3, 6))  # 避免頻繁發送請求 ip被封
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章