爬取b站彈幕並不困難。要得到up主所有視頻彈幕,我們首先進入up主視頻頁面,即https://space.bilibili.com/id號/video這個頁面。按F12打開開發者菜單,刷新一下,在network的xhr文件中有一個getSubmitVideo文件,這個文件里就有我們需要的視頻av號了 ...
爬取b站彈幕並不困難。要得到up主所有視頻彈幕,我們首先進入up主視頻頁面,即https://space.bilibili.com/id號/video這個頁面。按F12打開開發者菜單,刷新一下,在network的xhr文件中有一個getSubmitVideo文件,這個文件里就有我們需要的視頻av號了。如果直接抓取頁面是拿不到的,因為視頻是非同步載入的。
在這個文件里的data標簽下,有一個count是視頻總數,pages是第幾頁,vlist就是我們要找的視頻信息了,裡面的aid就是每個視頻的av號。它的請求鏈接是https://space.bilibili.com/ajax/member/getSubmitVideos?mid=av號&pagesize=30&tid=0&page=1&keyword=&order=pubdate。pagesize是每次傳多少個視頻信息。
拿到所有的視頻av號後,我們打開視頻頁面。同樣是按F12打開開發者菜單,刷新一下,在network的xhr中有兩個文件,一個以pagelist為開頭,另一個以list.so為開頭。這兩個文件,第一個裡包含了視頻的cid,第二個就是根據cid拿到的彈幕文件。同樣,我們根據視頻av號訪問第一個文件的請求url,得到cid,再根據cid訪問第二個請求url就可以了。
最後,我們對拿到的彈幕文件進行適當的整理。主要是從文件中的<d>標簽中提取出彈幕文字,然後去重,計數,再儲存到文件里。
import requests
from lxml import etree import os import json from bs4 import BeautifulSoup from requests import exceptions import re import time def download_page(url): headers = {
'User-Agent':"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"
} data = requests.get(url, headers=headers) return data def get_video_page(space_num): base_url = "https://www.bilibili.com/av" url = "https://space.bilibili.com/ajax/member/getSubmitVideos?mid={}&pagesize=99&tid=0&page=1&keyword=&order=pubdate".format(space_num) data = json.loads(download_page(url).content)['data'] total = data['count'] page_num = int(total/99) + 1 video_list = data['vlist'] video_url = [] for video in video_list: video_url.append(base_url + str(video['aid'])) for i in range(2, page_num+1): time.sleep(1) url = "https://space.bilibili.com/ajax/member/getSubmitVideos?mid={}&pagesize=99&tid=0&page={}&keyword=&order=pubdate".format(space_num, i) data = json.loads(download_page(url).content)['data'] video_list = data['vlist'] for video in video_list: video_url.append(base_url + str(video['aid'])) return video_url def get_barrage(name, space_num): video_list = get_video_page(space_num) aid_to_oid = 'https://api.bilibili.com/x/player/pagelist?aid={}&jsonp=jsonp' barrage_url = 'https://api.bilibili.com/x/v1/dm/list.so?oid={}' for url in video_list:
# 降低爬取速度防止被禁 time.sleep(1) aid = re.search(r'\d+$',url).group()
# 這裡有時會出現莫名其妙的錯誤 try: oid = json.loads(download_page(aid_to_oid.format(aid)).content)['data'][0]['cid'] barrage = download_page(barrage_url.format(oid)).content except requests.exceptions.ConnectionError: print('av:',aid) continue if not os.path.exists('barrage/{}'.format(name)): os.makedirs('barrage/{}'.format(name)) with open('barrage/{}/av{}.xml'.format(name,aid),'wb') as f: f.write(barrage) def reorganize_barrage(name): results = {} for filename in os.listdir('barrage/{}'.format(name)): html = etree.parse('barrage/{}/{}'.format(name,filename), etree.HTMLParser())
# 提取出xml文件中<d>標簽中的文字 barrages = html.xpath('//d//text()') for barrage in barrages:
# 有些彈幕會有回車符 barrage = barrage.replace('\r', '') if barrage in results: results[barrage] += 1 else: results[barrage] = 1 if not os.path.exists('statistical result'): os.makedirs('statistical result') with open('statistical result/{}.txt'.format(name), 'w', encoding='utf8') as f: for key,value in results.items(): f.write('{}\t:\t{}\n'.format(key.rstrip('\r'),value)) if __name__ == '__main__':
# 在space list.txt文件里,我是用“up主名稱:id”的格式來儲存的, with open('space list.txt', 'r') as f: for line in f.readlines(): name, num = line.split(':') print(name) get_barrage(name, space_number) reorganize_barrage(name)