为什么要保存 B 站视频的播放记录呢? 因为 B 站的历史记录,最多保存 3 个月,超过 3 个月自动清除。所以我专门写一个脚本,将历史记录导出,保存在数据库中,一来是是以备以后需要时能够找到,二来也方便对这些视频按自己的习惯进行分类和做备注。

B 站的历史记录,后端以 Web API 的方式将数据提交到前台,前后端是分离的。在浏览器中滚动条向下滑动的时候,动态提交 HTTP 请求,数据分批渲染。首先,我们需要学会如何查看 Web API。以 Chrome 浏览器为例。进入 B 站,点击右上角 「历史记录」按钮,然后按下 F12,调出开发者调试工具,切换到 Network 页签。因为历史记录是动态加载的,所以再点击筛选区的 「XHR」(XmlHttpRequest,其实就是 HTTP Request)。点击后,下面显示的都是与 xhr 相关的内容。

点击第二行(cursor***),右边出现多个页签,在 Headers 页签中,重要的有 Request URL 和 Request Headers 区域的 Cookie。将 Cookie 的内容拷贝到文本文件,比如将文件名命名为 cookie.txt。在 Chrome 浏览器中,默认情况下,右键不出现拷贝菜单,需要三次点击,选中整个字段,此时右键菜单有拷贝菜单出现。


切换到 Preview 页签,可以看出,B 站每次从后台返回 20 笔记录。我们可以通过 Preview 和 Response 来了解返回信息的数据结构。


向下滚动历史记录,左边出现更多的动态的请求内容,这些请求的 url path 主要的差异在 cursor 后面的 max 和 view_at 参数不同,max 和 view_at 来自上一次 response。换一个角度来说,每次服务端的 response,除本次 20 笔历史记录外,还同时返回下一次请求的 max 参数(表示最大的目标 ID号)和 view_at (查看的时间戳)参数。下图展示了刚才所述内容:


当所有的历史记录完毕,最后一次请求的响应,cursor 的 max = 0, view_at = 0, ps = 0。基于了解的这些信息,接下来可以通过 Python 代码来获取历史记录了。本次实现两个功能:

  • 获取历史记录
  • 保存到数据库

获取 B 站历史记录

编写一个从 url 返回 json 数据的函数。在该函数中,request header 参数有两个作用:提供客户端 cookie,以及将请求伪装为浏览器。

def get_response_json(url, req_headers):"""根据url获取json格式的response文本"""resp = requests.get(url, headers=req_headers)return json.loads(resp.text)

从 B 站历史记录的 api 获取 json 格式数据,刚刚提到的客户端 cookie,作用是提供客户端的登录信息,cookie 信息在 request header 中。通过面向对象的方式封装代码。将 cookie 和 header 的加载放在 _init_ 方法中。BiliHistory 类对外提供两个方法:

  • get_all_history(): 获取所有的浏览历史,list 类型。每一个元素为 dict 类型
  • save_db(): 将历史记录保存至 sqlite 数据库 (history.db3,硬编码)

BiliHistory 类的完整代码如下:

class BiliHistory(object):def __init__(self, cookie_file):self.base_url = "https://api.bilibili.com/x/web-interface/history/cursor"self.cookie = self._get_cookie_content(cookie_file)self.request_headers = self._set_req_headers()def _get_cookie_content(self, cookie_file):"""从cookies.txt中读取cookie"""with open(cookie_file, 'r') as fp:cookies = fp.read()return cookiesdef _set_req_headers(self):"""设置请求头:1)模拟浏览器;2)提供cookie"""headers = {"Accept": "*/*","Accept-Encoding": "gzip, deflate, br","Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6","Connection": "keep-alive","Cookie": self.cookie,"Host": "api.bilibili.com","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "  \"Chrome/88.0.4324.96 Safari/537.36 Edg/88.0.705.50"}return headersdef _get_history(self, max, view_at, business):"""根据url以及查询字符串中三个参数,获取20个历史记录,history_list: 为包含dict的列表,每一个list是一个历史记录cursor: 下一个请求的cursor信息"""url = self.base_url + f"?max={max}&view_at={view_at}&business={business}"resp = get_response_json(url, self.request_headers)history_list = resp.get("data").get("list")next_cursor = resp.get("data").get("cursor")return history_list, next_cursor def get_all_history(self):"""获取所有的的浏览历史记录"""histories = []max = 0view_at = 0business = ''ps = 20while(ps!=0): # ps为0表示后面没有记录history, cursor = self._get_history(max, view_at, business)max = cursor.get("max")view_at = cursor.get("view_at")business = cursor.get("business")ps = cursor.get("ps")for item in history:histories.append(item)time.sleep(0.1)return historiesdef save_db(self):"""保存到sqlite3数据库"""histories = self.get_all_history()for item in histories:history = item.get("history")view_time = item.get("view_at")# 如果记录不在数据库中,则新增记录if is_url_exists(view_time) == False:url_content = {"title": item.get("title"),"business": history.get("business"),"bvid": history.get("bvid"),"cid": history.get("cid"),"epid": history.get("epid"),"oid": history.get("oid"),"page": history.get("page"),"part": history.get("part"),"dt": history.get("dt"),"author_name": item.get("author_name"),"videos": item.get("videos"),"is_fav": item.get("is_fav"),"tag_name": item.get("tag_name"),"view_at": item.get("view_at"),"progress": item.get("progress"),"show_title": item.get("show_title"),"cover": item.get("cover"),"uri": item.get("uri")}create_url_info(url_content)

每次获取 20 条记录,在每次 Http 请求后,暂停 0.2 秒钟。

数据保存到数据库

使用 sqlalchemy ORM 的数据创建和查询功能。sqlalchemy 的用法本篇不讲述,只提供数据库操作的代码。有需要的小伙伴请参考我的博客:SQLAlchemy简明教程。

数据库操作的相关代码如下:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from model import BiliHistoryengine = create_engine("sqlite:///history.db3", echo=False)
session = sessionmaker(bind=engine)()def is_url_exists(view_time):"""根据浏览时间判断记录是否存在"""item = session.query(BiliHistory).filter(BiliHistory.view_at==view_time).first()return item != Nonedef create_url_info(url):url = BiliHistory(title = url.get("title"),business = url.get("business"),bvid = url.get("bvid"),cid = url.get("cid"),epid = url.get("epid"),oid = url.get("oid"),page = url.get("page"),part = url.get("part"),dt = url.get("dt"),author_name = url.get("author_name"),videos = url.get("videos"),is_fav  = url.get("is_fav"),tag_name = url.get("tag_name"),view_at = url.get("view_at"),progress = url.get("progress"),show_title = url.get("show_title"),cover =url.get("cover"),uri = url.get("uri"))session.add(url)session.commit()session.close()

源码

github - bilibili history

参考

Bilibili 历史记录API

如何用python获取和保存B站历史播放记录相关推荐

  1. 用python做通讯录包括姓名地址qq_我是如何用Python获取整个学校女生电话和QQ?技术撩妹...

    前言: 这个没什么技术难度,懂爬虫的人和程序员都可以用学的语言写出来 只是很多安全问题的存在,的确影响着我们的生活, 希望大家可以认识到一些网站的后台密码的规则与自己的安全性 简单的说,就是是程序员的 ...

  2. 如何用python获取美股财报日历

    自7月份开始,美股迎来二季度财报季,包含微软,特斯拉,AMD在内的行业翘楚纷纷公布了二季度财报,而且都超过市场预期.美股财报是美股投资者非常关注的数据资讯,本文将介绍如何用python获取财报日历. ...

  3. Python获取所有股票代码以及股票历史成交数据分析(二)

    接上一篇获取完股票代码信息后,我们打开东方财富网,一通瞎点,进到某股票的K线板块 打开调试界面,XHR中并没有我们想要的数据,从网页数据加载来看应该不是实时资源的肯定有个传输的地方,我们先清空完所有N ...

  4. Python获取所有股票代码以及股票历史成交数据分析

    一 .股票代码获取 最近入坑股市,摸爬滚打,只觉得自己长高了一茬,依旧是韭菜被无情收割呜呜呜呜,因此想了想弄个,所有股票的数据来看下资金动向,类似于龙虎板但是跨度大些. 从网上看了些资源发现并没有自己 ...

  5. 如何用python获取通达信数据_Python读取通达信本地数据

    一.介绍 python获取股票数据的方法很多,其中 Tushare 财经数据接口包很好用,当然,也可以通过通达信本地的数据获取,这样更为方便. 日线数据存在这路径下 D:\通达信\vipdoc\sh\ ...

  6. 如何用python获取文献_[python]eutilities获取文献题录

    之前这篇文章(路人乙小明:用entrez eutilities来查pubmed文献)提过如何利用entrez eutilities查找文献.当时在最后面获取论文摘要和题录的部分直接用了text的模式. ...

  7. 如何用Python获取基金行情并分析年度表现优异基金,解锁赚钱秘密?

    本文转载自公众号:数人之道 若需要本文所有源代码文件及数据可视化结果文件,请扫描下方二维码关注『数人之道』公众号,回复 2021基金 获取. 昨天是 2022 年工作日的第一天,同时也是国内证券市场的 ...

  8. 【python】如何用python生成并保存gif动态图

    有时候在展示实验结果的时候,通过一个gif的动态图可以很好进行表现,通过python生成这种gif图的原理实际上就是通过每次生成图像快照,将它们保存下来,然后通过imageio这个库再将他们组合成一个 ...

  9. python实时股票数据折线图_如何用python获取实时的股票数据?

    您需要具备以下指示(以Python为例) 1.相关模块及应用原因 requests模块:用于想接口发送请求使用 multiprocessing模块:因为股票数量大单个循环发送请求效率太低,建议进行并发 ...

最新文章

  1. preg_grep用法
  2. PHP-Webshell免杀研究
  3. JAVA的extends用法
  4. Javascript---Immediately-Invoked Function Expression (IIFE)立即执行的函数表达式
  5. 工作274:ele-图标使用
  6. c语言a 寻路算法,JS/HTML5游戏常用算法之路径搜索算法 A*寻路算法完整实例
  7. 疑似小米11系列旗舰跑分曝光:骁龙875性能突破天际
  8. apache jmeter 使用
  9. 你知道Object中有哪些方法及其作用吗?
  10. Netty之Pipeline总结
  11. 用C 程序理解汉字的机内码表示
  12. linux IIC子系统分析(七)——实例分析通过i2c-dev操作I2C设备
  13. 看看老毛子士兵干的事情!
  14. fastposter 1.6.0 发布 电商级海报生成器
  15. Rxjava+retrofit+okHttp+mvp网络请求数据
  16. TBTOOLS的使用:用TBTOOLS实现多序列比对
  17. 易贝按关键字搜索EBAY商品 API 返回值说明
  18. Laravel 接受Ajax的POST请求
  19. 稀土掘金 2022 开发者大会观后总结
  20. uniapp 即时通讯_uni-App 仿微信 App 即时通讯|vue+uniapp 聊天

热门文章

  1. ios浏览器滚动交互太差_使用ScrollMagic.js构建交互式滚动网站
  2. 程序员这样面试,拿到offer的几率是80%
  3. 储能逆变器资料,原理图,pcb,源代码
  4. 安利一款二维码api
  5. 看P2P流媒体之风生水起
  6. 良心安利SolidEdge 3d模型素材
  7. wireshark显示没有找到接口
  8. matlab brush怎么用,教程 | 轻松搞定brushlettering超美晒图大法,不用PS
  9. 35岁以后的Java程序员出路在哪里?帮你突破瓶颈
  10. 创业者具备了这些素质和品德,距离成功就不远了