最近在B站漫画发现好多白嫖资源,但是只能在线看或者用手机app看,眼睛很累,所以想着怎么把资源传到kindle上,所以准备写个爬虫。

转自:http://hentaix.cn/B%E7%AB%99%E6%BC%AB%E7%94%BB%E7%88%AC%E8%99%AB%E5%AE%9E%E6%88%98/

源码:https://github.com/xuruoyu/bilibili_manga_downloader

0x01 背景

先访问链接https://manga.bilibili.com/mc25966/376715?from=manga_detail
先看看网站请求吧

嗯,果然一堆请求,可以看出图片都是存放在cdn中的,但是每张图片都有token,如下图

这就简单了,只要能把图片是怎么请求的搞清楚就行了,嗯。。。果真这么简单么,果然我想多了。。。。

然后请求中包含一个data.index的请求,这个请求也是从cdn来的,目测这个文件就是包含所有图片的索引,来看看这个请求。

果然这个请求是个加密的文件。。。

0x02 加密的索引文件分析

我们用16进制文件查看器看看这个文件

这个文件看样子并不大,推测如果js要能够解析该数据的话,最终这个请求应该为json,或者文本(可能性不大,js处理起来太麻烦),但是从图片链接来看每张图片至少应该是个40 bytes的hash值,这个网页总共有60多张图片,那么应该大小为40*60>2400字节,但是这个请求只有1512字节,猜测是压缩过的

整理以下目前的猜测:

  1. 该请求最终目标为json,或者ascii文本,大概率是json。
  2. 这个请求应该是压缩过的,压缩算法未知。

从请求来看,目前只能获取到这些信息,那么开始分析代码吧~~~

0x03 初步窥探代码

webpack!!!哎。。。。还带了4层混淆,果然现在前端代码也不如从前容易分析了。
没办法,一点点解析吧~~~

果断先把代码dump下来,由于没有map文件,只能从混淆的代码中来分析了。

由于目标是如何解析那个data.index请求,那我们就从请求下手,看看能不能窥探到什么。
先全局搜一下字符串,看看能不能搜到data.index,嗯。。。果然啥也没有。。。

然后在看看请求,看来data.index文件也是通过请求获得的。

换个关键字再试试,GetImageIndex,看来有发现

从这个代码结构来看,应该是个post请求的promise对象,反向在搜索以下谁调用了这个代码,没办法,还是只能通过关键字getIndexFileUrl搜索一下

看来有新的发现,从这个对象名可以看出这应该是加载章节图片,其中有个调用引起了注意tn.decodeIndexFile,从文件名来看这个就应该是解码索引文件的函数了,从参数看应该传入了三个值作为输入:

  1. seasonId
  2. episodeId
  3. indexData

这几个参数中,episodeId容易理解即为章节id,seasonId应该也是个数值,data应该就是那个buffer

再尝试看看这个函数是怎么实现的,函数如下图:

果然webpack打包的代码真的恶心。。。
刚才猜测的有三个参数,这里派上了用场,Object(Jt.a)(t.seasonId, t.episodeId, o);应该就是解码索引文件的函数了,其中o应该就是那个buffer,从上面来看代码中强制转换为了Uint8Array,现在就要一层一层拨开webpack的封装了。。。

先看看那个Jt是啥,向上检索发现调用的是只一段

      }(), Jt = a("J3ON"), Xt = a("vDqi"), Kt = a.n(Xt).a.create();

好嘛。。。又是这种,那再看看J3ON是啥,这次换了个js文件,跳转到了bili.xxxx.js

看到了一个"a",这就是刚才那个Object(Jt.a)调用的函数,从这里看应该是混淆到r了,在检索一下,看看这个r是啥玩意

有点意思了,看到上面有一数组定义var u = [66, 73, 76, 73, 67, 79, 77, 73, 67]刚好就是那个BILICOMIC的magic,看来这就是解码函数了,然后再看看下面的代码,一堆位操作符,一定是这里了~~~先把代码粘出来

    var u = [66, 73, 76, 73, 67, 79, 77, 73, 67], l = u.length;function r (r, i, o) {return s(this, void 0, void 0, function () {var e, n;return c(this, function (t) {switch (t.label) {case 0:if (!f(r) || !f(i))throw new TypeError("[Indexer] Both seasonId and episodeId should be number.");if (null,"string" == typeof o && (o = function (t) {for (var e = atob(t.split(",")[1] || t), n = new ArrayBuffer(e.length), r = new Uint8Array(n), i = 0; i < e.length; i++)r[i] = e.charCodeAt(i);return r}(o)),e = null,!o.length || !function (n) {var r = !0;return u.some(function (t, e) {if (n[e] !== u[e])return !(r = !1)}),r}(o))throw new TypeError("[Indexer] Invalid index data.");return e = o.slice(l),n = function (t, e) {var n = new Uint8Array(new ArrayBuffer(8));return n[0] = e,n[1] = e >> 8,n[2] = e >> 16,n[3] = e >> 24,n[4] = t,n[5] = t >> 8,n[6] = t >> 16,n[7] = t >> 24,n}(r, i),function (t, e) {for (var n = 0, r = t.length; n < r; n++)t[n] = t[n] ^ e[n % 8]}(e, n),[4, function (o) {return s(this, void 0, void 0, function () {var e, n, r, i;return c(this, function (t) {switch (t.label) {case 0:e = "index.dat",r = n = null,t.label = 1;case 1:return t.trys.push([1, 3, , 4]),[4, Object(a.loadAsync)(o)];case 2:return n = t.sent(),[3, 4];case 3:throw t.sent();case 4:if (!(i = n.files[e]))throw new Error('[Indexer] Can not find file "' + e + '".');t.label = 5;case 5:return t.trys.push([5, 7, , 8]),[4, i.async("text")];case 6:return r = t.sent(),[3, 8];case 7:throw t.sent();case 8:return [2, r]}})})}(e)];case 1:return [2, t.sent()]}})})}

没办法,接着手动剥离,function r (r, i, o)有三个参数,按照顺序解释应该是seasonId、episodeId、indexData,然后再看看这里返回值里面e = o.slice(l),l应该是前面BILICOMIC的长度,应该是先删除这部分数据构成新的buffer,再往下看,代码构建了一个新的数组n,n长度为8,其中的值是通过seasonId和episodeId通过位运算的到的。

再往下看,终于看到了对buffer下刀的位置了

                function (t, e) {for (var n = 0, r = t.length; n < r; n++)t[n] = t[n] ^ e[n % 8]}(e, n)

看来这个n数组是一个mask,剩下的就简单了,用python实现以下上面两步:

  1. 构建mask数组n
  2. 对buffer进行异或
    python实现如下
def decode_index_data(season_id: int, episode_id: int, buf):u = [66, 73, 76, 73, 67, 79, 77, 73, 67]l = len(u)e = buf[l:]print(buf)_e = []for i in range(len(e)):_e.append(e[i])e = np.uint8(_e)print(e)n = [0, 0, 0, 0, 0, 0, 0, 0]n = np.array(n, dtype='uint8')n[0] = episode_idn[1] = episode_id >> 8n[2] = episode_id >> 16n[3] = episode_id >> 24n[4] = season_idn[5] = season_id >> 8n[6] = season_id >> 16n[7] = season_id >> 24print(n)_n = 0r = len(e)while _n < r:e[_n] = e[_n] ^ n[_n % 8]_n = _n + 1passprint("解密后:")print(e)ret = bytes(e)

输出以下结果

果然看到了PK 有木有!!!!确认是个zip文件,里面包含了一个index.dat的文件,解压出来果然是个json

0x04 下载这些图片~~~

剩下就简单了,这个json包含了所有图片的hash值,其中有个pics的数组,按照顺序存储就好啦~~

0x05 代码

https://github.com/xuruoyu/bilibili_manga_downloader

B站(哔哩哔哩)漫画爬虫实战相关推荐

  1. 爬虫实战之爬虫漫画(复现)

    目录 爬虫实战---动态网页内部加载 复现爬虫漫画 思路 所有章节的标题和URL 找到所有章节漫画内容 保存漫画 爬虫实战-动态网页内部加载 动态网页的爬取和静态网页的爬取不一样,静态网页我们想要的资 ...

  2. python 突破b站验证码_Python爬虫模拟登陆哔哩哔哩(bilibili)并突破点选验证码功能...

    写在前面 今天带给大家一个突破点选验证码的案例,利用爬虫模拟登陆哔哩哔哩,并且把一些采坑的地方给大家强调一下! 一.需求分析 模拟登陆哔哩哔哩 网站链接: https://passport.bilib ...

  3. 菜鸟弟弟从零开始的爬取Bilibili弹幕的Python爬虫教程-哔哩哔哩 - ( ゜- ゜)つロ 干杯~

    从零开始的爬取Bilibili弹幕的Python爬虫教程 或许可以作为一个爬虫小白的练手的demo? 还是先看看什么是爬虫吧!(还有Bilibili! ) 网络爬虫: 网络爬虫(又称为网页蜘蛛,网络机 ...

  4. 【爬虫+数据清洗+可视化分析】用Python分析哔哩哔哩“阳了“的评论数据

    目录 一.背景介绍 二.爬虫代码 爬虫部分不作讲解. 三.可视化代码 3.1 读取数据 3.2 数据清洗 3.3 可视化 3.3.1 IP属地分析-柱形图 3.3.2 评论时间分析-折线图 3.3.3 ...

  5. 哔哩哔哩 国外 android,哔哩哔哩漫画无限漫币版app

    哔哩哔哩漫画无限漫币版app是由国内最大的ACG平台"B站"倾情打造的海量高清正版漫画平台,我们不仅提供类似直播打赏的功能,对用户,榜单本身是确定明星偶像的地位,有利于增强用户与作 ...

  6. 从小破站到大B站:哔哩哔哩变味了?

    配图来自Canva 继<后浪>和<入海>之后,B站又拍片了. 作为B站三部曲的最终章,B站选择在自己11周年之际,发布这条由老年人主演的<喜相逢>.不同于之前的两部 ...

  7. android442电视怎么投屏,哔哩哔哩怎么投屏电视? b站投屏的方法

    B站看视频想要投放到电视中观看,该怎么投屏呢?下面我们就来看看详细的教程. 软件名称:Bilibili客户端( B站弹幕网安卓版) v5.20.1 for android(安卓)版软件大小:47.8M ...

  8. Python3 爬虫实战 — 模拟登陆哔哩哔哩【滑动验证码对抗】

    登陆时间:2019-10-21 实现难度:★★★☆☆☆ 请求链接:https://passport.bilibili.com/login 实现目标:模拟登陆哔哩哔哩,攻克滑动验证码 涉及知识:滑动验证 ...

  9. b站电脑客户端_B站(哔哩哔哩) 视频批量下载工具#电脑版##更新

    长按识别下方二维码关注 B站(哔哩哔哩) 视频下载工具#电脑版 软件介绍 这是一款可以在电脑端下载B站视频的工具 相信看过我文章的都知道以前已经推荐过一款 >>点我跳转<< 但 ...

最新文章

  1. Delphi.net Chrome
  2. 给网页图标字体 Font Awesome 添加动画效果
  3. 客户端AJAX验证表单
  4. c#获取当前应用程序所在路径
  5. Linux Android 常用shell命令汇总 不断更新
  6. sours insight 使用技巧
  7. 工商银行支付接口开发Java
  8. shell美元符_Linux Shell中的美元符号$ | 学步园
  9. 最重要的三种能力:思考力、行动力、表达力
  10. 亚信安全:发挥MSS托管安全运营服务优势 让你真正“减负”
  11. CSS系列之连续的字母或数字在Html盒子中不会自动换行,直接溢出
  12. deepin恢复出厂设置_如何恢复出厂设置
  13. python:实现绘制粽子(附完整源码)
  14. TOM企业邮箱注册流程是什么,如何开通邮箱
  15. w ndows无法连接到System,Windows无法连接到System Event Notification Service服务解决方法...
  16. Windows自带输入法中文符号或全角半角切换
  17. 编c语言程序 任意输入一个长方体的长,宽,高,计算体积.,C语言程序,由键盘任意输入一个长方体的长.宽.高,计算体积....
  18. 浙江省计算机二级题库excel,2014年浙江省计算机二级题库Excel03.xls
  19. 模仿是学习的最佳途径
  20. Qt 使用Visa库与数字仪器仪表TCP/IP通信(SCPI指令自动化测试)

热门文章

  1. CSS3 3D transform变换,不过如此
  2. 搭档之家|生活中的经济学:边际效用递减
  3. 摩西十诫——自我约束
  4. Lua Math函数
  5. python编程100例海绵宝宝-宝宝的python
  6. C/C++实习工作应该具备那些能力?才能拿更好的工作和薪资!
  7. 突然35岁:捡点我的职业生涯(转)
  8. 【MTK】flashlight驱动部分调试
  9. 【MacOS】mysql安装配置
  10. 存储在阿里云的视频在浏览器中直接播放