所有源码我放到了 蓝奏云,所有人可以免费下载,知识用来共享,csdn下载需要积分对新手不是很友好:https://www.lanzoux.com/ibiWDg4gc2f
download.py为主入口文件,其中有两个参数你可能需要调节

self.run_t = 7
self.save_dir = r"D:\喜马拉雅"

一个是同时下载个数,一个是保存路径,剩下的需要调节最下面的整本书的albumId了,另外其中我加入了转换mp3格式的函数,不过我默认注释掉了,如果想使用可以配置ffmpeg到环境变量中去

前言

这几天一直听听评书,发现喜马拉雅上的资源很多,不过很可惜都是付费的,所以我冲了一个月会员,简单写个爬虫,爬下来几10部,够我一年听的了

开始分析

打开chrome控制台,点击播放,最先拿到的一个接口就是

https://mpay.ximalaya.com/mobile/track/pay/244130607/?device=pc

当然这个是付费的一部书,所以如果你浏览器不带 会员的cookie是访问不到的,其中的数字 244130607,这个在他们的接口中叫做 trackId, 每个音频文件对应唯一的一个 trackId

也就是对应这个界面的后面的数字,通过这个唯一的trackId可以获取到音频文件,那么看一下这个接口返回的内容

{"ret": 0,
"msg": "0",
"trackId": 244130607,
"uid": 170217760,
"albumId": 30816438,
"title": "《三体》第一季 第十集 聚会与大撕裂",
"domain": "http://audiopay.cos.xmcdn.com",
"totalLength": 12780565,
"sampleDuration": 0,
"sampleLength": 0,
"isAuthorized": true,
"apiVersion": "1.0.0",
"seed": 9583,
"fileId": "27*31*44*62*1*8*6*48*52*4*6*17*16*6*35*35*6*43*25*27*48*63*58*4*50*47*60*64*15*39*59*49*2*36*48*48*16*58*18*44*2*32*12*7*52*64*51*26*29*4*22*",
"buyKey": "617574686f72697a6564",
"duration": 1578,
"ep": "20NvOoh6T39X3qwKO4cY5g5bVhg+1nfPHIQafFTmCXihnrqF2PjczO8O0auK1KJhDrJ30XMYfKJo2uz+xgwd3rwRPi5f",
"highestQualityLevel": 1,
"downloadQualityLevel": 1,
"authorizedType": 1
}

这里,我充会员了,所以可以直接用浏览器中打开这个url,其中有用的字段有了只有几个 seedfileId两个通过js加密算法计算出 m4a的路径,并拼接主域名,然后 ep 经过另一个加密算法得到url的访问参数buy_key sign token timestamp,最后将它们拼接到一起才是一个完整的 音频的url

两个js加密算法

经过我调试我分别找到了这两个加密的 js算法

  1. 计算 m4a的路径js算法:
function vt(t) {this._randomSeed = t,this.cg_hun()}vt.prototype = {cg_hun: function() {this._cgStr = "";var t = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890", e = t.length, n = 0;for (n = 0; n < e; n++) {var r = this.ran() * t.length, o = parseInt(r);this._cgStr += t.charAt(o),t = t.split(t.charAt(o)).join("")}},cg_fun: function(t) {t = t.split("*");var e = "", n = 0;for (n = 0; n < t.length - 1; n++)e += this._cgStr.charAt(t[n]);return e},ran: function() {this._randomSeed = (211 * this._randomSeed + 30031) % 65536;return this._randomSeed / 65536},};c = function(t, e) {var n = new vt(t).cg_fun(e);return "/" === n[0] ? n : "/".concat(n)
}console.log(c(9583,"27*31*44*62*1*8*6*48*52*4*6*17*16*6*35*35*6*43*25*27*48*63*58*4*50*47*60*64*15*39*59*49*2*36*48*48*16*58*18*44*2*32*12*7*52*64*51*26*29*4*22*"))

node跑一下可以得到 m4a的路径
输出:

/group3/M04/9E/88/wKgMbF4ejn2TfGPRAMMEFYoRHXs027.m4a
  1. 通过ep来计算url参数的js算法:
Z = function() {throw new TypeError("Invalid attempt to destructure non-iterable instance")}J = function(t, e) {var n = [], r = !0, o = !1, i = void 0;
try {for (var a, u = t[Symbol.iterator](); !(r = (a = u.next()).done) && (n.push(a.value),!e || n.length !== e); r = !0);
} catch (t) {o = !0,i = t
} finally {try {r || null == u.return || u.return()} finally {if (o)throw i}
}
return n
}Q = function(t) {if (Array.isArray(t))return t
}tt = function(t, e) {return Q(t) || J(t, e) || Z()
}function yt(t, e) {for (var n, r = [], o = 0, i = "", a = 0; 256 > a; a++)r[a] = a;for (a = 0; 256 > a; a++)o = (o + r[a] + t.charCodeAt(a % t.length)) % 256,n = r[a],r[a] = r[o],r[o] = n;for (var u = o = a = 0; u < e.length; u++)o = (o + r[a = (a + 1) % 256]) % 256,n = r[a],r[a] = r[o],r[o] = n,i += String.fromCharCode(e.charCodeAt(u) ^ r[(r[a] + r[o]) % 256]);return i
}var mt = yt("xm", "Ä[ÜJ=†Û3Áf÷N")gt = [19, 1, 4, 7, 30, 14, 28, 8, 24, 17, 6, 35, 34, 16, 9, 10, 13, 22, 32, 29, 31, 21, 18, 3, 2, 23, 25, 27, 11, 20, 5, 15, 12, 0, 33, 26]bt = function(t) {var e1 = yt(function(t, e) {for (var n = [], r = 0; r < t.length; r++) {for (var o = "a" <= t[r] && "z" >= t[r] ? t[r].charCodeAt() - 97 : t[r].charCodeAt() - "0".charCodeAt() + 26, i = 0; 36 > i; i++)if (e[i] == o) {o = i;break}n[r] = 25 < o ? String.fromCharCode(o - 26 + "0".charCodeAt()) : String.fromCharCode(o + 97)}return n.join("")}("d" + mt + "9",gt),e2 = function(t) {if (!t)return "";var e, n, r, o, i, a = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1];for (o = (t = t.toString()).length,r = 0,i = ""; r < o; ) {do {e = a[255 & t.charCodeAt(r++)]} while (r < o && -1 == e);if (-1 == e)break;do {n = a[255 & t.charCodeAt(r++)]} while (r < o && -1 == n);if (-1 == n)break;i += String.fromCharCode(e << 2 | (48 & n) >> 4);do {if (61 == (e = 255 & t.charCodeAt(r++)))return i;e = a[e]} while (r < o && -1 == e);if (-1 == e)break;i += String.fromCharCode((15 & n) << 4 | (60 & e) >> 2);do {if (61 == (n = 255 & t.charCodeAt(r++)))return i;n = a[n]} while (r < o && -1 == n);if (-1 == n)break;i += String.fromCharCode((3 & e) << 6 | n)}return i}(t)).split("-")console.log(e1)
}var c = bt("20NvOoh6T39X3qwKO4cY5g5bVhg+1nfPHIQafFTmCXihnrqF2PjczO8O0auK1KJhDrJ30XMYfKJo2uz+xgwd3rwRPi5f")

这段js比较复杂,调试的时候坑死我了,不在同一个地方,导致我来回复制,最终于才把这个算法整理到这一个js文件中,依然用 node跑一下,输出:

['617574686f72697a6564','ef9a0678d77870843ef203d6333ce021','5790','1598533668'
]

这几个参数分别对应的是:buy_key sign token timestamp
有了这了两个js算法就可以完全的解析 这个接口返回的参数了。

python 代码仿写加密算法

  1. 计算 m4a路径加密算法

class vt():def __init__(self,t):self._randomSeed = tself.cg_hun()def ran(self):self._randomSeed = (211 * self._randomSeed + 30031) % 65536return self._randomSeed / 65536def cg_hun(self):self._cgStr = ""t = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890"e = len(t)n = 0for i in range(e):r = self.ran() * len(t)o = int(r)self._cgStr += t[o]t = "".join(t.split(t[o]))def cg_fun(self,t):t = [int(i) if i else 0 for i in t.split("*")]e = ""n = 0;for n in range(n,len(t)-1):e += self._cgStr[t[n]]return edef path_decode(seed,fileId):c = vt(seed)p = c.cg_fun(fileId)return p if __name__ == '__main__':result = path_decode(9583,"27*31*44*62*1*8*6*48*52*4*6*17*16*6*35*35*6*43*25*27*48*63*58*4*50*47*60*64*15*39*59*49*2*36*48*48*16*58*18*44*2*32*12*7*52*64*51*26*29*4*22*")print(result)
  1. 通过ep来计算url参数的算法:
def yt(t, e):r = [0 for i in range(256)]o = 0i = ""for a in range(0,256):r[a] = a;for a in range(0,256):o = (o + r[a] + ord(t[a % len(t)])) % 256n = r[a]r[a] = r[o]r[o] = nu = 0o = 0a = 0for u in range(0,len(e)):a = (a + 1) % 256o = (o + r[a]) % 256n = r[a]r[a] = r[o]r[o] = ni += chr(ord(e[u]) ^ r[(r[a] + r[o]) % 256])return idef bt(t):def arg1(t,e):n = [' ' for i in range(256)]for r in range(0,len(t)):if "a" <= t[r] and "z" >= t[r]:o = ord(t[r]) - 97 else:o = ord(t[r]) - ord("0") + 26for i in range(0,36):if (e[i] == o):o = ibreakif 25< o:n[r] = chr(o - 26 + ord("0")) else:n[r] = chr(o + 97)return "".join(n).strip()a1 = arg1("d" + mt + "9", gt)def arg2(t):if not t:return ""e = n = r = o = i = a = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1];o = len(t)i = ""r = 0while r < o:while True:e = a[255 & ord(t[r])]r += 1if not (r < o and -1 == e):breakif (-1 == e):breakwhile True:n = a[255 & ord(t[r])]r += 1if not (r < o and -1 == n):breakif (-1 == n):breaki += chr(e << 2 | (48 & n) >> 4)while True:e = (255 & ord(t[r]))if 61 == e:return ir += 1e = a[e]if not (r < o and -1 == e):breakif (-1 == e):breaki += chr((15 & n) << 4 | (60 & e) >> 2);while True:n = (255 & ord(t[r]))if (61 == n):return ir += 1n = a[n]if not (r < o and -1 == n):breakif (-1 == n):breaki += chr((3 & e) << 6 | n)return ia2 = arg2(t)buy_key,sign,token,timestamp = yt(a1,a2).split('-')data = dict(buy_key=buy_key,sign=sign,token=token,timestamp=timestamp,)return datamt = yt("xm", "Ä[ÜJ=†Û3Áf÷N")
gt = [19, 1, 4, 7, 30, 14, 28, 8, 24, 17, 6, 35, 34, 16, 9, 10, 13, 22, 32, 29, 31, 21, 18, 3, 2, 23, 25, 27, 11, 20, 5, 15, 12, 0, 33, 26]def ep_decode(ep):data = bt(ep)return dataif __name__ == '__main__':print(ep_decode('20NvOoh6T39X3qwKO4cY5g5bVhg+1nfPHIQafFTmCXihnrqF2PjczO8O0auK1KJhDrJ30XMYfKJo2uz+xgwd3rwRPi5f'))

这个接口到此为止才算是完全可以解析。

免费接口分析

如果你没有充会员,免费的音频还是可以听的,我找到一个免费音频的接口

https://www.ximalaya.com/revision/play/v1/audio?id=324681559&ptype=1

返回值:

{"ret": 200,
"data": {"trackId": 324681559,
"canPlay": true,
"isPaid": false,
"hasBuy": true,
"src": "https://aod.cos.tx.xmcdn.com/group84/M03/4A/A6/wKg5Hl8s0cTwcp6xABQ0EbeuW5Q193.m4a",
"albumIsSample": false,
"sampleDuration": 48,
"isBaiduMusic": false,
"firstPlayStatus": true,
"isVipFree": false
}
}

这个接口还是比较简单的,返回值里面直接包含 m4a音频地址,没有加密措施,另外 url中的数字依然是 trackId,值得一提的是免费音频的trackId不能用在付费接口,我猜测是版本迭代的问题,或者是客户端不同的问题,因为当时我不只是分析网页的接口,还抓包了电脑客户端的接口,具体对应的是网页还是客户端我也忘了。

解析整本书的接口

喜马拉雅接口主要关键的有两个参数,一个是前面我说的 trackId 另一个就是albumIdtrackId 对应唯一的一个音频,而 albumId 对应的是唯一的一本书。

https://www.ximalaya.com/revision/album/v1/getTracksList?albumId=30816438&pageNum=1&pageSize=1000

返回值中就有每一集的trackId,其实喜马拉雅还有很多其他接口,搜索接口等等,一般的其他的接口需要在请求头中加入xm-sign,我也写了xm-sign的计算方法:

import requests
import time
import hashlib
import random
import json
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)# 获取sign签名def get_sign(headers):serverTimeUrl = "https://www.ximalaya.com/revision/time"response = requests.get(serverTimeUrl,headers=headers,verify=False)serverTime = response.textnowTime = str(round(time.time()*1000))sign = str(hashlib.md5("himalaya-{}".format(serverTime).encode()).hexdigest()) + "({})".format(str(round(random.random()*100))) + serverTime + "({})".format(str(round(random.random()*100))) + nowTimeheaders["xm-sign"] = signreturn headersdef get_header():headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36"}    headers = get_sign(headers)return headersif __name__ == '__main__':# 这是一个搜索接口url = "https://www.ximalaya.com/revision/search/main?core=all&spellchecker=true&device=iPhone&kw=%E9%9B%AA%E4%B8%AD%E6%82%8D%E5%88%80%E8%A1%8C&page=1&rows=20&condition=relation&fq=&paidFilter=false"s = requests.get(url,headers=get_header(),verify=False)print(s.json())

还有很多其他接口,我就懒得说了,因为我不想写了,有了这些就可以满足我下载整本书的需求了

最终整合

我写了 喜马拉雅 扫码登陆的脚本,因为我不能每次都去复制浏览器中的 cookie,这种重复劳动太傻了

import requests
import re
from threading import Thread
import time
import requests
from io import BytesIO
import http.cookiejar as cookielib
from PIL import Image
import sys
import psutil
from base64 import b64decode
import osrequests.packages.urllib3.disable_warnings()class show_code(Thread):def __init__(self,data):Thread.__init__(self)self.data = datadef run(self):img = Image.open(BytesIO(self.data))  # 打开图片,返回PIL image对象img.show()def is_login(session):headers = {'User-Agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"}url = "https://www.ximalaya.com/revision/main/getCurrentUser"try:session.cookies.load(ignore_discard=True)except Exception:passresponse  = session.get(url,verify=False,headers=headers)if response.json()['ret'] == 200:print(response.json())return session,Trueelse:return session,Falsedef login():if not os.path.exists(".cookie"):os.makedirs('.cookie')if not os.path.exists('.cookie/xmly.txt'):print("hello")with open(".cookie/xmly.txt",'w') as f:f.write("")session = requests.session()session.cookies = cookielib.LWPCookieJar(filename='.cookie/xmly.txt')session,status = is_login(session)if not status:url = "https://passport.ximalaya.com/web/qrCode/gen?level=L"response = session.get(url,verify=False)data = response.json()# with open('qrcode.jpg','wb') as f:# f.write(b64decode(data['img']))t= show_code(b64decode(data['img']))t.start()qrId = data['qrId']url = 'https://passport.ximalaya.com/web/qrCode/check/%s/%s' % (qrId,int(time.time()*1000))while 1:response = session.get(url,verify=False)data = response.json()# code = re.findall("window.wx_code='(.*?)'",response.text)# sys.exit()if data['ret'] == 0:# for proc in psutil.process_iter():  # 遍历当前process# try:#     if proc.name() == "Microsoft.Photos.exe":  #         proc.kill()  # 关闭该process# except Exception as e:#     print(e)breaktime.sleep(1)session.cookies.save()return session
if __name__ == '__main__':login()

简单的一个扫码登陆脚本,如果cookie自动保存成文件,下次使用的时候直接调用:

session = login()

就能在保持登陆状态下,访问各种接口

喜马拉雅加密算法分析相关推荐

  1. Python爬虫之JavaScript逆向,喜马拉雅加密算法分析

    前言 这几天一直听听评书,发现喜马拉雅上的资源很多,不过很可惜都是付费的,所以我冲了一个月会员,简单写个爬虫,爬下来几10部,够我一年听的了 开始分析 打开chrome控制台,点击播放,最先拿到的一个 ...

  2. QQ2004 sp1加密文件结构和加密算法分析

    原文: http://bbs.pediy.com/showthread.php?s=81db34495070105750b05461f05e5c0b&threadid=7933 测试确实可以查 ...

  3. 不混淆so文件_浅尝ollvm轻度混淆后的加密算法分析

    本文为看雪论优秀文章 看雪论坛作者ID:Avacci 该题源自看雪高研3W班9月第三题. 目标app只有一个很朴素的界面.点击"CHECK"按钮会在下方不断打印加密后的字符串.目标 ...

  4. Python爬虫进阶必备 | X中网密码加密算法分析

    0x01. 分析请求 通过输入账号密码抓包查看加密字段 可以见到基本的参数是显而易见的, <font color="red"> username </font&g ...

  5. Python爬虫进阶必备 | 极X助手加密算法分析

    极X助手 aHR0cHM6Ly93d3cuamlkYWlob21lLmNvbS9vZmZpY2VyLyMvbG9naW4= 先抓包看看这次要分析的参数.[图1-1] 图1-1 看到[图1-1]里是没有 ...

  6. 某生鲜电商mfsig加密算法分析

    初入道途 抓包分析 工具 抓包工具-charles https://www.charlesproxy.com/ 前提:手机和电脑均安装好charles证书 接口调试分析-postman 小程序包导出工 ...

  7. Auto.js Pro离线打包源代码加密算法分析

    今天在逛论坛时,发现有网友使用frida写了一个连接USB hook加解密Auto.js Project工程的工具 使用介绍是这样的: 要求:本机用pip安装frida 在目标机器上运行frida-s ...

  8. apk解密工具_手机端操作| Auto.js一键解密/一键加密工具免费分享

    ?  关注『掌玩小子』 带你『体验极客』 1 在之前的博文中,我前后分享了Auto.js相关的加密解密的文档,如<Auto.js Pro离线打包源代码加密算法分析><来 给你代码!A ...

  9. 加密芯片介绍 加密芯片选择(加密IC) 加密芯片原理

    原文链接:https://blog.csdn.net/chengjian815/article/details/107516575 前端时间有研究多款加密芯片,加密算法实现,以及破解可能,也有一些个人 ...

最新文章

  1. 【MATLAB】三维图形绘制 ( 三维平面图 | 二维网格 | meshgrid 函数 | 绘制网格 | mesh 函授 | 绘制平面 | surf 函数 | 绘制等高线 | contour 函数 )
  2. 光缆连接头符号缩写详解
  3. [渝粤教育] 西南科技大学 货币银行学 在线考试复习资料(1)
  4. 天然气表怎么看多少方_上海考大学难度怎么样?看2019上海高考“成绩分布表”和“本科分数线”就知道了!...
  5. 请不要再使用判断进行参数校验了
  6. sdcv: 在Ubuntu中使用命令行查询离线英文词典
  7. 2017级C语言大作业 - 见缝插针
  8. Linux - iptables
  9. [USACO14OPEN]Fair Photography【前缀和】
  10. 丁腈橡胶自然老化时间_氙灯老化试验箱和紫外光老化试验箱的区别
  11. 如何在ppt中生成柱状图_在PPT中怎么制作图表?PPT制作图表的方法
  12. smartbi v7 Linux,配置Smartbi
  13. 如何用计算机进行绘画,怎么用电脑画画-PS电脑手绘的5个基本步骤,轻松自学成PS手绘达人...
  14. Redis_17_Redis服务器中的数据库(五种基本类型底层存放)
  15. Springboot整合SpringSecurity 04-启用登出logout功能
  16. 文档生成工具-Doxygen使用方法以及注释规则
  17. u盘启动盘一键装系统教程
  18. 机器学习中的数学——粒子群算法(Particle Swarm Optimization, PSO)(三):改进的粒子群算法
  19. 刘未鹏 新书 《暗时间》 (全)
  20. 2023年高新技术企业认定时间

热门文章

  1. java调色板(用滑块调色)
  2. webview是什么?作用是什么?和浏览器有什么关系?
  3. 外贸网站最新的英文在线跟踪聊天咨询管理软件
  4. STM32矩阵按键扫描冲突问题
  5. 异构计算机 桌面,一种解决异构操作系统的复合桌面虚拟化架构及方法_2
  6. clover直接进windows_黑苹果CLOVER引导去除多余引导项Windows10 MacOS DeepinLinux Fyde...
  7. Tampermonkey油猴 加载 jQuery
  8. C语言将整数转换为字符串
  9. 基于FPGA的等精度频率计,频率测量范围0~25M正弦波(方波),相位测量范围0~100k
  10. php淘兴趣,Web前端开发实训案例教程(中级1+X证书制度试点培训用书)