网易云音乐爬虫

0

文明、富强、民主。。。

分析网易云音乐html DOM结构

想要爬取网易云歌单中的音乐,首先要知道我们需要拿到些什么,歌单id歌曲id接口地址这三个是必须的。接下来是具体的获取这些数据的操作流程:

  1. 打开网易云音乐首页,打开network。
  2. 随便点击一个歌单,找到https://music.163.com/playlist?id=请求。如图:

    api

  3. 拿到歌单id,即这个请求id=后面的数字

  4. 点开这个请求,在Preview查看请求返回的html源码,找到歌单中歌曲的名字,复制一个歌曲名。

    Preview

  5. 再在Response中,使用快捷键command+F查找(windows为control+F),粘贴刚才复制的歌曲名字,回车。

    Response

  6. 可以看到,歌曲名在一个ul>li>a标签中,并且在a标签的href中还带有这首歌的ID,完美。

  7. 在音乐页面,点击一首歌播放,查看获取歌曲播放地址的接口。很明显看到这个接口返回了歌曲的地址。

    songUrl

  8. 接下来就是来解析一下这个接口传递的参数了,这是最麻烦的一步了,有想了解的可以去自行百度了解一下,知乎中的大佬已经有人破解出来了。我这里就直接用我已经获得到的网易云Api了。

    # 获取播放地址(get请求): 
    http://music.163.com/song/media/outer/url?id=561042302.mp3
    

编写python爬虫

废话不多说,直接贴出源码,各个位置标注都已经做好

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import re
import urllib.request
import urllib.error
import urllib.parse
import json
import threading
from time import sleep
import os

json_path = 'data.json'

def get_all_hotSong():     #获取歌单所有歌曲名称和id
    # url='http://music.163.com/discover/toplist?id=3778678'    #网易云云音乐热歌榜url
    url='https://music.163.com/playlist?id=3778678'           #播放列表url z/mpi 2476005642 like 536254974
    header={    #请求头部
        'User-Agent':'Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
    }
    request=urllib.request.Request(url=url, headers=header)
    html=urllib.request.urlopen(request).read().decode('utf8')   #打开url
    html=str(html)     #转换成str
    pat1=r'<ul class="f-hide"><li><a href="/song\?id=\d*?">.*</a></li></ul>'  #进行第一次筛选的正则表达式
    result=re.compile(pat1).findall(html)     #用正则表达式进行筛选
    result=result[0]     #获取tuple的第一个元素

    pat2=r'<li><a href="/song\?id=\d*?">(.*?)</a></li>' #进行歌名筛选的正则表达式
    pat3=r'<li><a href="/song\?id=(\d*?)">.*?</a></li>'  #进行歌ID筛选的正则表达式
    hot_song_name=re.compile(pat2).findall(result)    #获取所有歌单的歌曲名称
    hot_song_id=re.compile(pat3).findall(result)    #获取所有歌单歌曲对应的Id

    return hot_song_name,hot_song_id

def download_song(hot_song_name,hot_song_id):
    singer_url = 'http://music.163.com/song/media/outer/url?id={}.mp3'.format(hot_song_id)
    res_path = '/Users/admin/Documents/songs/{}.mp3'.format(hot_song_name)
    if (os.path.exists(res_path)):
        print('{}---已存在!'.format(hot_song_name))
        return
    print('歌曲:{}'.format(hot_song_name),'ID:{}'.format(hot_song_id),'正在下载...')
    urllib.request.urlretrieve(singer_url,f_path) # 保存到本地目录    
    print('{}---下载完成'.format(hot_song_name))
    song_item = {'name':hot_song_name,'id':hot_song_id,'url':'http://music.163.com/song/media/outer/url?id={}.mp3'.format(hot_song_id)}
    songs.append(song_item)
    sleep(1)

hot_song_name,hot_song_id=get_all_hotSong()  #获取歌单所有歌曲名称和id
songs=[]
threads = []
files = range(len(hot_song_name))
num=0

while num < len(hot_song_name):    #下载歌单中的所有歌
    t = threading.Thread(target=download_song,args=(hot_song_name[num],hot_song_id[num]))
    threads.append(t)
    num+=1

runFiles = [];
for i in files:
    runFiles.append(i)
    if(len(runFiles)>=10):
        for i in runFiles:
            threads[i].start() 
        for i in runFiles:
            threads[i].join()
        runFiles=[]

if(len(runFiles)!=0):
    for i in runFiles:
        threads[i].start() 
    for i in runFiles:
        threads[i].join()

# 格式化数据,保存在json文件中
with open(json_path,'w') as dump_f:
    json.dump(songs,dump_f)

print('执行完毕')

踩坑

爬取的时候都很正常,但是当我打开data.json的时候我就惊呆了,中文居然全都变成了unicode码。在我耗费了一天一夜查找了各种资料之后,终于知道了原因,python在写入本地文件的时候,默认转码为ascII编码,所以,将下面这句话改成这样就OK了。

# 格式化数据,保存在json文件中
with open(json_path,'w') as dump_f:
#    json.dump(songs,dump_f)
+    json.dump(songs,dump_f,ensure_ascii=False)

源码地址:这里,觉得还不错的话记得给个star哦,