教大家爬取喜馬拉雅全站音頻數(shù)據(jù),探秘喜馬拉雅的天籟之音

一、前言
喜馬拉雅FM是一個(gè)知名的音頻分享平臺(tái),在移動(dòng)音頻行業(yè)的市場(chǎng)占有率已達(dá)73%,用戶規(guī)模突破4.8億,今天我們就帶大家突破層層障礙,探秘喜馬拉雅的天籟之音,實(shí)現(xiàn)實(shí)時(shí)抓捕并保存到本地。
個(gè)人覺(jué)得,聽書也是對(duì)情感的一種認(rèn)知和感受。看書的時(shí)候我們可以天馬行空,有各種理解,但聽的時(shí)候呢,聽一些優(yōu)質(zhì)的聲音和讀物,可以試著從別人的聲音里感知一些信息和情緒,就像我們?cè)谌粘I钪幸粯?,不僅要用眼睛看,還需要用耳朵去聽呀。
二、尋找音頻的URL
首先打開喜馬拉雅的網(wǎng)址:https://www.ximalaya.com/
然后我們輸入關(guān)鍵字 進(jìn)行搜索,此處以荒村陰陽(yáng)師為例,如下圖:

爬取的數(shù)據(jù)如下

你就會(huì)發(fā)現(xiàn)了:https://aod.cos.tx.xmcdn.com/group82/M00/8E/B8/wKg5HF8b_PXC6K1wADMmSFDB0hw087.m4a ?就是下載一個(gè)MP3的鏈接,點(diǎn)擊下載
訪問(wèn)下:https://aod.cos.tx.xmcdn.com/
j經(jīng)過(guò)我的分析,這個(gè)應(yīng)該Nigux代理請(qǐng)求,因此需要找到這個(gè)url的來(lái)源

三、請(qǐng)求參數(shù)的處理

沒(méi)錯(cuò),就是這個(gè):https://www.ximalaya.com/revision/play/v1/audio?id=68966138&ptype=1
在請(qǐng)求參數(shù)中,有一個(gè)xm-sign需要處理下
這個(gè)xm-sign是:md5(服務(wù)器時(shí)間戳)(100以內(nèi)隨機(jī)數(shù))服務(wù)器時(shí)間戳(100以內(nèi)隨機(jī)數(shù))現(xiàn)在時(shí)間戳。
這個(gè)我看出來(lái)的,為什么呢?因?yàn)槲沂谴笊?,沒(méi)有為什么???
獲取服務(wù)器時(shí)間戳,這個(gè)我也發(fā)現(xiàn)了,是https://www.ximalaya.com/revision/time
「我真的大神,你們可以叫我大師」
四、重新分析目標(biāo)網(wǎng)站
重新分析目標(biāo)網(wǎng)站:https://www.ximalaya.com/yinyue/12576446/
分析每一頁(yè)網(wǎng)頁(yè)url有什么不同:第一頁(yè)的是https://www.ximalaya.com/revision/play/albumalbumId=12576446&pageNum=1&sort=1&pageSize=30第二頁(yè)https://www.ximalaya.com/revision/play/albumalbumId=12576446&pageNum=2&sort=1&pageSize=30第三頁(yè)https://www.ximalaya.com/revision/play/albumalbumId=12576446&pageNum=3&sort=1&pageSize=30
一共有十七頁(yè),可以使用Python當(dāng)中的.format方法來(lái)占位(方法不唯一)
五、實(shí)現(xiàn)代碼
代碼如下
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):
????????"""
????????獲取服務(wù)器時(shí)間戳
????????:return:
????????"""
????????r?=?self.s.get(self.time_api,?headers=self.header)
????????return?r.text
????def?get_sign(self):
????????"""
????????獲取sign:md5(服務(wù)器時(shí)間戳)(100以內(nèi)隨機(jī)數(shù))服務(wù)器時(shí)間戳(100以內(nèi)隨機(jī)數(shù))現(xiàn)在時(shí)間戳
????????: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'請(qǐng)輸入要獲取喜馬拉雅節(jié)目的ID:')
????????xima.get_fm(xm_id)
????????self.index_choose()
????@staticmethod
????def?make_dir(xm_fm_id):
????????#?保存路徑,請(qǐng)自行修改,這里是以有聲書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'):
????????#?根據(jù)有聲書ID構(gòu)造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('(.*?)
',?r_fm_url.text,?re.S)[0]
????????print('書名:'?+?fm_title)
????????#?新建有聲書ID的文件夾
????????fm_path?=?self.make_dir(xm_fm_id)
????????#?取最大頁(yè)數(shù)
????????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)?+?'頁(yè)')
????????????????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頁(yè),30個(gè)音頻,休眠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

六、爬取結(jié)果

可能有人問(wèn)這樣的目的是什么?我可以在APP或者網(wǎng)頁(yè)上的直接聽??!
誒~對(duì)于某些需要的收費(fèi)的節(jié)目,量又很大,你可能會(huì)員過(guò)期了就無(wú)法享受其中的內(nèi)容,所以可以通過(guò)這種方式“下載”
當(dāng)然最終要的還是為了讓大家理解爬蟲的一些思路
