Python Dragonfly fm audio book batch download support account login original source code

Posted by runner on Sat, 08 Jan 2022 08:48:16 +0100

Target site audio novels - audio books - radio broadcast online listening - Dragonfly FM

The actual combat of python crawler is on the PC side. The mobile side may have a more convenient interface. You are welcome to pay attention to the discussion. Anyway, if you are practicing, just grab the package on the PC side

 


primary coverage

 

  • 1.post request login
  • 2. Simple use of hmacmd5 algorithm

 


The login of this example is very simple. Just post without any encryption. There is really no encryption and unknown parameters

Many people learn Python and don't know where to start.

Many people study python,After mastering the basic grammar, I don't know where to find cases.

Many people who have been able to learn cases do not know how to learn more advanced knowledge.

So for these three types of people, I will provide you with a good learning platform, free access to video tutorials, e-books, and the source code of the course!

QQ Group:101677771

Welcome to join us, discuss and study together

 

 

 

 


python implementation. Note that this is a method of a class. It is incomplete and cannot be run directly

 

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)
    if response.status_code==200:
        temp = response.json()
        errorno = temp['errorno']
        errormsg = temp['errormsg']
        if errorno == 0:
            print('login successful!','Login succeeded!')
            data = temp['data']
            self.qingting_id = data['qingting_id']
            self.access_token = data['access_token']
         
        else:
            print('Login failed','Login failed')
            print(errormsg)

 

After recording successfully, we put access_token and qingting_id is equivalent to a sign after login. If the account is a member, it is equivalent to a member sign
The real address of the audio requests such a url:

https://audio.qingting.fm/audiostream/redirect/294280/11604885


Where 294280 is the album id,
11604885 is the id of the current audio

 

 

It also takes some parameters, such as access_ token,qingting_ ID (in the response for successful login, all the responses not logged in in the figure above are empty), and some others, such as t, are time stamps,

 


device_id=MOBILESITE (unchanged)

 


The key is sign (if you try without sign, you will return a signature error)

 


You can try which js generated this sign through global search. I searched globally

 


device_id

 


In mian a load of. js # found the function that generates the sign (you need to distinguish it by yourself. It is a device_id: "MOBILESITE")

 


Search for other keywords should also be able to find smoothly

 

 

The sign here is the variable u, which is obtained from the variable c through a pile of encryption processing

 


We can output u and c from the console

 

 

So we know that sign actually encrypts other parameters of the request

 


At first, I mistakenly thought it was pure MD5, so I stuck it for a long time (I also went into the function to see how it was implemented)

 


In fact, the code has told you to use

 


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

 

After checking Hmac, it is found that it is a ready-made algorithm, and there are different modes. MD5 is one of them, and a secret key is required

 


I've told you everything here. The secret key of Hmac-md5 is fpmn12 & 38F_ 2e

 


Find an online encrypted website and try it. It's the same as the console output just now

 

 

import is required for python

 


hmac this library

 

import hmac
import time
 
base_url = "https://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)

 

You can get an audio. The rest is to get a pile. In fact, we can get the id of each audio

This is the interface I requested

info_api = 'https://i.qingting.fm/capi/channel/{self.bookid}/programs/{self.version}?curpage={str(page)}&pagesize=30&order=asc'


version is in the source code of the sound book home page. You can turn the page as long as you change 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 = "https://u2.qingting.fm/u2/api/v4/user/login"
        self.qingting_id = ''
        self.access_token = ''
        self.bookurl = bookurl
        # self.bookurl = 'https://www.qingting.fm/channels/257790'
        self.bookid = self.bookurl.split('/')[-1]
        self.version = ''
        self.qingtinghost = 'https://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!','Login succeeded!')
                data = temp['data']
                self.qingting_id = data['qingting_id']
                self.access_token = data['access_token']
             
            else:
                print('Login failed','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'https://i.qingting.fm/capi/channel/{self.bookid}/programs/{self.version}?curpage={str(page)}&pagesize=30&order=asc'
        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,'common{}collection'.format(total))
        for page in range(1,total_page+1):
            info_api = f'https://i.qingting.fm/capi/channel/{self.bookid}/programs/{self.version}?curpage={str(page)}&pagesize=30&order=asc'
            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('No permission to download,Please log in to the account that has purchased this audio.')
            print('errorno:',errorno,errormsg)
 
         
         
 
    def run(self):
        if self.ifLogin:
            self.login(self.user_id,self.password)
        programs =  self.get_book_info()
        count = 0
        for program in programs:
            count+=1
            try:
                id = program['id']
                title = str(count).zfill(4)+' '+program['title']+'.m4a'
                if not self.bookname =='':
                    title = os.path.join(self.bookname,title)
                whole_url =  self.get_src(id)
                self.downloadFILE(whole_url,title)
            except Exception as e:
                print(e)
                with open('log.txt','a',encoding='utf-8') as f:
                    f.write(str(count)+str(e)+'\n')
 
def get_config_info():
    with open('config.json','r',encoding='utf-8') as f:
        config = json.loads(f.read())
        return config
 
if __name__ == "__main__":
    # pyinstaller -F -i ico.ico QingTingFM.py
    config = get_config_info()
    if config["ifLogin"]:
        bookurl = input('Please enter a link to the home page where you want to download audio:(as[url=https://www.qingting.fm/channels/257790]https://www.qingting.fm/channels/257790[/url])')
        isvalid = re.search('https://www.qingting.fm/channels/\d+',bookurl)
        if isvalid:
            q  = QingTing(config["user_id"],config["password"],bookurl,1)
            q.run()
        else:
            print("The home page entered is not in the correct format")
    else:
        # Don't log in
        bookurl = input('Please enter a link to the home page where you want to download audio:(as[url=https://www.qingting.fm/channels/257790]https://www.qingting.fm/channels/257790[/url])')
        isvalid = re.search('https://www.qingting.fm/channels/\d+',bookurl)
        if isvalid:
            q  = QingTing(config["user_id"],config["password"],bookurl,0)
            q.run()
        else:
            print("The home page entered is not in the correct format")

 

Profile section

{   
    "ifLogin":1,

    "user_id":"135########",

    "password":"pwd########"
}

Topics: Python