爬取QQ音樂2W歌單和50W首歌曲
阿新 • • 發佈:2019-02-19
主要運用了Python中的Requests包和json包獲取內容,寫入到Mongodb資料庫並儲存,pandas用於匯出資料,程式碼詳細我最後會給出github
介面分析並爬取歌單id
我發現html原始碼並沒有我想要的資料,所以分析了Ajax請求,得到了我想要的資料。
在Chrome按F12(或Fn+F12)開啟開發者工具,在Network中選擇JS,並且重新整理頁面,找到關於歌單的渲染連線。
點選Headers獲取連結https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg,注意引數和headers資訊
必須傳入referer引數,不然你會得不到請求。它的返回內容只要提取getPlaylist() 裡面的內容就是json內容,我們可以用json包解析得到一個基於字典(dict)格式的資料塊。程式碼函式如下:
#爬取歌單id def getDissid(sin,ein): url = 'https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg' header = { 'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', 'cookie':'RK=7dNm4/X+Yj; tvfe_boss_uuid=bf00ee54e9081ab4; pgv_pvi=8772238336; pac_uid=1_857193777; pgv_pvid=6457341280; o_cookie=80; ptcz=c761e59c8c8d6bd5198866d02a5cb7313af1af468006c455d6c2b5d26201d42e; pgv_si=s10759168; _qpsvr_localtk=0.08285763449905015; ptisp=ctc; luin=o0857193777; lskey=00010000228dd1371b945c68ecfd3b71d3071425024a7a8a2a23e3ffcb5b9904c9f7088d2ea8c01539ffed92; pt2gguin=o0857193777; uin=o0857193777; skey=@Kydi7w0EI; p_uin=o0857193777; p_skey=HjsE9sEjznJfXk*9KFEeW4VZr6i3*tlXZ2nuzEw8kCg_; pt4_token=c-p6sv3JEboA51cSQ3ABqxM8O80Jct3jYYkgy-aEQuE_; p_luin=o0857193777; p_lskey=000400008f9c296cd10c03a5173d22a184aad124d791568e90e4198beb8ad699a4d02fbfc059f71ab3d8758c; ts_last=y.qq.com/portal/playlist.html; ts_refer=ui.ptlogin2.qq.com/cgi-bin/login; ts_uid=3392060960', 'referer':'https://y.qq.com/portal/playlist.html' } paramter = { 'g_tk':'1089387893', 'jsonpCallback':'getPlaylist', 'loginUin':'0', 'hostUin':'0', 'format':'jsonp', 'inCharset':'utf8', 'outCharset':'utf-8', 'notice':'0', 'platform':'yqq', 'needNewCode':'0', 'categoryId':'10000000', 'sortId':'5', 'sin':sin,#開始結點 'ein':ein #結束結點,用於翻頁 } html = requests.get(url=url,params=paramter,headers=header) res = json.loads(html.text.lstrip('getPlaylist(').rstrip(')'))['data']['list'] data = [] if res != []: for t_item in res: item = {} ILLEGAL_CHARACTERS_RE = re.compile(r'[\000-\010]|[\013-\014]|[\016-\037]') #用於去掉非法字元 item['createtime'] = t_item['createtime'] item['creator_qq'] = t_item['creator']['qq'] item['creator_name'] = t_item['creator']['name'] item['creator_name'] = ILLEGAL_CHARACTERS_RE.sub(r'', item['creator_name']) item['creator_isVip'] = t_item['creator']['isVip'] item['dissid'] = t_item['dissid']# item['dissname'] = t_item['dissname'] item['dissname'] = ILLEGAL_CHARACTERS_RE.sub(r'', item['dissname']) item['listennum'] = t_item['listennum'] data.append(item) return data
歌單介面分析並獲取歌單資訊和歌曲id,同樣的獲取方式,開啟開發者工具
程式碼如下
歌曲詳細資訊資料的獲取,用同樣的方法分析介面連結,並且傳入歌曲id,這裡就不累贅重複方法了,這一步主要目的是獲取歌詞,用於後續的文字分析。這裡要注意的是,由於歌詞原始碼是如圖所示的內容,因此我們利用正則對歌詞做了稍微的清洗。#爬取歌曲id def getSongid(dissid): url = 'https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg' header = { 'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', 'referer':'https://y.qq.com/n/yqq/playlist/{}.html'.format(dissid) } paramters = { 'type':'1', 'json':'1', 'utf8':'1', 'onlysong':'0', 'disstid':dissid, 'format':'jsonp', 'g_tk':'1089387893', 'jsonpCallback':'playlistinfoCallback', 'loginUin':'0', 'hostUin':'0', 'inCharset':'utf8', 'outCharset':'utf-8', 'notice':0, 'platform':'yqq', 'needNewCode':0 } html = requests.get(url=url,params=paramters,headers=header) cdlist = json.loads(html.text.lstrip('playlistinfoCallback(').rstrip(')'))['cdlist'] if len(cdlist)>=1: cdlist = cdlist[0] data1 = {} #儲存歌單資訊資料 data2 = [] #用於儲存歌曲部分資訊 ILLEGAL_CHARACTERS_RE = re.compile(r'[\000-\010]|[\013-\014]|[\016-\037]') data1['desc'] = ILLEGAL_CHARACTERS_RE.sub(r'', cdlist['desc']) data1['dissid'] = dissid data1['songids'] = cdlist['songids'] data1['tags'] = ','.join([i['name'] for i in cdlist['tags']]) tags = ','.join([i['name'] for i in cdlist['tags']]) for item in cdlist['songlist']: tmp = {} tmp['albumname'] = item['albumname'] tmp['songname'] = item['songname'] tmp['singer'] = ','.join([i['name'] for i in item['singer']]) tmp['tags'] = tags if item.has_key('size128'): tmp['size128'] = item['size128'] if item.has_key('songmid'): tmp['songmid'] = item['songmid'] if item.has_key('songid'): tmp['songid'] = item['songid'] data2.append(tmp) return [data1,data2]
#獲取歌詞內容
def getLyric(musicid,songmid):
url = 'https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric.fcg'
header = {
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36',
'referer':'https://y.qq.com/n/yqq/song/{}.html'.format(songmid)
}
paramters = {
'nobase64':1,
'musicid':musicid, #傳入之前獲取到的id
'callback':'jsonp1',
'g_tk':'1134533366',
'jsonpCallback':'jsonp1',
'loginUin':'0',
'hostUin':'0',
'format':'jsonp',
'inCharset':'utf8',
'outCharset':'utf-8',
'notice':'0',
'platform':'yqq',
'needNewCode':'0'
}
html = requests.get(url=url,params=paramters,headers=header)
res = json.loads(html.text.lstrip('jsonp1(').rstrip(')'))
#由於一部分歌曲是沒有上傳歌詞,因此沒有預設為空
if res.has_key('lyric'):
lyric = json.loads(html.text.lstrip('jsonp1(').rstrip(')'))['lyric']
#對歌詞內容做稍微清洗
dr1 = re.compile(r'&#\d.;',re.S)
dr2 = re.compile(r'\[\d+\]',re.S)
dd = dr1.sub(r'',lyric)
dd = dr2.sub(r'\n',dd).replace('\n\n','\n')
return dd
else:
return ""
這裡在爬取歌曲資訊資訊和歌詞的時候,因為獲取到的歌曲id就有70多W(包括重複的),建議開啟多執行緒或者多程序,我就是用pool.map開啟了4程序爬取,效率提高了不止4倍,大概2天就爬完了(當然每個人電腦配置不一樣,我的比較渣)資料樣式圖
心得:
1.獲取QQ音樂的介面連結其實不是很難,資料也是很明顯就看到了。
2.一開始明明是請求成功,就是返回空資料,後來才發現原來是headers傳少了referer引數,因為原先沒有傳入referer的習慣,忽略了。
3,.歌曲時長裡面是沒有具體資料的,只有檔案大小,不過我想應該可以根據檔案大小轉化為時長,差個公式。
4.程式碼中用到了Mongo資料庫,因為資料量有點多,不能保證一次性跑完並且中途不出差錯,就沒有用pandas.to_csv最後寫入資料。
寫的不好,多多指正,謝謝!