Python爬蟲實踐 網易雲音樂

一、前言

最近,網易的音樂不少聽不到了,恰好也看到不少教程,跟進學習了一下,也集大全了吧,原本想優化一下的,可是發現問題仍是有點複雜,最後另闢捷徑,提供了簡單的方法啊!html

本文主要參考 python編寫GUI版網易雲音樂爬蟲 後改寫,有興趣的能夠看看文章的GUI,瞭解更多知識~python

二、Python + 爬蟲

首先,說一下準備工做:git

  • Python:須要基本的python語法基礎
  • requests:專業用於請求處理,requests庫學習文檔中文版
  • lxml:其實能夠用pythonth自帶的正則表達式庫re,可是爲了更加簡單入門,用 lxml 中的 etree 進行網頁數據定位爬取。
  • re:python正則表達式處理
  • json:python的json處理庫

若是你們對上面的庫還比不懂,能夠看看個人以前文章 《Python爬蟲實踐入門篇》github

而後,說一下咱們如今已經知道下載連接是這樣的:正則表達式

http://music.163.com/song/media/outer/url?id='
複製代碼

id 就是歌曲的id!json

因此,如今咱們爬蟲主要的工做就是找到這個id,固然爲了更好的保存,也要找到這個歌名啦!api

那如今就是要找到咱們須要爬蟲的網站連接啦!我分析了一下,大概是下面三種:瀏覽器

#歌曲清單
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 就能夠了,不必爬蟲啊!bash

好啦!感受重點都說完了,提取和解析就是用 lxml,不懂的就看我以前的文章啊 《Python爬蟲實踐入門篇》python爬蟲

三、下載歌詞

若是還要下載歌詞,那也很簡單,經過接口,有歌曲的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
}
複製代碼

剩下的也沒有什麼好說的啦!

四、坑點與進階

表面上很簡單,可是須要注意的是,網易返回的連接,數據是js動態加載,也就是爬蟲獲得的網頁數據和瀏覽器獲得的dom內容和結構不同!

  • 坑 其中,搜索列表爬蟲回來的內容,徹底得不到歌曲id!!!

  • 解決 解決方法也是有的!

    • python模擬瀏覽器 使用selenium+phantomjs無界面瀏覽器,這二者的結合其實就是直接操做瀏覽器,能夠獲取JavaScript渲染後的頁面數據。

    缺點:

    因爲是無界面瀏覽器,採用此方案效率極低,若是大批量抓取不推薦。

對於異步請求而且數據在源碼中並不存在的,同時也就沒法抓取到的數據。

- 搜索的歌曲變成歌單
 好比想下載所有的某一歌手的所有音樂,用手機雲音樂搜索,而後所有保存到新建一個歌單,這樣就能夠啦!
複製代碼

總結

用python,就必定要簡單,我認爲複雜的東西,仍是儘可能少作,能取巧就取巧,因此本文沒有使用selenium+phantomjs實踐,若是想了解更多selenium+phantomjs內容,能夠參考文末引用連接。

注:本文只是技術交流,請不要商業用途~ 若有違反,本人一律不負責。

所有代碼

又是很是簡單的100行代碼完事!!!

GitHub: WebCrawlerExample/163_NeteaseMusic.py at master · iHTCboy/WebCrawlerExample

import 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)

複製代碼

參考


  • 若有疑問,歡迎在評論區一塊兒討論!
  • 若有不正確的地方,歡迎指導!
> 注:本文首發於 [iHTCboy's blog](https://iHTCboy.com),如若轉載,請注來源
相關文章
相關標籤/搜索