Python 蜻蜓fm有声书批量下载 支持账号登录 原创源码

40 阅读5分钟

录成功后我们把access_token和qingting_id拿到,相当于一个登录后的标志如果账号是会员相当于一个会员标志

音频的真实地址请求了这样一个url:

audio.qingting.fm/audiostream…

其中294280是专辑id,

11604885是当前音频的id

其中还带了一些参数比如access_token,qingting_id(登录成功的response中有,上图没有登录所有是空的),另外还有一些比如t是时间戳,

device_id=MOBILESITE(不变)

关键就在于sign(尝试过不加sign会返回一个签名错误)

可以通过全局搜索试一下是哪个js生成的这个sign我全局搜索了一下

device_id

mian.一大堆.js 找到了生成sign的函数(需要自己分辨一下是一个device_id: "MOBILESITE"的)

搜索其他关键字应该也是可以顺利找到的

这里的sign是u这个变量它是由c这个变量通过一堆加密处理得到的

我们可以控制台输出一下u和c

所以我们就知道了sign实际是加密了请求的其他参数

一开始我误以为是单纯的MD5所以卡了好久(还进入函数内部看他具体是怎么实现的看的一头雾水)

其实代码已经告诉用的是

createHmac("md5", "fpMn12&38f_2e")

查了下Hmac发现就是一种现成的算法,还有不同的模式MD5是其中一种,需要一个秘钥

这里什么都告诉你了,用Hmac-md5秘钥是fpMn12&38f_2e

找个在线加密的网站试了下,果然和刚才控制台输出的一样

python的话需要import

hmac这个库

import hmac

import time

base_url = "audio.qingting.fm"

bookid = "294280"

id = "11590788"

access_token = ""

qingting_id =""

timestamp = str(round(time.time()*1000))

data = f"/audiostream/redirect/{bookid}/{id}?access_token={access_token}&device_id=MOBILESITE&qingting_id={qingting_id}&t={timestamp}"

message = data.encode('utf-8')

key = "fpMn12&38f_2e".encode('utf-8')

sign = hmac.new(key, message, digestmod='MD5').hexdigest()

whole_url = base_url+data+"&sign="+sign

print(whole_url)

得到一个音频可以做到了剩下的就是得到一堆了,其实我们得到每个音频的id就可以了

我请求的是这个接口

info_api = 'i.qingting.fm/capi/channe…'

version在声书主页的源代码中,只要改curpage就可以翻页了

完整源码

import requests

import re

import hmac

import time

from tqdm import tqdm

from bs4 import BeautifulSoup

import os

import json

import sys

import urllib3

urllib3.disable_warnings()

class QingTing():

def init(self,user_id,password,bookurl,ifLogin):

self.ifLogin = ifLogin

self.user_id = user_id

self.password = password

self.session = requests.session()

self.session.headers.update({'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'})

self.login_url = "u2.qingting.fm/u2/api/v4/u…"

self.qingting_id = ''

self.access_token = ''

self.bookurl = bookurl

self.bookurl = 'www.qingting.fm/channels/25…'

self.bookid = self.bookurl.split('/')[-1]

self.version = ''

self.qingtinghost = 'audio.qingting.fm'

self.save_path = ''

self.bookname = ''

def login(self,user_id,password):

data = {

'account_type': '5',

'device_id': 'web',

'user_id': user_id,

'password': password

}

response = self.session.post(self.login_url,data=data,verify=False)

if response.status_code==200:

temp = response.json()

errorno = temp['errorno']

errormsg = temp['errormsg']

if errorno == 0:

print('login successful!','登录成功!')

data = temp['data']

self.qingting_id = data['qingting_id']

self.access_token = data['access_token']

else:

print('Login failed','登录失败')

print(errormsg)

time.sleep(10)

sys.exit(0)

def __get_version(self):

response = self.session.get(url=self.bookurl,verify=False)

if response.status_code==200:

soup = BeautifulSoup(response.text,'lxml')

temp_bookname = soup.select('div.album-info-root > div.top > div.info.right > h1')[0].string

replaced_pattern = '[\/:*?"<>|]'

self.bookname = re.sub(replaced_pattern,' ',temp_bookname,flags=re.M +re.S)

if not os.path.exists(self.bookname):

os.makedirs(self.bookname)

matched = re.search('"version":"(\w+)"',response.text,re.S)

if matched:

version = matched.group(1)

self.version = version

return version

def __get_total_page(self):

self.__get_version()

page = 1

info_api = f'i.qingting.fm/capi/channe…'

response = self.session.get(info_api,verify=False)

if response.status_code==200:

temp = response.json()

total = temp['data']['total']

total_page = int(int(total)/30)+1

return total,total_page

def get_book_info(self):

total,total_page = self.__get_total_page()

print(self.bookname,'共{}集'.format(total))

for page in range(1,total_page+1):

info_api = f'i.qingting.fm/capi/channe…'

response = self.session.get(info_api,verify=False)

programs = response.json()['data']['programs']

for program in programs:

print(program['id'],program['title'])

yield program

def get_src(self,id):

bookid = self.bookid

access_token = self.access_token

qingting_id =self.qingting_id

timestamp = str(round(time.time()*1000))

data = f"/audiostream/redirect/{bookid}/{id}?access_token={access_token}&device_id=MOBILESITE&qingting_id={qingting_id}&t={timestamp}"

message = data.encode('utf-8')

key = "fpMn12&38f_2e".encode('utf-8')

sign = hmac.new(key, message, digestmod='MD5').hexdigest()

whole_url = self.qingtinghost+data+"&sign="+sign

return whole_url

def downloadFILE(self,url,name):

resp = self.session.get(url=url,stream=True,verify=False)

if resp.headers['Content-Type'] =='audio/mpeg':

content_size = int(int(resp.headers['Content-Length'])/1024)

with open(name, "wb") as f:

print("Pkg total size is:",content_size,'k,start...')

for data in tqdm(iterable=resp.iter_content(1024),total=content_size,unit='k',desc=name):

f.write(data)

print(name , "download finished!")

else:

errorno = resp.json()['errorno']

errormsg = resp.json()['errormsg']

print('没有权限下载,请登录已购此音频的账号。')

print('errorno:',errorno,errormsg)

def run(self):

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述

了解详情:docs.qq.com/doc/DSnl3ZG…