1、查看要爬取頁面
打開B站網址,輸入“新型冠狀病毒肺炎”關鍵字,顯示界面如下:
2、確定爬蟲邏輯
查看網頁的內容後,一個網址頁面下20個視頻,這裏只採集20頁的視頻數據(共400個視頻),因爲是出現的視頻按照點擊量進行排序的,所以再往後的視頻爬取意義就不大了,因此基本爬蟲邏輯如下:
【分頁網址的url採集】——> 【單個視頻url的採集】——> 【進入視頻播放頁面獲取數據信息】——> 【打開存放彈幕的網頁】 ——> 【按照要求爬取數據並存放到數據庫】
函數式編程:
1)函數式編程
函數1:get_outer_urls(n) → 【分頁網址url採集】
n:爬取頁數
結果:得到分頁網址的list
函數2:get_inner_urls(ui,d_h,d_c) → 【視頻頁面url採集】
ui:分頁網址
d_h:user-agent信息
d_c:cookies信息
結果:得到存放單個視頻頁面的list
函數3:get_data(ui,d_h,d_c,table) → 【視頻頁面數據採集 / cid信息 / 彈幕xml數據採集】
ui:視頻頁面網址
d_h:user-agent信息
d_c:cookies信息
table:mongo集合對象
3、實戰操作
步驟一、前期準備並封裝第一個函數
1)導入相關的庫,和設置代碼分開標識
2)分析網頁的規律,查看網頁的2-4頁(一般選取2-4就可以看出規律),網址如下:
u2 = https://search.bilibili.com/all?keyword=%E6%96%B0%E5%9E%8B%E5%86%A0%E7%8A%B6%E7%97%85%E6%AF%92%E8%82%BA%E7%82%8E&order=click&duration=0&tids_1=0&page=2
u3 = https://search.bilibili.com/all?keyword=%E6%96%B0%E5%9E%8B%E5%86%A0%E7%8A%B6%E7%97%85%E6%AF%92%E8%82%BA%E7%82%8E&order=click&duration=0&tids_1=0&page=3
u4 = https://search.bilibili.com/all?keyword=%E6%96%B0%E5%9E%8B%E5%86%A0%E7%8A%B6%E7%97%85%E6%AF%92%E8%82%BA%E7%82%8E&order=click&duration=0&tids_1=0&page=4
......
通過2-4頁網址的梳理,可以看出分頁網址的規律,也就是最後面的“page=”之後數字發生變化,而且對應着具體的頁碼,那麼獲取前二十頁的分頁網址,就可以使用下面的方式:
urllist = []
for i in range(1,21):
ui = f"https://search.bilibili.com/all?keyword=%E6%96%B0%E5%9E%8B%E5%86%A0%E7%8A%B6%E7%97%85%E6%AF%92%E8%82%BA%E7%82%8E&order=click&duration=0&tids_1=0&page={i}"
urllist.append(ui)
print(urllist)
輸出結果爲:(這裏只截取前五頁的網址)
3) 封裝第一個函數
def get_outer_urls(n):
'''
【分頁網址url採集】
n:爬取頁數
結果:得到分頁網頁的list
'''
lst = []
for i in range(1,21):
ui = f"https://search.bilibili.com/all?keyword=%E6%96%B0%E5%9E%8B%E5%86%A0%E7%8A%B6%E7%97%85%E6%AF%92%E8%82%BA%E7%82%8E&order=click&duration=0&tids_1=0&page={i}"
lst.append(ui)
return(lst)
urllst = get_outer_urls(20)
print(urllst)
輸出結果和上面的一致
步驟二、設置請求頭headers和登錄信息cookies
這一步需要用戶登錄,沒有賬號的話,需要進行註冊,cookies和headers(每個人的cookies和headers應單獨設置)的獲取方式如下,
文字詳述: 以上面的登錄後的網頁界面爲例,鼠標右鍵檢查,然後選擇右邊標籤Network,然後刷新一下該網頁,這時候在右方Doc下面的Name菜單欄下會出現一個新的信息,選擇第一個文件,然後拉到底,就可以找到cookies和headers的信息。
步驟歸納:【登錄的頁面】–> 【右鍵檢查】–> 【Network】–> 【刷新】–> 【Doc下面的Name菜單欄】–> 【點擊第一個文件下拉到底】–> 【Request Headers下】
圖示
查找到headers和cookies之後,將其寫入到字典中儲存,如下(代碼在spyder裏運行)
dic_headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"}
cookies = "_uuid=3D3C1683-5F16-D3EC-36E2-5967E731F7DA81323infoc; buvid3=3EB2F2F9-8EE3-4AFE-B3D6-7620A3B2E636155823infoc; LIVE_BUVID=AUTO4015671567822432; sid=bj42fy4m; CURRENT_FNVAL=16; stardustvideo=1; rpdid=|(umYuYRkm~|0J'ulY~ul~JlY; UM_distinctid=16ce1f17c989db-0c9243ec350826-e343166-144000-16ce1f17c99a18; CURRENT_QUALITY=0; DedeUserID=38449436; DedeUserID__ckMd5=272068a4511232d7; SESSDATA=3a11597f%2C1583975698%2C7bfdfc21; bili_jct=d8d63e8aa5a2eb9adf9f30698873d271; INTVER=1; arrange=matrix"
dic_cookies = {}
for i in cookies.split("; "):
dic_cookies[i.split("=")[0]] = i.split("=")[1]
print(dic_headers)
print(dic_cookies)
輸出結果如下:
步驟三、網頁信息請求和網頁初解析
首先嚐試進行網頁信息請求(以搜索關鍵詞顯示的第一個網頁爲例),代碼如下
url = 'https://search.bilibili.com/all?keyword=%E6%96%B0%E5%9E%8B%E5%86%A0%E7%8A%B6%E7%97%85%E6%AF%92%E8%82%BA%E7%82%8E&order=click&duration=0&tids_1=0'
r = requests.get(url,headers = dic_headers, cookies = dic_cookies)
print(r)
輸出結果爲:<Response [200]> (說明網站信息可以正常訪問,接下來進行頁面的解析)
在網頁界面鼠標右鍵,選擇檢查,可以發現,搜到的視頻都是在【ul】標籤下的【li】標籤裏面,如下
那麼代碼實現視頻信息的獲取如下:
soup = BeautifulSoup(r.text, 'lxml')
lis = soup.find("ul",class_ = "video-list clearfix").find_all("li")
print(lis[0])
輸出的結果爲:(這裏以第一個視頻爲例,輸出結果)
接着就可以獲取視頻的url、標題、上傳時間和作者信息等數據,但是由於彈幕信息必須要進入視頻的播放界面纔可以獲取到,因此在這一步只需要獲取視頻url即可,其餘的信息在視頻播放界面的網頁上獲取,通過上面的輸出可以發現,視頻網址放在裏【a】標籤下
代碼實現視頻url的獲取如下:
inner_url = lis[0].a['href']
print(inner_url)
輸出的結果爲:(發現最前面少了https:,在封裝函數的時候需要加上)
‘//www.bilibili.com/video/av84850049?from=search’
步驟四、封裝第二個函數
上面實現了單個視頻url的獲取,接下來只需要進行遍歷循環並把結果儲存到列表即可,也就是封裝第二個函數的要求,代碼如下
def get_inter_urls(ui,d_h,d_c):
'''
【視頻頁面url採集】
ui:視頻信息網頁url
d_h:user-agent信息
d_c:cookies信息
結果:得到一個視頻頁面的list
'''
ri = requests.get(ui, headers = d_h, cookies = d_c)
soupi = BeautifulSoup(ri.text, 'lxml')
lis = soupi.find('ul',class_="video-list clearfix").find_all('li')
lst = []
for li in lis:
lst.append('https:' + li.a['href'])
return lst
url_1 = urllst[0]
print(get_inter_urls(url_1,dic_headers,dic_cookies))
輸出結果爲:(以第一頁爲例,可以輸出該頁的20個視頻的url)
步驟五、網頁深度解析(獲取標題、時間和彈幕等)並將數據存入數據庫
1) 獲取標題
和之前的網頁初解析一樣,找到標題所對應的的標籤信息,如下
代碼實現如下:
url_1 = urllst[0]
inner_lst = get_inter_urls(url_1,dic_headers,dic_cookies)
r = requests.get(inner_lst[0],headers = dic_headers, cookies = dic_cookies)
soup = BeautifulSoup(r.text, 'lxml')
title = soup.h1['title']
print(title)
輸出結果如下:
2) 獲取上傳時間
上傳時間所對應的標籤信息如下,信息在【div class=‘video-data’】標籤下的【span】裏面
代碼實現如下:
upload_time = soup.find("span", class_ = "a-crumbs").next_sibling.text
print(upload_time)
輸出的結果爲:
3) 獲取作者
作者對應的標籤信息如下,名稱在【div class=‘name’】下的第一個【a】標籤下
代碼實現如下:
author = soup.find("div", class_ = 'name').a.text
print(author)
輸出的結果爲:
4) 獲取彈幕數據
① 解析一下cid
首先要從 B 站彈幕的說起,B 站視頻的 ID 名字是 cid,一個 AV (視頻)號下如果有多個分 P,就會佔用多個 cid,cid 可以看做是視頻的唯一 ID,通過這個 ID ,我們可以讀取到 B 站的彈幕格式爲 comment.bilibili.com/[cid].xml,在一開始給出的彈幕網址就爲:https://comment.bilibili.com/84682646.xml
比如在第一個視頻的網頁界面點擊鼠標右鍵查看源代碼,然後搜索“cid”,就會發現相關的信息
複製"cid":後的數字查看一下這個信息在“檢查”界面對應的標籤的信息是怎麼樣的,搜索如下,可以看出都是在【script】標籤下面
② cid 數據採集
對比“源代碼"和"檢查"頁面,發現可以很好的獲取cid的方式是通過在源代碼窗口下查找,因爲這裏面的數據直接以字典的形式存儲的,而如果再"檢查"界面進行匹配標籤,在來查找內容就顯着很複雜,代碼如下
cid = re.search(r'"cid":(\d*),', ri.text).group(1)
print(cid)
輸出的結果爲:
③ 彈幕信息採集
這裏只需要將對應位置的數字換成cid信息即可,然後嘗試獲取該cid對應的url下面的內容(注意亂碼的解決方式)
cid = re.search(r'"cid":(\d*),', r.text).group(1)
cid_url = f"https://comment.bilibili.com/{cid}.xml"
r2 = requests.get(cid_url)
r2.encoding = r2.apparent_encoding
soup2 = BeautifulSoup(r2.text, 'lxml')
print(soup2)
輸出結果爲:(截取部分片段)
檢驗能否獲取該頁面正常數據
dmlst = re.findall(r'<d.*?/d>',r2.text)
print(dmlst)
輸出的結果爲:(截取部分片段)
彈幕的標籤信息全部存儲到了dmlst裏面了,接下來是提取裏面的內容,順便把之前的內容也寫入到字典裏面
for dm in dmlst:
dic = {}
dic['標題'] = title
dic['上傳時間'] = upload_time
dic['cid'] = cid
dic['作者'] = author
dic['彈幕內容'] = re.search(r'>(.*)<',dm).group(1)
dic['其他信息'] = re.search(r'p="(.*)">',dm).group(1)
print(dic)
輸出結果爲:(截取部分)
至此,就把相應的數據全部儲存在字典裏面了,接下來就是配置數據庫和封裝第三個函數了
步驟六、配置數據庫和封裝第三個函數
1) 配置數據庫
import pymongo
myclient = myclient = pymongo.MongoClient("mongodb://localhost:27017/")
db = myclient['冠狀病毒彈幕數據']
datatable = db['data']
上述代碼實現數據庫的創建及命名,以及存放數據表格的創建
2)封裝第三個函數,並將數據寫入到數據庫
只需要將每次生成的dic直接插入到創建的數據表格中即可,爲了可視化儲存過程,可以進行計數統計
def get_data(ui,d_h,d_c,table):
'''
ui:視頻頁面網址
d_h:user-agent信息
d_c:cookies信息
table:mongo集合對象
'''
ri = requests.get(url = ui, headers = d_h, cookies = d_c)
soupi = BeautifulSoup(ri.text, 'lxml')
#title = soupi.find(id = "viewbox_report").span.text
title = soupi.h1['title']
upload_time = soupi.find("span", class_ = "a-crumbs").next_sibling.text
#upload_time = re.search(r'(20.*\d)',soupi.find("div",class_ ="video-data").text)
cid = re.search(r'"cid":(\d*),', ri.text).group(1)
cid_url = f"https://comment.bilibili.com/{cid}.xml"
r2 = requests.get(cid_url)
r2.encoding = r2.apparent_encoding
dmlst = re.findall(r'<d.*?/d>',r2.text)
n = 0
for dm in dmlst:
dic = {}
dic['標題'] = title
dic['上傳時間'] = upload_time
dic['cid'] = cid
dic['作者'] = author
dic['彈幕內容'] = re.search(r'>(.*)<',dm).group(1)
dic['其他信息'] = re.search(r'p="(.*)">',dm).group(1)
table.insert_one(dic)
n += 1
return n
最後運行代碼:
inner_urllst = []
error_lst = []
for outer_url in urllst:
try:
inner_urllst.extend(get_inter_urls(outer_url,dic_headers,dic_cookies))
print("已成功獲取{}條視頻數據".format(len(inner_urllst)))
except:
error_lst.append(outer_url)
print("獲取視頻信息失敗,網址爲:",outer_url)
count = 0
for u in inner_urllst:
try:
count += get_data(u,dic_headers,dic_cookies,datatable)
print('數據採集並存入成功,總共採集{}條數據'.format(count))
except:
error_lst.append(u)
print('數據採集失敗,數據網址爲:',u)
輸出的結果爲:
數據庫裏面的數據
數據分析
首先將保存在MongoDB數據庫的數據導出,如下
接着就可以就可以讀取桌面的csv文件了,代碼如下:
import pandas as pd
df = pd.read_csv(r"C:\Users\86177\Desktop\result.csv")
data = df['彈幕內容']
data.value_counts()[:20]
輸出的結果爲:(按照字頻選擇前二十進行輸出)
最後將結果進行詞雲展示;
import pandas as pd
from wordcloud import WordCloud
df = pd.read_csv(r"C:\Users\86177\Desktop\result.csv")
data = df['彈幕內容']
txt = " ".join(data.tolist())
txt = txt.replace("哈","")
wc = WordCloud(width=600, height=400).generate(txt)
wc.to_file('wordcloud.png')
輸出的結果爲: