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),如若转载,请注来源
相关文章
相关标签/搜索