用Python列出哔哩哔哩up主剧集目录
專 欄
❈丁果,Python中文社区作者。对django、pyqt、opencv、tornado感兴趣。
GitHub:https://github.com/lidingke
❈
特别喜欢看木鱼水心的解说,特别是木鱼微剧场系列。有点麻烦的是剧集都是离散的,为此写个爬虫把url爬下来,并根据标题按剧集分类,列出了一个目录。这样以后看起来就方便了,不用一页一页找了。
github仓库如下:
https://github.com/lidingke/muyushuixin
这篇文章主要讲三部分内容:
1、爬取内容并解析生成目录的流程。 2、用到的一些小tips。 3、哪部分处理的不好以及计划内的改进。
目标
列出目录并按剧集分类,按照观看人数排序。选择B站的原因是B站较少有视频失效的情况,而且B站的架构更流畅更适合爬虫爬取。
爬虫部分
爬虫的目标地址是blibili上的木鱼水心的空间,该空间的结构是分页式的,总共17页。
正常套路就是用字符串的format方法拼接出17个url,并用request获取,然后lxml+xpath解析出相应标签里的内容就行了,需要解析出的html元素就是标题和观看人数。
通过解析标题里的“《》”来解析出标题,通过“()”里的part来解析出序号。图中画红框就是需要解析出的内容。
明确了思路就开始写了,结果发现获取不到相应的信息,爬取到的网页会直接跳登陆页面。遇到这种情况我以为是要搞模拟登陆,结果配置好cookie后发现还是获取不到。
折腾了好久,发现原因是B站的空间内容是动态生成的。搞清楚这一点后,果断打开chrome的开发者的Network栏抓包,设置到XHR,最终找到了json地址,我们需要的内容在getSubmitVideos这一栏下,如下图所示。
后面的事就简单了,按照这个地址拼接成分页的url,request这些url得到到json数据,连lxml+xpath都不用。
虽然没用上xpath,这里还是提一下,用chrome可以自动生成xpath的匹配规则,在相应的html标签上右键->Copy->Copy Xpath就可以了。
总结一下就是静态网页用xpath或者BS,动态网页就是抓包找获取json的url。
解析部分
需要的value是获取到的json中的data-vlist-(title,play,aid),其中title就是标签,play是人气,aid是视频id,最后的aid是用来拼接单个视频url的。
小tips1,采用继承自collection的自定义类:
保存解析后的值用了一个自定义的类——Schedule,该类继承自MutableMapping,这是一个自定义字典。
将解析的代码块放入setitem方法,最后输出的的格式化代码放入str方法中。
这么做可以在节省大量代码的情况下保证程序的规范,一般我们需要让对象的表现更自然一些的话可以这么做。
小tips2,用元组做字典的键:
元组是一种可hash的数据结构,为了实现更细粒度的key,采用元组是一种较好的办法。
具体到这个自定义类中,应该改用剧集名+分剧序列号作为key,比如('人民的名义',1)这样的,并且这样的key也可以方便用起来时拆包。
不过有点不好的是,如果要搜索的时候就不好用in dict.keys()这样的方法了,得遍历keys。所以在getitem方法中选择返回符合剧集名的所有item的列表。
小tips3,排序时使用key和reverse属性:
程序中用到两次排序,其中一次是这样的,
show_list.sort(key=lambda x: x[1], reverse=True)
这里用一个lambda函数将元组的序列为1,也就是第二个值作为排序索引,同时用reverse逆了个序。
如果不用lambda,可以用operater模块中的itemgetter方法,不用reverse也可以用切片中的[::-1]逆序。
需要的改进
解析分剧集序列号这部分代码用的是字符串的find方法和正则模块,搭配大量的if-else,简直糟糕透了。这么做的原因也是分剧在标题中格式不统一,有按照P和Part来分的,有按照上中下来分的,还有Part加罗马数字分的。打算后面以状态机的方式写个正则来一劳永逸的解决这个问题。
用自定义的数据结果在解析和排序时也是挺绕的,打算后面按照数据库设计范式来改,不知道利用数据库范式的设计会不会有所改善。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- python数据类型强制转换实例详解
- PHP使用OB缓存实现静态化功能示例
- 在Keras中实现保存和加载权重及模型结构
- Tensorflow与Keras自适应使用显存方式
- Python类及获取对象属性方法解析
- Keras实现DenseNet结构操作
- python中format函数如何使用
- keras得到每层的系数方式
- 解决TensorFlow调用Keras库函数存在的问题
- php判断电子邮件是否正确方法
- python db类用法说明
- python中wheel的用法整理
- 使用Keras训练好的.h5模型来测试一个实例
- python中查看.db文件中表格的名字及表格中的字段操作
- Ubuntu 16.04中Laravel5.4升级到5.6的步骤