python3——爬取B站弹幕

在简书发现一篇有趣的文章:爬虫,走起,用Excel实现5min抓取B站弹幕及初步处理
讲到了如何根据开发者工具,获得B站视频的弹幕信息,不过有个不足就是手动保存弹幕信息。而通过python我们可以轻松地自动存储。

以《周刊哔哩哔哩排行榜#359》http://www.bilibili.com/video/av10394940/ 为例:
根据文中的方法,我们得到弹幕网址: http://comment.bilibili.com/17168035.xml

很容易就猜想到弹幕的网址格式就是:
http://comment.bilibili.com/数字.xml ,我们可以尝试着修改一下数字,发现确实如此。
那么如何确定数字“17168035”,就是我们自动获取弹幕列表的关键。
我们在视频网页的html搜索“17168035”


天啊,竟然有。那问题就太简单了。用正则表达式一下子就拿出来了:

danmu_id = re.findall(r'cid=(\d+)&', html)[0]

直接看下代码吧,没什么难的。

import requests, re
from bs4 import BeautifulSoup as BS
#打开网页函数
def open_url(url):
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.103 Safari/537.36'}
    response = requests.get(url=url, headers=headers)
    response.encoding = 'utf-8'
    html = response.text
    return html
#获取弹幕url中的数字id号
def get_danmu_id(html):
    #用try防止有些av号没视频
    try:
        soup = BS(html, 'lxml')
        #视频名
        title = soup.select('div.v-title > h1')[0].get_text()
        #投稿人
        author = soup.select('meta[name="author"]')[0]['content']
        #弹幕的网站代码
        danmu_id = re.findall(r'cid=(\d+)&', html)[0]
        print(title, author)
        return danmu_id
    except:
        print('视频不见了哟')
        return False

video_url ='http://www.bilibili.com/video/av10394940/'
video_html = open_url(video_url)
danmu_id = get_danmu_id(video_html)
if danmu_id:
    danmu_url = 'http://comment.bilibili.com/{}.xml'.format(danmu_id)
    danmu_html = open_url(url=danmu_url)
    soup = BS(danmu_html, 'lxml')
    all_d = soup.select('d')
    for d in all_d:
        #把d标签中P的各个属性分离开
        danmu_list = d['p'].split(',')
        #d.get_text()是弹幕内容
        danmu_list.append(d.get_text())
        print(danmu_list)

显示结果如下,可以再把这些个列表保存起来。


注意:
后来发现有一些B站网址还是不行, 比如埃罗芒阿老师这种网址,不是传统的av号形式(http://bangumi.bilibili.com/anime/5997/play#103921 )。
静态页面是实现不了,不过在开发者工具中还是能找到弹幕对应的数字号的,因此可以考虑之前说过的selenium+Chrome或PhantomJS的组合。

很好奇P中的各个数值都代表了什么,查了下。

引自:https://zhidao.baidu.com/question/1430448163912263499.html
<d p="0,1,25,16777215,1312863760,0,eff85771,42759017">前排占位置</d>
p这个字段里面的内容:
0,1,25,16777215,1312863760,0,eff85771,42759017
中几个逗号分割的数据
第一个参数是弹幕出现的时间 以秒数为单位。
第二个参数是弹幕的模式1..3 滚动弹幕 4底端弹幕 5顶端弹幕 6.逆向弹幕 7精准定位 8高级弹幕
第三个参数是字号, 12非常小,16特小,18小,25中,36大,45很大,64特别大
第四个参数是字体的颜色 以HTML颜色的十进制为准
第五个参数是Unix格式的时间戳。基准时间为 1970-1-1 08:00:00
第六个参数是弹幕池 0普通池 1字幕池 2特殊池 【目前特殊池为高级弹幕专用】
第七个参数是发送者的ID,用于“屏蔽此弹幕的发送者”功能
第八个参数是弹幕在弹幕数据库中rowID 用于“历史弹幕”功能。

第一个参数,可以按照如下转换成时间(没找到对应的函数):

seconds = eval('1306.3900146484')
#商,余数 = divmod(被除数, 除数)
m, s = divmod(seconds, 60)
h, m = divmod(m, 60)
print ("%02d:%02d:%02d" % (h, m, s))

运行结果
00:21:46

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
其中第四个参数,我们怎么知道具体的颜色是什么呢
比如输出反复出现的16777215是什么颜色。我们可以进行以下的转换:
十进制->十六进制->RGB颜色表示
十进制数16777215换成十六进制数FFFFFF,也就是255,255,255。白色!
知道方法后,我们可以在在线调色板(点我进入)就能知道任意数字的颜色了,比如那个41194(十六进制00A0EA)。


原来是蓝色呀。
-----------------------------------------------------------------------------------

第五个参数,我们可以把它转换下:

import time
print(time.ctime(1494238353))

输出结果:
Mon May  8 18:12:33 2017

后续更新了下程序,修正了某些网址不能用静态获取弹幕数字号的问题。

import requests, re, time, csv
from bs4 import BeautifulSoup as BS
from selenium import webdriver
#打开网页函数
def open_url(url):
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.103 Safari/537.36'}
    response = requests.get(url=url, headers=headers)
    response.encoding = 'utf-8'
    html = response.text
    return html
#获取弹幕url中的数字id号

#当requests行不通时,采用selenium的方法。
def sele_get(url):
    SERVICE_ARGS = ['--load-images=false', '--disk-cache=true']
    driver = webdriver.PhantomJS(service_args = SERVICE_ARGS)
    driver.get(url)
    time.sleep(2)
    danmu_id = re.findall(r'cid=(\d+)&', driver.page_source)[0]

    return danmu_id


def get_danmu_id(html, url):
    try:
        soup = BS(html, 'lxml')
        #视频名
        title = soup.select('title[data-vue-meta="true"]')[0].get_text()
        #投稿人
        author = soup.select('meta[name="author"]')[0]['content']
        #弹幕的网站代码
        try:

            danmu_id = re.findall(r'cid=(\d+)&', html)[0]
            #danmu_id = re.findall(r'/(\d+)-1-64', html)[0]
            #print(danmu_id)
        except:
            danmu_id = sele_get(url)
        print(title, author)
        return danmu_id
    except:
        print('视频不见了哟')
        return False
#秒转换成时间
def sec2str(seconds):
    seconds = eval(seconds)
    m, s = divmod(seconds, 60)
    h, m = divmod(m, 60)
    time = "%02d:%02d:%02d" % (h, m, s)
    return time

#csv保存函数
def csv_write(tablelist):
    tableheader = ['出现时间', '弹幕模式', '字号', '颜色', '发送时间' ,'弹幕池', '发送者id', 'rowID', '弹幕内容']
    with open('danmu.csv', 'w', newline='', errors='ignore') as f:
        writer = csv.writer(f)
        writer.writerow(tableheader)
        for row in tablelist:
            writer.writerow(row)


video_url ='http://www.bilibili.com/video/av5038338/'
video_html = open_url(video_url)
danmu_id = get_danmu_id(video_html, video_url)
all_list = []
if danmu_id:
    danmu_url = 'http://comment.bilibili.com/{}.xml'.format(danmu_id)
    danmu_html = open_url(url=danmu_url)
    soup = BS(danmu_html, 'lxml')
    all_d = soup.select('d')
    for d in all_d:
        #把d标签中P的各个属性分离开
        danmu_list = d['p'].split(',')
        #d.get_text()是弹幕内容
        danmu_list.append(d.get_text())
        danmu_list[0] = sec2str(danmu_list[0])
        danmu_list[4] = time.ctime(eval(danmu_list[4]))
        all_list.append(danmu_list)
        print(danmu_list)
    all_list.sort()
    csv_write(all_list)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,050评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 在凤鸣山的北边有一个小村落,也没有啥正经的名字,只因村里的人大多姓吴,就叫吴家村。吴家村是个很贫穷的小村落,村里统...
    兮云阅读 778评论 6 9
  • 松间明月照不透 清泉石上常断流 流觞曲水不见酒 黄鹤楼前猛抬头 长天秋水隔两边 落霞孤鹜没出现 黄昏约人空等候 月...
    雕兄_KYP阅读 385评论 0 0