天天看點

爬蟲綜合大作業

    這個作業的要求來自于:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3075 。

    B站有很多号稱“鎮站之寶”的視訊,擁有着數量極其恐怖的評論和彈幕。這次我的目的就是爬取B站視訊的評論資料,分析某番劇為何會深受大家喜愛。

    首先我通過B站大神的資料統計了解到,B站評論數量最多的番劇是《全職高手》。如下圖所示:

爬蟲綜合大作業
        通過資料可知《全職高手》這部番的第一集和最後一集分别占據了評論數量排行榜的第二名和第一名,遠超越其他很多很火的番。于是我先去B站https://www.bilibili.com/bangumi/play/ep107656 ,發現這部番需要開通大會員才能觀看。如下圖所示:
爬蟲綜合大作業
     因為我不是B站大會員,看不了視訊,我便百度百科了《全職高手》https://baike.so.com/doc/289439-27501474.html 。《全職高手》是一部遊戲競技類的番,故事講述網遊榮耀中被譽為教科書級别的頂尖高手葉修,因為種種原因遭到俱樂部的驅逐,離開職業圈的他寄身于一家網吧成了一個小小的網管。但是擁有十年遊戲經驗的他,在榮耀新開的第十區重新投入了遊戲,帶着對往昔的回憶和一把未完成的自制武器,開始了重返巅峰之路。下圖是這部番劇中的人物關系圖:
爬蟲綜合大作業
     雖然視訊看不了,評論和相關回複卻是可以看的。如下圖所示:
爬蟲綜合大作業
爬蟲綜合大作業

    63w6條!9k多頁的評論!由于資料量實在龐大,每條資料一一檢視顯然不現實,我就開始編寫爬蟲爬取所需資料。

    使用Python爬取網頁一般分為五個階段,接下來我将按步驟分析并爬取評論資料,分析番劇《全職高手》大火的原因。

1. 分析目标網頁

    首先我觀察評論區結構,發現評論區為滑鼠點選翻頁形式,共9399頁資料,每一頁有20條評論,每條評論包含使用者名、評論内容、評論樓層、時間日期、點贊數等使用者個人資訊。如下圖所示:

爬蟲綜合大作業

    接着我打開開發者工具,用滑鼠點選評論翻頁,觀察這個過程變化。

    我發現整個過程中URL不變,說明評論區翻頁不是通過URL控制,而在每翻一頁時,網頁會向伺服器發出Request URL的請求。如下圖所示:

爬蟲綜合大作業
    點選Preview欄,切換到預覽頁面,可以看到這個請求傳回的最終結果。下面是該請求傳回的json檔案,包含了本頁在replies裡的評論資料。這個json檔案裡包含了很多資訊,除了網頁上展示的資訊,還有很多未展示的資訊。如下圖所示:
爬蟲綜合大作業
爬蟲綜合大作業

2.擷取網頁内容

    現在正式寫代碼來爬取資料------通路目标url的網頁,擷取目标網頁的html内容并傳回。代碼如下:

# -*- coding: utf-8 -*-

"""

Spyder Editor

This is a temporary script file.

import requests

def fetchURL(url):

'''

功能:通路 url 的網頁,擷取網頁内容并傳回

參數:

url :目标網頁的 url

傳回:目标網頁的 html 内容

headers = {

'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',

'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',

}

try:

r = requests.get(url,headers=headers)

r.raise_for_status()

print(r.url)

return r.text

except requests.HTTPError as e:

print(e)

print("HTTPError")

except requests.RequestException as e:

except:

print("Unknown Error !")

if __name__ == '__main__':

url = 'https://api.bilibili.com/x/v2/reply?callback=jQuery172020326544171595695_1541502273311&jsonp=jsonp&pn=2&type=1&oid=11357166&sort=0&_=1541502312050'

html = fetchURL(url)

print(html)

    運作結果如下圖所示:

爬蟲綜合大作業

    運作後發現網頁出錯,伺服器拒絕了我們的通路。

    同理,這個請求放浏覽器位址欄裡面直接打開,網頁出錯,伺服器拒絕通路,什麼内容都看不到。如下圖所示:

爬蟲綜合大作業

    查閱許多相關資料https://baike.so.com/doc/5438832-5677151.html之後,我找到了解決方法。原請求的URL有callback 、jsonp、pn、type、oid和

_ = 共六個參數。其中,真正有用的參數隻有三個:pn(頁數)、type(=1)和oid(視訊id)。删除其餘不必要的參數後,用新整理出的URL通路伺服器,成功擷取到評論資料。如下圖所示:

爬蟲綜合大作業
     在主函數中,通過寫一個for循環來改變pn(頁數)的值,擷取每一頁的評論資料。

if __name__ == '__main__':
    for page in range(0,9400):
        url = 'https://api.bilibili.com/x/v2/reply?type=1&oid=11357166&pn=' + str(page)
        html = fetchURL(url)      

3.提取關鍵資訊

    通過json庫對擷取的響應内容進行解析,接着提取所需的資訊,即樓層、使用者名、性别、時間、評價、點贊數和回複數。代碼如下:

# -*- coding: utf-8 -*-
"""
Spyder Editor

This is a temporary script file.
"""

import requests
import json
import time
 
def fetchURL(url):
    '''
    功能:通路 url 的網頁,擷取網頁内容并傳回
    參數:
        url :目标網頁的 url
    傳回:目标網頁的 html 内容
    '''
    headers = {
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
    }
    
    try:
        r = requests.get(url,headers=headers)
        r.raise_for_status()
        print(r.url)
        return r.text
    except requests.HTTPError as e:
        print(e)
        print("HTTPError")
    except requests.RequestException as e:
        print(e)
    except:
        print("Unknown Error !")
        
def parserHtml(html):
    '''
    功能:根據參數 html 給定的記憶體型 HTML 檔案,嘗試解析其結構,擷取所需内容
    參數:
            html:類似檔案的記憶體 HTML 文本對象
    '''
    s = json.loads(html)
 
    for i in range(20):
        comment = s['data']['replies'][i]
 
        # 樓層,使用者名,性别,時間,評價,點贊數,回複數
        floor = comment['floor']
        username = comment['member']['uname']
        sex = comment['member']['sex']
        ctime = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(comment['ctime']))
        content = comment['content']['message']
        likes = comment['like']
        rcounts = comment['rcount']
 
        print('--'+str(floor) + ':' + username + '('+sex+')' + ':'+ctime)
        print(content)
        print('like : '+ str(likes) + '      ' + 'replies : ' + str(rcounts))
        print('  ')

url = 'https://api.bilibili.com/x/v2/reply?type=1&oid=11357166&pn=3'
html = fetchURL(url)
parserHtml(html)      

          運作結果如下圖所示:

爬蟲綜合大作業

4.輸出儲存

    爬取到想要的評論資料後,除了将其顯示輸出,還要将資料以csv格式儲存于本地并存儲到資料庫中。項目部分代碼如下:

import json

import time

import random

import pymysql

import pandas as pd

from sqlalchemy import create_engine

# headers是為了把爬蟲程式僞裝成浏覽器

# 請求标頭中的所有資訊都是headers内容,添加到requests請求中

r = requests.get(url,headers=headers) # 發送請求,擷取某個網頁

r.raise_for_status() # 如果發送了一個錯誤請求(用戶端錯誤或者伺服器錯誤響應),可以通過r.raise_for_status()來抛出異常

except requests.HTTPError as e: # 如果HTTP請求傳回了不成功的狀态碼,r.raise_for_status()會抛出一個 HTTPError異常

# 抛異常處理

def parserHtml(html):

功能:根據參數 html 給定的記憶體型 HTML 檔案,嘗試解析其結構,擷取所需内容

html:類似檔案的記憶體 HTML 文本對象

s = json.loads(html) # json.load()主要用來讀寫json檔案函數:json.loads函數的使用,将字元串轉化為字典

print('error')

commentlist = []

hlist = []

# 清單append()方法用于将傳入的對象附加(添加)到現有清單中

hlist.append("序号")

hlist.append("名字")

hlist.append("性别")

hlist.append("時間")

hlist.append("評論")

hlist.append("點贊數")

hlist.append("回複數")

#commentlist.append(hlist)

# 樓層,使用者名,性别,時間,評價,點贊數,回複數

for i in range(20): # 每頁隻有20條評論,循環20次

comment = s['data']['replies'][i]

blist = []

floor = comment['floor']

username = comment['member']['uname']

sex = comment['member']['sex']

ctime = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(comment['ctime']))

content = comment['content']['message']

likes = comment['like']

rcounts = comment['rcount']

blist.append(floor)

blist.append(username)

blist.append(sex)

blist.append(ctime)

blist.append(content)

blist.append(likes)

blist.append(rcounts)

commentlist.append(blist)

writePage(commentlist)

print('---'*20)

def writePage(urating):

Function : To write the content of html into a local file

html : The response content

filename : the local filename to be used stored the response

# Pandas讀取本地CSV檔案并設定Dataframe(資料格式),将爬取資料儲存到本地CSV檔案。加上mode='a' 追加寫入資料;sep=','表示資料間使用逗号作為分隔符;不儲存列名和行索引

dataframe = pd.DataFrame(urating)

dataframe.to_csv('B_comments.csv', mode='w', index=False, sep=',', header=False)

# 将爬取資料儲存到資料庫中

conInfo = "mysql+pymysql://root:@localhost:3306/comments?charset=utf8"

engine = create_engine(conInfo,encoding='utf-8')

dataframe.to_sql(name='comment',con=engine,if_exists='append',index=False,index_label='id')

pymysql.connect(host='localhost',port=3306,user='root',passwd='',db='comments',charset='utf8')

# 循環輸出9399頁,共188454條評論資料内容

for page in range(0,9400):

url = 'https://api.bilibili.com/x/v2/reply?type=1&oid=11357166&pn=' + str(page)

html = fetchURL(url)

parserHtml(html)

# 為了降低被封ip的風險,設定合理的爬取間隔,每爬20頁便随機歇1~5秒。

if page%20 == 0:

time.sleep(random.random()*5)

爬蟲綜合大作業

    資料儲存如下圖所示(相關檔案儲存在檔案夾):

爬蟲綜合大作業

 5.資料分析

    資料的可視化分析:(Pyecharts)

    注意:得到資料之後要先對資料進行清洗和預處理再進行資料的統計分析。代碼如下:

from pyecharts import Pie
attr = ['男','女','保密']
value= [31129,63123,93748]
pie = Pie('《全職高手》評論性别比例', title_pos='center', width=900)
pie.add("7-17", attr, value, center=[75, 50], is_random=True, radius=[30, 75], rosetype='area',   is_legend_show=False, is_label_show=True)
pie.render('./網友評論性别.html')      

    運作結果如下圖所示:

爬蟲綜合大作業

     可以看出,這類講網遊的動漫,不僅深受大衆喜愛,而且女生尤其喜歡,這部番劇觀看人數女生還遠超男生呢。

     詞雲分析:(jieba)

     代碼如下:

import jieba
 
# 導入matplotlib,用于生成2D圖形
 
import matplotlib.pyplot as plt
 
# 導入wordcount,用于制作詞雲圖
 
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
 
  
 
# 擷取所有評論
 
comments = []
 
with open('B_comments.csv', mode='r', encoding='utf-8') as f:
 
    rows = f.readlines()
 
    for row in rows:
 
        comment = row.split(',')[3]
 
        if comment != '':
 
            comments.append(comment)
 
  
 
# 設定分詞
 
comment_after_split = jieba.cut(str(comments), cut_all=False)  # 非全模式分詞,cut_all=false
 
words = " ".join(comment_after_split)  # 以空格進行拼接
 
# print(words)
 
  
 
# 設定屏蔽詞
 
stopwords = STOPWORDS.copy()
 
stopwords.add("電影")
 
stopwords.add("一部")
 
stopwords.add("一個")
 
stopwords.add("沒有")
 
stopwords.add("什麼")
 
stopwords.add("有點")
 
stopwords.add("這部")
 
stopwords.add("這個")
 
stopwords.add("不是")
 
stopwords.add("真的")
 
stopwords.add("感覺")
 
stopwords.add("覺得")
 
stopwords.add("還是")
 
stopwords.add("但是")
 
stopwords.add("就是")
 
  
 
# 導入背景圖
 
bg_image = plt.imread('bg.jpg')
 
  
 
# 設定詞雲參數,參數分别表示:畫布寬高、背景顔色、背景圖形狀、字型、屏蔽詞、最大詞的字型大小
 
wc = WordCloud(width=1024, height=768, background_color='white', mask=bg_image, font_path='STKAITI.TTF',
 
               stopwords=stopwords, max_font_size=400, random_state=50)
 
# 将分詞後資料傳入雲圖
 
wc.generate_from_text(words)
 
plt.imshow(wc)
 
plt.axis('off')  # 不顯示坐标軸
 
plt.show()
 
# 儲存結果到本地
 
wc.to_file('詞雲圖.jpg')      

     運作結果如下圖所示:

爬蟲綜合大作業

     可以看出,全職、榮耀、君莫笑、葉修、葉神等劇情裡面的重要事件和人物都是評論裡常提及的詞語,“啊啊啊”也出現非常多,是因為大部分人在上線觀劇後都會發出巨多的感歎詞表示激動之情。

爬蟲綜合大作業

     除此之外,分析使用者個人昵稱,還會發現這裡面有許多死忠粉,在昵稱裡面會采用和《全職高手》相關資訊。比如,“葉修、陳果、唐柔、蘇沐橙......”是劇中人物名字,“君莫笑、毀人不倦、寒煙柔......”是人物遊戲角色昵稱。而其餘角色如黃少天、藍河、魏琛等都是主角葉修的好友,也是網絡上主角的同性cp對象。此外,像“BOSS、團隊、治療、戰術、節奏、屬性、武器、神之領域......”則是遊戲本身或遊戲過程中的一些重要名詞。

總結:     

     爬取使用者評論資料後,分析可知《全職高手》這部番劇如此受青睐的原因有:首先,它是根據蝴蝶藍編寫的同名小說《全職高手》改編,吸引一大批書迷去觀看。其次,故事所揭露的正是當今社會的一些現象和人文理念,引發觀衆共鳴。再加上制作精良且有國内老牌動畫制作公司視美精典保駕護航,許多觀衆都給予很高的評價,堪稱“國漫神作”。

     在本次作業爬取B站最受喜愛番劇20w評論資料過程中,我遇到了不少困難。譬如:

   (1)請求的網頁url不能直接用,需要對參數進行篩選整理後才能通路網頁。

   (2)爬取過程并不順利,因為如果爬取期間有使用者發表評論,則請求傳回的響應會為空,導緻程式出錯。是以在實際爬取過程中,需記錄爬取的位置,以便出錯之後從該位置繼續爬取。此外,挑選發帖人數少的時間段爬取資料,可以極大程度降低程式出錯的機率。

   (3)爬取到的資料有多處不一緻,主要原因有------評論區樓層隻到了20w,但是評論數量卻有63w6條,數目不一緻是由于B站的評論是可以回複的,回複的評論也會計算到總評論數裡。我隻爬取了樓層的評論,而相應的回複内容則忽略,隻需統計回複數量。另外,評論區樓層有20w條,但是最後爬取的資料隻有18w條,這是因為有删評論的情況。評論删除後,後面的樓層并不會重新排序,而是把删掉的那層空下了,導緻樓層數和評論數不一緻。