Skip to content

Instantly share code, notes, and snippets.

@tigerwang202
Created April 9, 2016 16:45
Show Gist options
  • Save tigerwang202/2641e442eefb3bc07ea570e493789247 to your computer and use it in GitHub Desktop.
Save tigerwang202/2641e442eefb3bc07ea570e493789247 to your computer and use it in GitHub Desktop.
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# https://gist.github.com/tigerwang202/ba16e5a31e0da3c307cc#file-m163api-py
import re
import os
import sys
import md5
import json
import random
import requests
ENCODE = 'gbk' if sys.platform.startswith('win') else 'utf8'
CMD = 'curl -# -o "{name}" "{url}"'
SEARCH = 'http://music.163.com/api/search/get/web'
DETAIL = 'http://music.163.com/api/song/detail/?ids=[{}]'
LRC = 'http://music.163.com/api/song/media?id={}'
HEADERS = {'Referer': 'http://music.163.com'}
MP3 = 'http://m{}.music.126.net/{}/{}.mp3'
class Json:
def __repr__(self):
return json.dumps(self.json, indent=2)
def json2obj(json):
if isinstance(json, dict):
obj = Json()
setattr(obj, 'json', json)
for k, v in json.items():
k = k.replace(' ', '_')
setattr(obj, k, json2obj(v))
return obj
if isinstance(json, list):
return map(json2obj, json)
return json
def search(q, tp=1):
'''
>>> search('2375').result.songs[0].id
365613
'''
return json2obj(requests.post(SEARCH, headers=HEADERS,
data=dict(type=tp, s=q, offset=0,
limit=30, total='true')
).json())
def searchAlbum(q):
return search(q, tp=10)
def searchArtist(q):
return search(q, tp=100)
def detail(sids):
'''
>>> detail([365613]).songs[0].name
u'2375'
'''
return json2obj(requests.get(DETAIL.format(','.join(map(str, sids))),
headers=HEADERS).json())
def url_2_sids(url):
'''
>>> url_2_sids('http://music.163.com/#/album?id=36197')
['365612', '365613', '365614', '365615', '365617']
'''
_id = url.split('=')[1]
if 'song?id' in url:
return [_id]
else:
r = requests.get(url.replace('/#/', '/'), headers=HEADERS)
return re.findall(r'song\?id=(\d+)', r.content)
def encrypted_id(id):
''' from https://github.com/yanunon/NeteaseCloudMusic '''
byte1 = bytearray('3go8&$8*3*3h0k(2)2')
byte2 = bytearray(id)
byte1_len = len(byte1)
for i in xrange(len(byte2)):
byte2[i] = byte2[i] ^ byte1[i % byte1_len]
m = md5.new()
m.update(byte2)
result = m.digest().encode('base64')[:-1]
result = result.replace('/', '_')
result = result.replace('+', '-')
return result
def download(sids, withlrc=False):
'''
>>> dfsId = str(detail([365613]).songs[0].bMusic.dfsId)
>>> MP3.format(1, encrypted_id(dfsId), dfsId)
'http://m1.music.126.net/n5usuzBfOxdzcGGnF_2hnQ==/2781764418296774.mp3'
'''
json = detail(sids)
for i in json.songs:
'''
name = ''
for c in i.name:
try:
name += c.encode(ENCODE)
except:
pass
artist = ''
for c in i.artists[0].name:
try:
artist += c.encode(ENCODE)
except:
pass
'''
name = i.name.encode(ENCODE)
artist = i.artists[0].name.encode(ENCODE)
# url = i.mp3Url
mp3file = artist + ' - ' + name + '.mp3'
try:
dfsId = str(i.bMusic.dfsId)
except:
# 对于已下架歌曲只下载m4a格式,质量较差
dfsId = str(i.audition.dfsId)
url = MP3.format(random.randrange(1, 3), encrypted_id(dfsId), dfsId)
print artist, name, url
cc = os.system(CMD.format(name=mp3file, url=url))
# assert not cc, 'Interrupted'
if cc:
print 'download error, skip...'
if os.path.exists(mp3file):
os.remove(mp3file)
continue
if withlrc:
lrc = requests.get(LRC.format(i.id), headers=HEADERS).json()
lrc = json2obj(lrc)
if hasattr(lrc, 'lyric'):
lrcfile = artist + ' - ' + name + '.lrc'
open(lrcfile, 'w').write(lrc.lyric.encode('utf-8'))
def main():
# print(encrypted_id('5794426278434182'))
if len(sys.argv) != 2:
print 'Usage: download_music_from_163 url'
print 'url: song or album \'s page url.'
exit(1)
else:
param = sys.argv[1]
if param.startswith('http'):
songids = url_2_sids(param)
download(songids)
elif param.endswith('.txt'):
''' batch download urls in txt file '''
with open(param, 'r') as f:
lines = f.readlines()
for url in lines:
if url.startswith('http'):
songids = url_2_sids(url)
download(songids)
f.close()
else:
print 'unknown param, quit!'
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment