1、前言
最近,網易的音樂很多聽不到了,剛好也看到很多教程,跟進學習了一下,也集大全了吧,本來想優化一下的,但是發現問題還是有點複雜,最後另辟捷徑,提供了簡單的方法啊!
本文主要參考
python編寫GUI版網易雲音樂爬蟲後改寫,有興趣的可以看看文章的GUI,了解更多知識~
2、Python + 爬蟲
首先,說一下準備工作:
- Python:需要基本的python文法基礎
- requests:專業用于請求處理, requests庫學習文檔中文版
- lxml:其實可以用pythonth自帶的正規表達式庫re,但是為了更加簡單入門,用 lxml 中的 etree 進行網頁資料定位爬取。
- re:python正規表達式處理
- json:python的json處理庫
如果大家對上面的庫還比不懂,可以看看我的之前文章
《Python爬蟲實踐入門篇》然後,說一下我們現在已經知道下載下傳連結是這樣的:
http://music.163.com/song/media/outer/url?id='
id
就是歌曲的id!
是以,現在我們爬蟲主要的工作就是找到這個id,當然為了更好的儲存,也要找到這個歌名啦!
那現在就是要找到我們需要爬蟲的網站連結啦!我分析了一下,大概是下面三種:
#歌曲清單
music_list = 'https://music.163.com/#/playlist?id=2412826586'
#歌手排行榜
artist_list = 'https://music.163.com/#/artist?id=8325'
#搜尋清單
search_list = 'https://music.163.com/#/search/m/?order=hot&cat=全部&limit=435&offset=435&s=梁靜茹'
如果你已經隻是想下載下傳一首歌,比如靜茹-勇氣:
https://music.163.com/#/song?id=254485
,那你直接就用浏覽器打開
http://music.163.com/song/media/outer/url?id=254485
就可以了,沒必要爬蟲啊!
好啦!感覺重點都說完了,提取和解析就是用 lxml,不懂的就看我之前的文章啊
3、下載下傳歌詞
如果還要下載下傳歌詞,那也很簡單,通過接口,有歌曲的id就可以:
url = 'http://music.163.com/api/song/lyric?id={}&lv=-1&kv=-1&tv=-1'.format(song_id)
傳回的json資料大概長這樣:
{
sgc: true,
sfy: false,
qfy: false,
lrc:
{
version: 7,
lyric: "[00:39.070]開了窗 等待天亮\n[00:46.160]看這城市 悄悄的 熄了光\n[00:51.850]聽風的方向\n[00:55.090]這一刻 是否和我一樣\n[00:58.730]孤單的飛翔\n[01:02.300]模糊了眼眶\n[01:07.760]廣播裡 那首歌曲\n[01:14.830]重複當時 那條街那個你\n[01:20.410]相同的桌椅\n[01:23.740]不用言語 就會有默契\n[01:27.470]這份親密\n[01:30.560]那麼熟悉\n[01:33.850]在愛裡 等着你\n[01:37.480]被你疼惜 有種暖意\n[01:41.090]在夢裡 全是你\n[01:43.920]不要再遲疑 把我抱緊"
},
klyric:
{
version: 0,
lyric: null
},
tlyric:
{
version: 0,
lyric: null
},
code: 200
}
剩下的也沒有什麼好說的啦!
4、坑點與進階
表面上很簡單,但是需要注意的是,網易傳回的連結,資料是js動态加載,也就是爬蟲得到的網頁資料和浏覽器得到的dom内容和結構不一樣!
-
坑
其中,搜尋清單爬蟲回來的内容,完全得不到歌曲id!!!
-
解決
解決方法也是有的!
-
python模拟浏覽器
使用selenium+phantomjs無界面浏覽器,這兩者的結合其實就是直接操作浏覽器,可以擷取JavaScript渲染後的頁面資料。
由于是無界面浏覽器,采用此方案效率極低,如果大批量抓取不推薦。
對于異步請求并且資料在源碼中并不存在的,同時也就無法抓取到的資料。
-
搜尋的歌曲變成歌單
比如想下載下傳全部的某一歌手的全部音樂,用手機雲音樂搜尋,然後全部儲存到建立一個歌單,這樣就可以啦!
-
-
進階
如果想使用了解更多網易雲音樂js的加密解密過程,可以看看這個
Python 爬蟲如何擷取 JS 生成的 URL 和網頁内容? - 路人甲的回答 - 知乎
總結
用python,就一定要簡單,我認為複雜的東西,還是盡量少做,能取巧就取巧,是以本文沒有使用selenium+phantomjs實踐,如果想了解更多
selenium+phantomjs
内容,可以參考文末引用連結。
注:本文隻是技術交流,請不要商業用途~ 如有違反,本人一概不負責。
全部代碼
又是非常簡單的100行代碼完事!!!
GitHub:
WebCrawlerExample/163_NeteaseMusic.py at master · iHTCboy/WebCrawlerExampleimport os
import re
import json
import requests
from lxml import etree
def download_songs(url=None):
if url is None:
url = 'https://music.163.com/#/playlist?id=2384642500'
url = url.replace('/#', '').replace('https', 'http') # 對字元串進行去空格和轉協定處理
# 網易雲音樂外鍊url接口:http://music.163.com/song/media/outer/url?id=xxxx
out_link = 'http://music.163.com/song/media/outer/url?id='
# 請求頭
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
'Referer': 'https://music.163.com/',
'Host': 'music.163.com'
}
# 請求頁面的源碼
res = requests.get(url=url, headers=headers).text
tree = etree.HTML(res)
# 音樂清單
song_list = tree.xpath('//ul[@class="f-hide"]/li/a')
# 如果是歌手頁面
artist_name_tree = tree.xpath('//h2[@id="artist-name"]/text()')
artist_name = str(artist_name_tree[0]) if artist_name_tree else None
# 如果是歌單頁面:
#song_list_tree = tree.xpath('//*[@id="m-playlist"]/div[1]/div/div/div[2]/div[2]/div/div[1]/table/tbody')
song_list_name_tree = tree.xpath('//h2[contains(@class,"f-ff2")]/text()')
song_list_name = str(song_list_name_tree[0]) if song_list_name_tree else None
# 設定音樂下載下傳的檔案夾為歌手名字或歌單名
folder = './' + artist_name if artist_name else './' + song_list_name
if not os.path.exists(folder):
os.mkdir(folder)
for i, s in enumerate(song_list):
href = str(s.xpath('./@href')[0])
song_id = href.split('=')[-1]
src = out_link + song_id # 拼接擷取音樂真實的src資源值
title = str(s.xpath('./text()')[0]) # 音樂的名字
filename = title + '.mp3'
filepath = folder + '/' + filename
print('開始下載下傳第{}首音樂:{}\n'.format(i + 1, filename))
try: # 下載下傳音樂
#下載下傳歌詞
#download_lyric(title, song_id)
data = requests.get(src).content # 音樂的二進制資料
with open(filepath, 'wb') as f:
f.write(data)
except Exception as e:
print(e)
print('{}首全部歌曲已經下載下傳完畢!'.format(len(song_list)))
def download_lyric(song_name, song_id):
url = 'http://music.163.com/api/song/lyric?id={}&lv=-1&kv=-1&tv=-1'.format(song_id)
# 請求頭
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
'Referer': 'https://music.163.com/',
'Host': 'music.163.com'
# 'Origin': 'https://music.163.com'
}
# 請求頁面的源碼
res = requests.get(url=url, headers=headers).text
json_obj = json.loads(res)
lyric = json_obj['lrc']['lyric']
reg = re.compile(r'\[.*\]')
lrc_text = re.sub(reg, '', lyric).strip()
print(song_name, lrc_text)
if __name__ == '__main__':
#music_list = 'https://music.163.com/#/playlist?id=2384642500' #歌曲清單
music_list = 'https://music.163.com/#/artist?id=8325' #歌手排行榜
# music_list = 'https://music.163.com/#/search/m/?order=hot&cat=全部&limit=435&offset=435&s=梁靜茹' #搜尋清單
download_songs(music_list)
參考
- Python爬蟲實踐入門篇 | iHTCboy's blog
- Python 爬蟲擷取網易雲音樂歌手歌詞
- python爬蟲的最佳實踐(五)--selenium+PhantomJS的簡單使用
- Selenium with Python — Selenium Python Bindings 2 documentation
- ariya/phantomjs: Scriptable Headless Browser
- 如有疑問,歡迎在評論區一起讨論!
- 如有不正确的地方,歡迎指導!
注:本文首發于 iHTCboy's blog ,如若轉載,請注來源