Python批量转换网易云为直链

今天,完成了两件“大”事——

  • 把WordPress上的所有文章搬到Hexo了;(WordPress目前只为音乐服务)

  • 发现了可以取代之前java那篇的更好的网易云直链转换方法。

本篇来稍微谈一下第二件:


起因

晚饭后,我在用pot player听歌的时候,突然发现播放列表空了——

于是,像往常一样,我打开了java链接转换文件;

但是不一会,列表又空了。

我不禁感到自己的能力上的深深不足——转换的效率实在是太低了,

一首歌——>一个链接——>转换链接——>打开链接——>下载音频,

总共经过了五个步骤,最终也只得出来一首歌。

因此,我便Google了“网易云Python”;

经过

果不其然,我找到了不少网易云py的实例,

但最终发现——要不然是少了什么环境,要不然就是没法使用;

就在这时,我发现了一个能引起我共鸣的py:

它的大概思路就是在一个页面(歌单或者歌手页)request一下所有的歌曲,

(可能是吧 或者是ping?post?Python太复杂了我不了解)

获取URL和歌曲名称之后截断ID部分,并重新组成网易云官方API提供的直链格式,

然后再输出直链与歌曲名(这部分自己可以调整,artist_name为歌手参数,可以在输出的时候加上。

(所以我说有共鸣,跟前面的java有异曲同工之妙)

最后,我稍微改了一下输出的地方,本来是输出“xxx已下载”,现在的输出为“xxx”+“音乐直链”。(src)

哔哩哔哩演示:

结果

直接上源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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=6704027713'
url = url.replace('/#', '').replace('https', 'http')
out_link = 'http://music.163.com/song/media/outer/url?id='
headers = {
'User-Agent': 'Chrome/68.0.3440.106',
'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_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
title = str(s.xpath('./text()')[0])
filename = title + '\n''音频直链地址:' + src # 可以只保留src 这样输出的只有直链
filepath = folder + '/' + filename

print('正在获取第{}首歌曲名及音频直链:{}\n\n'.format(i + 1, filename))

try:
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'
}
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=2199699508' # 在此输入URL 歌单或歌手都可以
download_songs(music_list)

发现

原本作者(非我)的想法应该是(我也不是清楚就是了)获取网易云的二进制文件,加上.mp3以生成音频文件;

但是(可能ta那个时代可以)获取的文件打开发现都是网易云反爬虫返回的“网络繁忙(错误)”文件;

于是后来我去搜索了一下网易云(缓存)的加密方式——

参考:https://www.xuenixiang.com/thread-2413-1-1.html

解密的大概做法:

打开缓存目录——>找到.uc的最大的文件——>使用010Edit全选后处理为无符号 十六进制——>二进制异或“A3”

然后改后缀mp3播放即可。

(还是比较麻烦的,最终得到的也是使用爬虫一键得到的结果)

嗯,同时也发现了一个Python来解密缓存文件:

延伸:缓存解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Author: C0n5t4ntK
# Personal Blog: www.elapse.life

import os
import time

# Decrypt file using nor 0xa3 for each byte
def decrypt(cache_path, export_path, password = 0xa3):
f_read = open(cache_path, 'rb')
f_write = open(export_path, 'wb')
for obj in f_read:
for byte in obj:
new_byte = byte ^ password
f_write.write(bytes([new_byte]))
f_read.close()
f_write.close()

# Mode 1
def decrypt_file():
cache_path = input('Pls input the path of the file!\n')
export_path = input('Where do you want to export? Pls input the specific file\'s path,(including file name and extension). Or press ENTER to use the default directory.\n')
if export_path == '':
print('You didn\'t write down the path to export, the default path is D:\\\\decrypt_Export_From_Netease_Music\\decrypted.mp3.')
if not os.path.exists('D:\\decrypt_Export_From_Netease_Music'):
os.makedirs('D:\\decrypt_Export_From_Netease_Music')
export_path = 'D:\\decrypt_Export_From_Netease_Music\\decrypted.mp3'
try:
print('Wait a second pls...')
time_start = time.time()
decrypt(cache_path, export_path)
time_end = time.time()
time_used = time_end - time_start
except Exception:
print('The path is wrong!')
return
else:
print('Done! It took ' + "{:.2f}".format(time_used) + 's. Enjoy!')

# Mode 2
def decrypt_files():
cache_path = input('Pls input the path of cache directory!\n')
export_path = input('Where do you want to export? Input a directory path! Or press ENTER to use the default directory.\n')
if export_path == '':
print('You didn\'t write down the path to export, the default path is D:\\\\decrypt_Export_From_Netease_Music.')
if not os.path.exists('D:\\decrypt_Export_From_Netease_Music'):
os.makedirs('D:\\decrypt_Export_From_Netease_Music')
export_path = 'D:\\decrypt_Export_From_Netease_Music'
try:
print('Wait a second pls...')
time_start = time.time()
count = 0
for root, dirs, files in os.walk(cache_path):
for file in files:
(filename, extension) = os.path.splitext(file)
if (extension == '.uc'):
decrypt(cache_path + '\\' + file, export_path + '\\' + filename + '.mp3')
count += 1
print('Already converted ' + str(count) + ' file(s)...')
time_end = time.time()
time_used = time_end - time_start
except Exception:
print('Wrong path! Pls try again later.')
return
else:
print('Done! It took ' + "{:.2f}".format(time_used) + 's. Enjoy!')

# Mode 3
def decrypt_recent_files():
num = input('How many files do you want to convert? Input a number.\n')
try:
num = int(num)
except Exception:
print('The number is invalid...')
return
cache_path = input('Pls input the path of cache directory!\n')
export_path = input('Where do you want to export? Input a directory path! Or press ENTER to use the default directory.\n')
if export_path == '':
print('You didn\'t write down the path to export, the default path is D:\\\\decrypt_Export_From_Netease_Music.')
if not os.path.exists('D:\\decrypt_Export_From_Netease_Music'):
os.makedirs('D:\\decrypt_Export_From_Netease_Music')
export_path = 'D:\\decrypt_Export_From_Netease_Music'
try:
print('Wait a second pls...')
time_start = time.time()
count = 0
files_list = []
files = os.listdir(cache_path)
for file in files:
(filename, extension) = os.path.splitext(file)
if (extension == '.uc'):
files_list.append(file)
files_list.sort(key=lambda file: os.path.getmtime(cache_path + '\\'+ file) if not os.path.isdir(cache_path + '\\'+ file) else 0)
lens = len(files_list)
if num <= lens:
for file in files_list[-1:-(num + 1):-1]:
(filename, extension) = os.path.splitext(file)
decrypt(cache_path + '\\' + file, export_path + '\\' + filename + '.mp3')
count += 1
print('Already converted ' + str(count) + ' file(s)...')
else:
print('There\'re not enough files to convert, try to input smaller.')
return
time_end = time.time()
time_used = time_end - time_start
except Exception:
print('Sorry the path is wrong, pls try again later.')
return
else:
print('Done! It took ' + "{:.2f}".format(time_used) + 's. Enjoy!')

if __name__ == '__main__':
print('------------------------------')
print('This is a small python script(on windows) which can decrypt music files with \".uc\" extension from Netease Cloud Music\'s cache.')
print('It has three simple modes, which are: convert single file, convert all files in a directory or convert the most recently used files. You can follow the coming hints.')
print('------------------------------')
print('Attention: You can find the cache directory in the settings of Netease Cloud Music.')
print('And the default directory is C:\\\\Users\\$your_username\\AppData\\Local\\Netease\\CloudMusic\\Cache\\Cache.')
print('Type \'CTRL + C\' to stop.')
print('And sorry Netease Cloud Music didn\'t cache files by song\'s name, so I cannot name the files by the song.^_^')
print('------------------------------')
choose = input('Please pick a number to choose a mode you want to use.\n1. Single file\n2. All files in directory\n3. Most recently used file\n')
if choose == '1':
print('You chose single file mode, of course.')
decrypt_file()
elif choose == '2':
print('Here\'s the directory mode.')
decrypt_files()
elif choose == '3':
print('Seems like you want to convert recent files.')
decrypt_recent_files()
else:
print('Invalid number! Please try again later!'

参考:https://www.elapse.life/article/python-script-to-decrypt-netease-music-cache-file

打赏
  • Copyrights © 2005-2021 听话的便当
  • 访问人数: | 浏览次数:

Thanks♪(・ω・)ノ 都是微信

支付宝
微信