0x01 工具
python3, chrome
0x02 查找接口
这里一共找到3个接口,第一个接口:
https://api.bilibili.com/x/v1/dm/list.so?oid=xxxxxx
其返回的内容是:
该接口目前只能请求到1000条数据,可能有别的参数可以控制翻页,但目前没有找到。
第二个接口:
# 通过oid和月份获取该月哪些天有弹幕数据
https://api.bilibili.com/x/v2/dm/history/index?type=1&oid=xxxxxx&month=2020-05
返回的内容:
通过上面返回的日期构造url(需要携带登录后的cookie):
# 通过oid和日期获取当天的弹幕数据
https://api.bilibili.com/x/v2/dm/history?type=1&oid=xxxxxxx&date=2020-07-11
返回的内容:
这里重点分析第三个接口:
https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid=xxxxx&pid=xxxx&segment_index=1
老规矩,F12打开,查看该接口返回的内容,发现:
全部乱码,但可以看到其实格式是和第一个接口请求返回的应该是一样的。
0x03 响应内容
直接使用python请求看看:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import requests
def main():
url = 'https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid=xxx&pid=xxx&segment_index=1'
headers = {
'accept': '*/*',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'origin': 'https://www.bilibili.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'
}
html = requests.request(url=url, method='get', headers=headers)
print(html.text)
if __name__ == '__main__':
main()
返回结果:
实际上是可以看到弹幕内容的,但是还有部分内容无法看到。若想要看到完整的内容怎么办,通过JS调试查看是怎么编码的,继续往下。
先将返回的内容转为十六进制,由于html.text的数据类型本身就是bytes类型,所以直接转:
def bytesToHexString(bs):
return ['%02X ' % b for b in bs]
转换后得到十六进制的列表:
0x04 分析接口使用到的JS
打开chrome,F12,刷新页面,找到链接,随便下几个断点:
然后开始枯燥的单步调试。。。
发现请求位置:
继续。。。
这里我们看看返回的内容:
好像有一丢丢眼熟,我们将刚才请求到的十六进制转为十进制瞅瞅。
稍微修改一下刚才转换的代码:
def bytesToHexString(bs):
return [int('%02X ' % b, 16) for b in bs]
转换得到:
和上面的数据一模一样,只是上边的是有符号整数,说明思路没错,数据需要这样转换来编码。
此处发现疑似转换编码的地方:
继续进去来到:
柳暗花明,这个位置就是编码的地方。
接下来就是将这部分用到的JS代码全部扣下来,整理完成后先在浏览器试试:
0x05 python调用JS
由于对编码加密算法不太熟悉,不清楚python是否有现成的编码方式直接调用,所以通过python调用JS的方式编码解析。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import requests
import execjs
def main():
url = 'https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid=xxxx&pid=xxxxx&segment_index=1'
headers = {
'accept': '*/*',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'origin': 'https://www.bilibili.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'
}
html = requests.request(url=url, method='get', headers=headers)
tt = bytesToHexString(html.content)
with open(r'D:\Desktop\bili.js', 'r') as f:
js_data = f.read()
ctx = execjs.compile(js_data)
ret = ctx.call('run', tt)
print(ret)
def bytesToHexString(bs):
return [int('%02X ' % b, 16) for b in bs]
if __name__ == '__main__':
main()
python3运行的时候报错:
Exception in thread Thread-1:
Traceback (most recent call last):
File "E:\Python36\lib\threading.py", line 916, in _bootstrap_inner
self.run()
File "E:\Python36\lib\threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "E:\Python36\lib\subprocess.py", line 1063, in _readerthread
buffer.append(fh.read())
UnicodeDecodeError: 'gbk' codec can't decode byte 0xb6 in position 1061: illegal multibyte sequence
Traceback (most recent call last):
File "D:/directory/test/test01.py", line 29, in <module>
main()
File "D:/directory/test/test01.py", line 22, in main
ret = ctx.call('run', tt)
File "E:\Python36\lib\site-packages\execjs\_abstract_runtime_context.py", line 37, in call
return self._call(name, *args)
File "E:\Python36\lib\site-packages\execjs\_external_runtime.py", line 92, in _call
return self._eval("{identifier}.apply(this, {args})".format(identifier=identifier, args=args))
File "E:\Python36\lib\site-packages\execjs\_external_runtime.py", line 78, in _eval
return self.exec_(code)
File "E:\Python36\lib\site-packages\execjs\_abstract_runtime_context.py", line 18, in exec_
return self._exec_(source)
File "E:\Python36\lib\site-packages\execjs\_external_runtime.py", line 87, in _exec_
output = self._exec_with_pipe(source)
File "E:\Python36\lib\site-packages\execjs\_external_runtime.py", line 103, in _exec_with_pipe
stdoutdata, stderrdata = p.communicate(input=input)
File "E:\Python36\lib\subprocess.py", line 843, in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
File "E:\Python36\lib\subprocess.py", line 1113, in _communicate
stdout = stdout[0]
IndexError: list index out of range
网上查看资料说是execjs默认使用的是gbk格式,在加载utf-8编码格式的文件的时候会报此错误(实际上我修改文件编码格式后保存再次运行还是报错)。其中一个解决办法是修改execjs的subprocess.py文件:
修改后保存,再次运行:
搞定。