最近想爬下B站的彈幕和評論,發現網上找到的教程基本都失效了,畢竟爬蟲和反爬是屬于魔高一尺、道高一丈的雙方,程序員小哥哥們在網絡的兩端斗智斗勇,也是精彩紛呈。
當然了,對于爬蟲這一方,爬取網站數據,一般目的都是比較明確的,比如我這里就是為了冰冰,廢話不多說,開干!
獲取彈幕數據
這里先聲明一點,雖然網絡上的整體教程都失效了,但是有一些步驟還是可以參考的,比如我們可以知道,對于彈幕數據,我們是可以通過如下的一個接口來獲取的。
??https://comment.bilibili.com/xxxx.xml??
在瀏覽器打開可以看到如下:

數據還是非常干凈的,那么下一步就是看如何獲取這個 xml 的 url 地址了,也就是如何獲取 324768988 ID;
接下來我們搜索整個網頁的源碼,可以發現如下情況;

也就是說,我們需要的 ID 是可以在 script 當中獲取的,下面就來編寫一個提取 script 內容的函數。
def getHTML_content(self):
# 獲取該視頻網頁的內容
response = requests.get(self.BVurl, headers = self.headers)
html_str = response.content.decode()
html=etree.HTML(html_str)
result=etree.tostring(html)
return result
def get_script_list(self,str):
html = etree.HTML(str)
script_list = html.xpath("http://script/text()")
return script_list
拿到所有的 script 內容之后,我們再來解析我們需要的數據。
script_list = self.get_script_list(html_content)
# 解析script數據,獲取cid信息
for script in script_list:
if '[{"cid":' in script:
find_script_text = script
final_text = find_script_text.split('[{"cid":')[1].split(',"page":')[0]
最后,我們再把整體代碼封裝成一個類,就完成了彈幕抓取的數據收集工作了。
spider = BiliSpider("BV16p4y187hc")
spider.run()
結果如下:

獲取評論數據
對于評論數據,可能要復雜一些,需要分為主(main)評論和回復主評論的 reply 評論。
我們通過瀏覽器工具抓取網頁上的所有請求,然后搜索 reply,可以得到如下結果:

我們先來看看 main 請求,整理后通過瀏覽器訪問如下:

也可以直接通過 requests 請求;

通過觀察可以得知,響應消息里的 replies 就是主評論內容,同時我們還可以改變 url 當中的 next 參數來翻頁,進而請求不同的數據。
這里我們再關注下 rpid 參數,這個會用于 reply 評論中。
再來看看 reply 評論,同樣可以使用 requests 直接訪問,同時 url 當中的 root 參數就是我們上面提到的 rpid 參數。

我們厘清了上面的關系之后,我們就可以編寫代碼了;
def get_data(data):
data_list = []
comment_data_list = data["data"]["replies"]
for i in comment_data_list:
data_list.append([i['rpid'], i['like'], i['member']['uname'], i['member']['level_info']['current_level'], i['content']['message']])
return data_list
def save_data(data_type, data):
if not os.path.exists(data_type + r'_data.csv'):
with open(data_type + r"_data.csv", "a+", encoding='utf-8') as f:
f.write("rpid,點贊數量,用戶,等級,評論內容\n")
for i in data:
rpid = i[0]
like_count = i[1]
user = i[2].replace(',', ',')
level = i[3]
content = i[4].replace(',', ',')
row = '{},{},{},{},{}'.format(rpid,like_count,user,level,content)
f.write(row)
f.write('\n')
else:
with open(data_type + r"_data.csv", "a+", encoding='utf-8') as f:
for i in data:
rpid = i[0]
like_count = i[1]
user = i[2].replace(',', ',')
level = i[3]
content = i[4].replace(',', ',')
row = '{},{},{},{},{}'.format(rpid,like_count,user,level,content)
f.write(row)
f.write('\n')
for i in range(1000):
url = "https://api.bilibili.com/x/v2/reply/main?jsnotallow=jsonp&next={}&type=1&oid=972516426&mode=3&plat=1&_=1632192192097".format(str(i))
print(url)
d = requests.get(url)
data = d.json()
if not data['data']['replies']:
break
m_data = get_data(data)
save_data("main", m_data)
for j in m_data:
reply_url = "https://api.bilibili.com/x/v2/reply/reply?jsnotallow=jsonp&pn=1&type=1&oid=972516426&ps=10&root={}&_=1632192668665".format(str(j[0]))
print(reply_url)
r = requests.get(reply_url)
r_data = r.json()
if not r_data['data']['replies']:
break
reply_data = get_data(r_data)
save_data("reply", reply_data)
time.sleep(5)
time.sleep(5)
爬取過程中:

這樣,針對一個冰冰視頻,我們就完成了上千評論的抓取;
可視化
下面我們簡單做一些可視化動作;
先來看下我們爬取的數據整體的樣子:

因為數據中有一些空值,我們來處理下:
df_new = df.dropna(axis=0,subset = ["用戶"])
下面就可以作圖了,GO!
使用 pyecharts 還是我們的首選,畢竟編寫容易
評論熱度
df1 = df.sort_values(by="點贊數量",ascending=False).head(20)
c1 = (
Bar()
.add_xaxis(df1["評論內容"].to_list())
.add_yaxis("點贊數量", df1["點贊數量"].to_list(), color=Faker.rand_color())
.set_global_opts(
title_opts=opts.TitleOpts(title="評論熱度Top20"),
datazoom_opts=[opts.DataZoomOpts(), opts.DataZoomOpts(type_="inside")],
)
.render_notebook()
)

等級分布
pie_data = df_new.等級.value_counts().sort_index(ascending=False)
pie_data.tolist()
c2 = (
Pie()
.add(
"",
[list(z) for z in zip([str(i) for i in range(6, 1, -1)], pie_data.tolist())],
radius=["40%", "75%"],
)
.set_global_opts(
title_opts=opts.TitleOpts(title="等級分布"),
legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
)
.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
.render_notebook()
)

評論詞云
def wordcloud(data, name, pic=None):
comment = jieba.cut(str(data), cut_all=False)
words = ' '.join(comment)
img = Image.open(pic)
img_array = np.array(img)
wc = WordCloud(width=2000, height=1800, background_color='white', font_path=font, mask=img_array,
stopwords=STOPWORDS, contour_width=3, contour_color='steelblue')
wc.generate(words)
wc.to_file(name + '.png')
wordcloud(df_new["評論內容"], "冰冰", '1.PNG')

好了,今天的分享就到這里,喜歡冰冰就點個在看吧!