一、前言
喜马拉雅FM是一个知名的音频分享平台,在移动音频行业的市场占有率已达73%,用户规模突破4.8亿,今天我们就带大家突破层层障碍,探秘喜马拉雅的天籁之音,实现实时抓捕并保存到本地。
个人觉得,听书也是对情感的一种认知和感受。看书的时候我们可以天马行空,有各种理解,但听的时候呢,听一些优质的声音和读物,可以试着从别人的声音里感知一些信息和情绪,就像我们在日常生活中一样,不仅要用眼睛看,还需要用耳朵去听呀。
二、寻找音频的URL
首先打开喜马拉雅的网址:https://www.ximalaya.com/
然后我们输入关键字 进行搜索,此处以荒村阴阳师
为例,如下图:
爬取的数据如下
你就会发现了:https://aod.cos.tx.xmcdn.com/group82/M00/8E/B8/wKg5HF8b_PXC6K1wADMmSFDB0hw087.m4a 就是下载一个MP3的链接,点击下载
访问下:https://aod.cos.tx.xmcdn.com/
j经过我的分析,这个应该Nigux代理请求,因此需要找到这个url的来源
在这里插入图片描述
三、请求参数的处理
在这里插入图片描述
没错,就是这个:https://www.ximalaya.com/revision/play/v1/audio?id=68966138&ptype=1
在请求参数中,有一个xm-sign
需要处理下
这个xm-sign
是:md5(服务器时间戳)(100以内随机数)服务器时间戳(100以内随机数)现在时间戳。
这个我看出来的,为什么呢?因为我是大神,没有为什么???
获取服务器时间戳,这个我也发现了,是https://www.ximalaya.com/revision/time
「我真的大神,你们可以叫我大师」
四、重新分析目标网站
重新分析目标网站:https://www.ximalaya.com/yinyue/12576446/ 分析每一页网页url有什么不同:第一页的是https://www.ximalaya.com/revision/play/albumalbumId=12576446&pageNum=1&sort=1&pageSize=30
第二页https://www.ximalaya.com/revision/play/albumalbumId=12576446&pageNum=2&sort=1&pageSize=30
第三页https://www.ximalaya.com/revision/play/albumalbumId=12576446&pageNum=3&sort=1&pageSize=30
一共有十七页,可以使用Python当中的.format方法来占位(方法不唯一)
五、实现代码
代码如下
代码语言:javascript复制import hashlib
import json
import os
import re
import time
import random
import requests
'''有声书下载'''
class ximalaya(object):
def __init__(self):
self.base_url = 'https://www.ximalaya.com'
self.base_api = 'https://www.ximalaya.com/revision/play/album?albumId={}&pageNum={}&sort=0&pageSize=30'
self.time_api = 'https://www.ximalaya.com/revision/time'
self.header = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:63.0) Gecko/20100101 Firefox/63.0'}
self.s = requests.session()
def get_time(self):
"""
获取服务器时间戳
:return:
"""
r = self.s.get(self.time_api, headers=self.header)
return r.text
def get_sign(self):
"""
获取sign:md5(服务器时间戳)(100以内随机数)服务器时间戳(100以内随机数)现在时间戳
:return: xm_sign
"""
nowtime = str(round(time.time() * 1000))
servertime = self.get_time()
sign = str(hashlib.md5("himalaya-{}".format(servertime).encode()).hexdigest()) "({})".format(
str(round(random.random() * 100))) servertime "({})".format(str(round(random.random() * 100))) nowtime
self.header["xm-sign"] = sign
def index_choose(self):
xm_id = input(u'请输入要获取喜马拉雅节目的ID:')
xima.get_fm(xm_id)
self.index_choose()
@staticmethod
def make_dir(xm_fm_id):
# 保存路径,请自行修改,这里是以有声书ID作为文件夹的路径
fm_path = './{}'.format(xm_fm_id)
f = os.path.exists(fm_path)
if not f:
os.makedirs(fm_path)
print('make file success')
else:
print('file already exists')
return fm_path
def get_fm(self, xm_fm_id,lable='youshengshu'):
# 根据有声书ID构造url
fm_url = self.base_url '/{}/{}'.format(lable,xm_fm_id)
r_fm_url = self.s.get(fm_url, headers=self.header)
fm_title = re.findall('<h1 class="title vA_">(.*?)</h1>', r_fm_url.text, re.S)[0]
print('书名:' fm_title)
# 新建有声书ID的文件夹
fm_path = self.make_dir(xm_fm_id)
# 取最大页数
s = re.findall(r'/{}/{}/p(d )/'.format(lable,xm_fm_id), r_fm_url.text, re.S)
max_page = sorted([int(i) for i in s])[-1]
if max_page:
for page in range(1, int(max_page) 1):
print('第' str(page) '页')
self.get_sign()
r = self.s.get(self.base_api.format(xm_fm_id, page), headers=self.header)
r_json = json.loads(r.text)
for audio in r_json['data']['tracksAudioPlay']:
audio_title = str(audio['trackName']).replace(' ', '')
audio_src = audio['src']
self.get_detail(audio_title, audio_src, fm_path)
# 每爬取1页,30个音频,休眠3秒
time.sleep(3)
else:
print(os.error)
def get_detail(self, title, src, path):
r_audio_src = self.s.get(src, headers=self.header)
m4a_path = path '/' title '.m4a'
if not os.path.exists(m4a_path):
with open(m4a_path, 'wb') as f:
f.write(r_audio_src.content)
print(title '保存完毕...')
else:
print(title 'm4a已存在')
if __name__ == '__main__':
xima = ximalaya()
xima.index_choose()
# 12576446
六、爬取结果
可能有人问这样的目的是什么?我可以在APP或者网页上的直接听啊!
诶~对于某些需要的收费的节目,量又很大,你可能会员过期了就无法享受其中的内容,所以可以通过这种方式“下载”
当然最终要的还是为了让大家理解爬虫的一些思路