嗶哩嗶哩熱榜爬蟲程序及數據處理


 完整的代碼與結果在最下面

一、設計方案

1.爬蟲的目標是嗶哩嗶哩排行榜上視頻的信息https://www.bilibili.com/ranking/all/0/1/7)


2.爬取的內容包括網頁上顯示的所有內容,有排名標題,播放量,彈幕數,up,得分以及視頻的url

 

3.設計方案:根據作業的要求,制作爬蟲程序爬取信息並進行數據處理,整個程序分成四個部分,包括數據爬取:(get_rank),數據清洗與處理:(rubbish),文本分析生成詞雲:(message),數據分析與可視化:(watch)四個部分,所用到的庫有request,BeautifulSoup,csv,collections,jieba,io,wordcloud,matplotlib。使用的IDE為anaconda環境配置的pycharm

 

 

設計難點:難點主要在數據處理方面,因為嗶哩嗶哩排行榜上爬取到的數據全都不是單純的數字,所以在數據處理時遇到了許多問題,這方面查找資料的時間也最長

 

 

二、主題頁面的結構特征分析

 

通過觀察網頁代碼,發現排行榜上的每一個視頻的標簽為li,類名為rank-item,並且我們通過len函數打印提取到的數量也正好100符合排行榜的視頻量,所以我們可以在當中繼續得到視頻其他信息的屬性,在代碼中都有注釋。

 

 

 

 

三、網絡爬蟲程序設計

# 代碼中注釋部分的print都是為了調試用的

1.數據爬取(get_rank)

在保存數據部分用了self函數創建數據對象來將數據讀入video對象中並且放在一個列表里,最后保存在csv文件中

import csv
import requests
from bs4 import BeautifulSoup
import pandas as pd
from collections import OrderedDict
import jieba
import io
from wordcloud import WordCloud
import matplotlib.pyplot as plt


# 注釋部分的print都是為了調試用的


def get_rank():  # 數據爬取與采集
    # 發起網絡請求
    url = 'https://www.bilibili.com/ranking/all/0/1/7'
    response = requests.get(url)
    html_text = response.text
    soup = BeautifulSoup(html_text, 'html.parser')

    # 用來保存視頻信息的對象
    class Video:
        def __init__(self, rank, title, point, visit, review, up, url):
            self.rank = rank
            self.title = title
            self.point = point
            self.visit = visit
            self.review = review
            self.up = up
            self.url = url

        def to_csv(self):
            return [self.rank, self.title, self.point, self.visit, self.review, self.up, self.url]

        @staticmethod
        def csv_title():
            return ['排名', '標題', '分數', '播放量', '彈幕數', 'UP', 'URL']

    # 提取列表
    items = soup.find_all('li', {'class': 'rank-item'})
    videos = []  # 保存提取出來的video
    for itm in items:
        title = itm.find('a', {'class': 'title'}).text  # 標題
        point = itm.find('div', {'class': 'pts'}).text  # 綜合得分
        rank = itm.find('div', {'class': 'num'}).text  # 排名
        visit = itm.find('span', {'class': 'data-box'}).text  # 播放量
        review = itm.find_all('span', {'class': 'data-box'})[1].text  # 彈幕數
        up = itm.find_all('span', {'class': 'data-box'})[2].text  # up
        url = itm.find('a', {'class': 'title'}).get('href')  # 獲取鏈接
        v = Video(rank, title, point, visit, review, up, url)
        videos.append(v)
    # 保存
    file_name = f'top100.csv'
    with open(file_name, 'w', newline='') as f:
        pen = csv.writer(f)
        pen.writerow(Video.csv_title())
        for v in videos:
            pen.writerow(v.to_csv())  # 導出數據到csv文件中
    print('保存csv成功')


get_rank()
get_rank

 

 

結果:

 

 

 

 

 2.數據清洗與處理(rubbish)

這一部分遇到了幾個小問題,包括pycharm控制窗的輸出結果不會顯示所有數據,以及數據的編碼問題,都在網上找到解決的方法,最后將清洗過的數據保存為xls文件

def rubbish():  # 對數據進行清洗和處理
    # pycharm控制窗的輸出結果不會顯示所有數據,所以在網上得到加入這三行代碼進行解決方便查看結果
    pd.set_option('display.width', 1000)  # 加了這一行那表格的一行就不會分段出現了
    # 顯示所有列
    pd.set_option('display.max_columns', None)
    # 顯示所有行
    pd.set_option('display.max_rows', None)
    # 對齊輸出結果
    pd.set_option('display.unicode.ambiguous_as_wide', True)
    pd.set_option('display.unicode.east_asian_width', True)

    # 使用‘utf-8’會報錯,使用其他解碼會亂碼,最終在網上得到了答案:‘在后面加入指定編譯器為python即可’
    # 將csv格式數據寫入到excel中
    df = pd.read_csv('top100.csv', engine='python', error_bad_lines=False)  # 當某行數據有問題時,不報錯,直接跳過,處理臟數據時使用
    # print(df)   #輸出csv表格中結果
    data = OrderedDict()  # 有序字典
    # print(df.columns)     #列名
    for line in list(df.columns):
        data[line] = list(df[line])  # 構建excel格式

    obj = pd.DataFrame(data)
    obj.to_excel('top100.xls', index=False)
    # 查看統計信息,設置參數buf來存儲字符串使數據不打印出來
    buf = io.StringIO()
    df.info(buf=buf)
    s = buf.getvalue()
    # print(s)
    print('保存xls成功')


rubbish()
rubbish

 

 

 

 

 

 3.文本分析生成詞雲:(message)

文本分析,包括使用jieba庫進行分詞和wouldcould生成詞雲,先用列的標題進行查詢,讀取標題所在的那一列保存在txt文檔中,然后使用jieba庫進行分詞,使用wouldcould制作詞雲保存成圖片

 

def message():  # 文本分析,包括使用jieba庫進行分詞和wouldcould生成詞雲
    # 用DictReader讀取csv的某一列,用列的標題查詢
    with open('top100.csv', 'rt') as csvfile:
        reader = csv.DictReader(csvfile)
        column = [row['標題'] for row in reader]
    # print(column)
    # 將標題列保存到txt文件中
    file = open('top100標題.txt', 'w')
    file.write(str(column))
    file.close()  # 關閉文件
    print('保存txt成功')

    # 使用jieba庫進行中文分詞
    final = ""
    # 文件夾位置
    filename = r"top100標題.txt"
    # 打開文件夾,讀取內容,並進行分詞
    with open(filename, 'r', encoding='gb18030') as f:
        for line in f.readlines():
            word = jieba.cut(line)
            for i in word:
                final = final + i + " "
    # print(final)
    print('jieba分詞成功')

    # 使用worldcould制作詞雲
    # 打開文本
    text = open('top100標題.txt').read()
    # 生成對象
    wc = WordCloud(font_path='C:\Windows\Fonts\simfang.ttf', width=800, height=600, mode='RGBA',
                   background_color=None).generate(text)
    # 顯示詞雲
    plt.imshow(wc, interpolation='bilinear')
    plt.axis('off')
    plt.show()
    # 保存到文件
    wc.to_file('標題詞雲.png')  # 生成圖像是透明的
    print('保存詞雲成功')


message()
message

 

jieba庫分詞結果:

 

 

保存結果:

 

 

 

 4.數據分析與可視化:(watch)

數據分析與可視化,包括繪制折線圖,柱形圖,直方圖,散點圖

def watch():  # 數據分析與可視化,包括繪制折線圖,柱形圖,直方圖,散點圖
    # 獲得繪圖數據
    point = pd.read_csv('top100.csv', engine='python')
    # print(data.isnull().sum)
    # 將字符串數據進行去除替換
    rank = point['排名']
    # print(rank)
    points = point['分數'].map(lambda x: int(x.replace('綜合得分', '')))
    # print(points)
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用來正常顯示中文標簽
    plt.rcParams['axes.unicode_minus'] = False  # 用來正常顯示負號
    # 根據數據繪制折線圖
    plt.plot(rank, points, c='red', alpha=0.5)
    # 繪圖表區域着色
    plt.fill_between(rank, points, facecolor='blue', alpha='0.2')
    # 設置圖形的格式
    plt.title('top100綜合熱度得分折線圖', fontsize=24)
    plt.xlabel('排名', fontsize=24)
    plt.ylabel('熱度得分', fontsize=12)
    # 參數刻度線樣式設置
    plt.tick_params(axis='both', which='major', labelsize=10)
    # 保存圖片
    plt.savefig(fname="top100綜合熱度得分折線圖.png", figsize=[10, 10])
    # 顯示折線圖
    plt.show()
    print('折線圖保存成功')
    # 根據數據繪制柱形圖
    # 創建基礎圖
    fig = plt.figure()
    # 在基礎圖上僅繪制一個圖,括號中的三個參數代表基礎圖中的統計圖布局,參數一次代表:圖的行數量、圖的列數量、第幾個圖。本例中,為1行1列,第一個圖
    bar1 = fig.add_subplot(1, 1, 1)
    # 繪制柱形圖,align表示條形與標簽中間對齊。
    bar1.bar(rank, points, align='center', color="blue")
    # 設置x、y軸標簽
    plt.xlabel("排名")
    plt.ylabel("熱度得分")
    # 設置統計圖標題
    plt.title("top100綜合熱度得分柱形圖")
    # 保存圖片
    plt.savefig(fname="top100綜合熱度得分柱形圖.png", figsize=[10, 10])
    # 顯示統計圖
    plt.show()
    print('柱形圖保存成功')
    # 繪制直方圖
    # 繪制基礎圖
    fig = plt.figure()
    hist1 = fig.add_subplot(1, 1, 1)
    # 繪制直方圖
    # bins=50 表示每個變量的 值應該被分成 50 份。normed=False 表示直方圖顯示的是頻率分布
    hist1.hist(points, bins=50, color="blue", density=False)
    # 確定坐標軸位置
    hist1.xaxis.set_ticks_position("bottom")
    hist1.yaxis.set_ticks_position("left")
    # 設置坐標軸標簽
    plt.xlabel("熱度得分")
    plt.ylabel("人數")
    # 設置標題
    plt.title("top100綜合熱度得分直方圖")
    # 保存圖片
    plt.savefig(fname="top100綜合熱度得分直方圖.png", figsize=[10, 10])
    # 顯示圖形
    plt.show()
    print('直方圖保存成功')
    # 繪制散點圖
    fig = plt.figure()
    scatter1 = fig.add_subplot(1, 1, 1)
    # 導入數據
    scatter1.scatter(rank, points)
    # 確定坐標軸位置
    scatter1.xaxis.set_ticks_position('bottom')
    scatter1.yaxis.set_ticks_position('left')
    # 設置坐標軸標簽
    plt.xlabel("排名")
    plt.ylabel("熱度得分")
    # 設置圖表標題
    plt.title("top100綜合熱度得分散點圖")
    # 保存圖片
    plt.savefig(fname="top100綜合熱度得分散點圖.png", figsize=[10, 10])
    # 顯示圖形
    plt.show()
    print('散點圖保存成功')


watch()
watch

 

 

 

結果:

 

 

 

 

完整代碼

 

 

import csv
import requests
from bs4 import BeautifulSoup
import pandas as pd
from collections import OrderedDict
import jieba
import io
from scipy.optimize import leastsq
from wordcloud import WordCloud
import matplotlib.pyplot as plt


# 注釋部分的print都是為了調試用的

def get_rank():  # 數據爬取與采集
    try:
        # 發起網絡請求
        url = 'https://www.bilibili.com/ranking/all/0/1/7'
        response = requests.get(url)
        html_text = response.text
        soup = BeautifulSoup(html_text, 'html.parser')

        # 用來保存視頻信息的對象
        class Video:
            def __init__(self, rank, title, point, visit, review, up, url):
                self.rank = rank
                self.title = title
                self.point = point
                self.visit = visit
                self.review = review
                self.up = up
                self.url = url

            def to_csv(self):
                return [self.rank, self.title, self.point, self.visit, self.review, self.up, self.url]

            # 使用靜態方法
            @staticmethod
            def csv_title():
                return ['排名', '標題', '分數', '播放量', '彈幕數', 'UP', 'URL']

        # 提取列表
        items = soup.find_all('li', {'class': 'rank-item'})
        videos = []  # 保存提取出來的video
        for itm in items:
            title = itm.find('a', {'class': 'title'}).text  # 標題
            point = itm.find('div', {'class': 'pts'}).text  # 綜合得分
            rank = itm.find('div', {'class': 'num'}).text  # 排名
            visit = itm.find('span', {'class': 'data-box'}).text  # 播放量
            review = itm.find_all('span', {'class': 'data-box'})[1].text  # 彈幕數
            up = itm.find_all('span', {'class': 'data-box'})[2].text  # up
            url = itm.find('a', {'class': 'title'}).get('href')  # 獲取鏈接
            v = Video(rank, title, point, visit, review, up, url)
            videos.append(v)
        # 保存
        file_name = f'top100.csv'
        with open(file_name, 'w', newline='') as f:
            pen = csv.writer(f)
            pen.writerow(Video.csv_title())
            # 導出數據到csv文件中
            for v in videos:
                pen.writerow(v.to_csv())
        print('保存csv成功')
    except:
        return "保存csv失敗"


def rubbish():  # 對數據進行清洗和處理
    try:
        # pycharm控制窗的輸出結果不會顯示所有數據,所以在網上得到加入這三行代碼進行解決方便查看結果
        # 加了這一行那表格的一行就不會分段出現了
        pd.set_option('display.width', 1000)
        # 顯示所有列
        pd.set_option('display.max_columns', None)
        # 顯示所有行
        pd.set_option('display.max_rows', None)
        # 對齊輸出結果
        pd.set_option('display.unicode.ambiguous_as_wide', True)
        pd.set_option('display.unicode.east_asian_width', True)

        # 使用‘utf-8’會報錯,使用其他解碼會亂碼,最終在網上得到了答案:‘在后面加入指定編譯器為python即可’
        # 將csv格式數據寫入到excel中
        df = pd.read_csv('top100.csv', engine='python', error_bad_lines=False)  # 當某行數據有問題時,不報錯,直接跳過,處理臟數據時使用
        # print(df)   #輸出csv表格中結果
        data = OrderedDict()  # 有序字典
        # print(df.columns)     #列名
        # 構建excel格式
        for line in list(df.columns):
            data[line] = list(df[line])
        obj = pd.DataFrame(data)
        obj.to_excel('top100.xls', index=False)
        # 查看統計信息,設置參數buf來存儲字符串使數據不打印出來
        buf = io.StringIO()
        df.info(buf=buf)
        s = buf.getvalue()
        print(s)
        print('保存xls成功')
    except:
        return "保存xls失敗"


rubbish()


def message():  # 文本分析,包括使用jieba庫進行分詞和wouldcould生成詞雲
    try:
        # 用DictReader讀取csv的某一列,用列的標題查詢
        with open('top100.csv', 'rt') as csvfile:
            reader = csv.DictReader(csvfile)
            column = [row['標題'] for row in reader]
        # print(column)
        # 將標題列保存到txt文件中
        file = open('top100標題.txt', 'w')
        file.write(str(column))
        # 關閉文件
        file.close()
        print('保存txt成功')
    except:
        return "保存txt失敗"

    try:
        # 使用jieba庫進行中文分詞
        final = ""
        # 文件夾位置
        filename = r"top100標題.txt"
        # 打開文件夾,讀取內容,並進行分詞
        with open(filename, 'r', encoding='gb18030') as f:
            for line in f.readlines():
                word = jieba.cut(line)
                for i in word:
                    final = final + i + " "
        # print(final)
        print('jieba分詞成功')
    except:
        return 'jieba分詞失敗'

    try:
        # 使用worldcould制作詞雲
        # 打開文本
        text = open('top100標題.txt').read()
        # 生成對象
        wc = WordCloud(font_path='C:\Windows\Fonts\simfang.ttf',
                       width=800,
                       height=600,
                       mode='RGBA',
                       background_color=None).generate(text)
        # 顯示詞雲
        plt.imshow(wc, interpolation='bilinear')
        plt.axis('off')
        plt.show()
        # 保存到文件
        wc.to_file('標題詞雲.png')  # 生成圖像是透明的
        print('保存詞雲成功')
    except:
        return '保存詞雲失敗'


message()


def watch():  # 數據分析與可視化,包括繪制折線圖,柱形圖,直方圖,散點圖
    try:
        # 獲得繪圖數據
        point = pd.read_csv('top100.csv', engine='python')
        # print(data.isnull().sum)
        # 將字符串數據進行去除替換
        rank = point['排名']
        # print(rank)
        points = point['分數'].map(lambda x: int(x.replace('綜合得分', '')))
        # print(points)
        # 用來正常顯示中文標簽
        plt.rcParams['font.sans-serif'] = ['SimHei']
        # 用來正常顯示負號
        plt.rcParams['axes.unicode_minus'] = False
        print('獲取繪圖數據成功')
    except:
        return '獲取數據失敗'

    try:
        # 根據數據繪制折線圖
        plt.plot(rank,
                 points,
                 c='red',
                 alpha=0.5)
        # 繪圖表區域着色
        plt.fill_between(rank,
                         points,
                         facecolor='blue',
                         alpha='0.2')
        # 設置圖形的格式
        plt.title('top100綜合熱度得分折線圖',
                  fontsize=24)
        plt.xlabel('排名',
                   fontsize=24)
        plt.ylabel('熱度得分',
                   fontsize=12)
        # 參數刻度線樣式設置
        plt.tick_params(axis='both',
                        which='major',
                        labelsize=10)
        # 保存圖片
        plt.savefig(fname="top100綜合熱度得分折線圖.png",
                    figsize=[10, 10])
        # 顯示折線圖
        plt.show()
        print('折線圖保存成功')
    except:
        return '折線圖保存失敗'

    try:
        # 根據數據繪制柱形圖
        # 創建基礎圖
        fig = plt.figure()
        # 在基礎圖上僅繪制一個圖,括號中的三個參數代表基礎圖中的統計圖布局,參數一次代表:圖的行數量、圖的列數量、第幾個圖。本例中,為1行1列,第一個圖
        bar1 = fig.add_subplot(1, 1, 1)
        # 繪制柱形圖,align表示條形與標簽中間對齊。
        bar1.bar(rank,
                 points,
                 align='center',
                 color="blue")
        # 設置x、y軸標簽
        plt.xlabel("排名")
        plt.ylabel("熱度得分")
        # 設置統計圖標題
        plt.title("top100綜合熱度得分柱形圖")
        # 保存圖片
        plt.savefig(fname="top100綜合熱度得分柱形圖.png",
                    figsize=[10, 10])
        # 顯示統計圖
        plt.show()
        print('柱形圖保存成功')
    except:
        return '柱形圖保存失敗'

    try:
        # 繪制直方圖
        # 繪制基礎圖
        fig = plt.figure()
        hist1 = fig.add_subplot(1, 1, 1)
        # 繪制直方圖
        # bins=50 表示每個變量的 值應該被分成 50 份。normed=False 表示直方圖顯示的是頻率分布
        hist1.hist(points,
                   bins=50,
                   color="blue",
                   density=False)
        # 確定坐標軸位置
        hist1.xaxis.set_ticks_position("bottom")
        hist1.yaxis.set_ticks_position("left")
        # 設置坐標軸標簽
        plt.xlabel("熱度得分")
        plt.ylabel("人數")
        # 設置標題
        plt.title("top100綜合熱度得分直方圖")
        # 保存圖片
        plt.savefig(fname="top100綜合熱度得分直方圖.png", figsize=[10, 10])
        # 顯示圖形
        plt.show()
        print('直方圖保存成功')
    except:
        return '直方圖保存失敗'

    try:
        # 繪制散點圖
        fig = plt.figure()
        scatter1 = fig.add_subplot(1, 1, 1)
        # 導入數據
        scatter1.scatter(rank, points)
        # 確定坐標軸位置
        scatter1.xaxis.set_ticks_position('bottom')
        scatter1.yaxis.set_ticks_position('left')
        # 設置坐標軸標簽
        plt.xlabel("排名")
        plt.ylabel("熱度得分")
        # 設置圖表標題
        plt.title("top100綜合熱度得分散點圖")
        # 保存圖片
        plt.savefig(fname="top100綜合熱度得分散點圖.png",
                    figsize=[10, 10])
        # 顯示圖形
        plt.show()
        print('散點圖保存成功')
    except:
        return '散點圖保存失敗'


watch()

  

運行結果

 

 

 

 

 

 

 

四、結論

通過爬蟲程序的制作,認識到利用爬蟲程序可以做到很多很酷的事情,能夠通過爬蟲爬取信息並運用其他庫處理信息對工作效率的提升是多么的大,提高了自己對python的興趣,堅定了認真學習的目標。與java相比python確實上手快 庫多 簡潔,java就一個httpclient就夠學好久了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM