自建Navidrome音乐服务器 + selenium爬虫音乐获取流程
不想每个月花钱买QQ音乐/网易云音乐会员,偶尔听听歌,发现没会员整个人心情都不好了;于是准备:
1.自己搭建navidrome服务,支持在windows/手机端用音流Stream music的app 愉快听歌
2.基于python的selenium半自动化远程下载音乐至服务器:想听什么歌;都能较为方便的添加到服务器中,不需要手动下载音乐,然后传输等等;
接下来先看看成品效果:
以及我通过命令行,输入百度云的音乐共享链接,自动化下载到服务器的效果:
基本功能是可以保证的 --- 虽然会有些error,无伤大雅hhh
整体流程包含如下操作:
1.在服务器端部署Navidrome:
建议是通过docker部署,这部分操作不做赘述,各个平台的docker部署方法大同小异;如果不清楚如何部署docker,可以看我的一篇文章 哪吒监控部署 流媒体解锁:Grafana + Prometheus 中如何使用docker安装grafana的
2.将Navidrome的端口通过FRP内网穿透到外部
例如我穿透到music.overstic.top这个链接,就可以在外面也能听音乐了;这部分操作可以看我另一篇文章 FRP 内网穿透,支持通过域名访问内部服务器资源
3.安装Aria2下载软件
安装aria2的目的是,这个可以通过网页输入下载链接,然后直接下载到本地服务器;而且他有一个RPC下载,可以直接在百度云网页版中讲需要下载的文件直接推送到aria2,非常方便;Aria2的安装操作请看这篇文章 Aria2 下载器网页版配置流程 -- Windows版本
4.在Google浏览器上安装 篡改猴 Beta版,找到百度云直链的脚本
通过篡改猴的脚本,可以将百度云保存的文件获取直链,方便Aria2直接下载;同时脚本支持直接批量将直链通过RPC发送给我的Aria2后自动下载。
首先进入篡改猴Beta,如果链接失效直接在google搜索 篡改猴 Beta,找到chrome应用商店里面软件,然后安装;
安装完之后,右上角应该会出现两个图标:
其中红色的是Beta版本,百度云直链的脚本需要在Beta脚本上运行;黑色的是普通版本,也可以运行别的脚本,可以自行探索;
接下来在 Greasy Fork 油叉 上搜索百度云直链,找到可以使用的直链脚本,建议选择安装人数多的。我现在使用的是
网盘直链下载助手(自动显示暗号),当前用着还行,后面就不一定了;
下载好后,可以进入百度云盘的网页版,先看右上角的篡改猴是否有个数字1,如果有则表示这个脚本可以生效了;然后在云盘页面上找是否多了一个下载助手的按钮,有的话则说明可以使用
我们主要会用到里面的RPC下载或者Aria下载;其中RPC下载只要在助手设置中配置好aria2下载页面的参数,就可以自动推送;Aria下载需要自己拷贝连接进去,稍微麻烦一些;
对于RPC下载,按照如下配置:
其中主机则是你aria2 web服务在公网的地址;
PRC端口是aria2 server的端口;
RPC密钥同理
保存路径是服务器上音乐的保存路径
配置好后,如果你想批量传音乐,勾选音乐 -> 点击下载助手 -> 选择RPC下载 -> 在弹出的页面上选择发送全部连接即可;形如:
5.在Windows上部署selenium脚本:
包含两个脚本,一个是cookie获取脚本:
import time
import re
import random
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import *
from selenium.webdriver.chrome.service import Service
import pandas as pd
import copy
import re
import os
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
service = Service("D:\chromedriver-win64\chromedriver.exe")
driver = webdriver.Chrome(service=service)
driver.get("https://pan.baidu.com/")
Input("等待登陆,登陆后回车")
cookies = driver.get_cookies()
with open("baidu_cookies.json", "w") as f:
json.dump(cookies, f)
print('Cookie 保存成功')
print('地址:')
print(os.path.join(os.getcwd(),"baidu_cookies.json"))
运行这个脚本,在弹出的页面正常登录账号后,即可将cookie记录;
第二个是百度云音乐保存脚本:
import time
import re
import random
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import *
from selenium.webdriver.chrome.service import Service
import pandas as pd
import copy
import os
import json
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
Timewait = 5
xpath_main_loginbutton = '//*[@id="layoutHeader"]/div/div/dl/dd[2]/a[1]'
xpath_login_userloginbutton = '//*[@id="TANGRAM__PSP_11__changePwdCodeItem"]'
xpath_login_autologinbutton = '//*[@id="TANGRAM__PSP_11__clientOnekeySumbit"]'
xpath_login_errorinfo = '//*[@id="TANGRAM__PSP_11__clientShareError"]'
xpath_login_user = '//*[@id="TANGRAM__PSP_11__userName"]'
xpath_login_pwd = '//*[@id="TANGRAM__PSP_11__password"]'
xpath_login_submit = '//*[@id="TANGRAM__PSP_11__submit"]'
xpath_login_agree = '//*[@id="TANGRAM__PSP_11__isAgree"]'
xpath_save_sure = '//*[@id="fileTreeDialog"]/div[3]/a[1]/span/span'
def check_login():
#判断是否登陆
try:
element = driver.find_element('xpath',xpath_main_loginbutton)
if element.text == "登录":
return False
elif element.text == "客户端下载":
return True
else:
return True
except Exception as e:
print('未找到"登陆"选项,请检查: {e}')
return False
def login():
element = driver.find_element('xpath',xpath_main_loginbutton)
print("点击登陆按钮")
element.click() #点击登陆
time.sleep(Timewait)
print("点击账号登陆")
element_loginmethod = driver.find_element('xpath',xpath_login_userloginbutton)
element_loginmethod.click() #点击登陆
time.sleep(Timewait)
print("尝试能否直接登陆")
#判断能否直接登陆
element_login = driver.find_element('xpath',xpath_login_autologinbutton)
if element_login.text == "账号登录" or element_login.text == "直接登录":
element_login.click()
print("可以快捷登陆,尝试直接登陆")
time.sleep(Timewait)
element_grant_error = check_elements('授权已过期')
if element_grant_error:
print('授权已过期,重新输入账号密码')
loginwithPWD()
else:
print("快捷登陆成功")
else:
print("无法快捷登陆,重新输入账号密码")
loginwithPWD()
def loginwithCookie():
try:
with open(r"D:\Shares\Disk1\代码\baidu_cookies.json", "r") as f:
cookies = json.load(f)
for cookie in cookies:
driver.add_cookie(cookie)
driver.refresh()
time.sleep(Timewait)
except FileNotFoundError:
print("Cookies 文件不存在,请检查路径")
def loginwithPWD(usr_name = 'XXX',pwd = 'XXX'):
#不能直接登陆
print("不能直接登陆,输入账号密码")
element_zhanghaologin = driver.find_element('xpath',xpath_login_userloginbutton)
element_zhanghaologin.click()
time.sleep(Timewait)
print("点击账号登陆")
element = driver.find_element('xpath','//*[@id="TANGRAM__PSP_11__clientOnekeyChangeLogin"]')
element.click()
time.sleep(Timewait)
element_usrname = driver.find_element('xpath',xpath_login_user)
element_usrname.clear()
element_usrname.send_keys(usr_name)
time.sleep(Timewait)
element_pwd = driver.find_element('xpath',xpath_login_pwd)
element_pwd.clear()
element_pwd.send_keys(pwd)
time.sleep(Timewait)
element_submit = driver.find_element('xpath',xpath_login_submit)
while not element_submit.is_enabled():
element_accept = driver.find_element('xpath',xpath_login_agree)
element_accept.click()
time.sleep(Timewait)
element_submit.click()
time.sleep(Timewait)
Input("确认是否正常登陆,若没有,手动登陆")
def check_elements(Str):
Elements = driver.find_elements('xpath', "//*[contains(text(), '%s')]"%Str)
Elements = [i for i in Elements if i.text == Str]
if Elements:
element = Elements[0]
return element
def find_music():
elements_best = []
Music_name = []
sequence = ['flac','mp3']
for quality in sequence:
print("查找%s格式"%quality)
elements_cur = driver.find_elements('xpath', "//*[contains(text(), '%s')]/parent::div"%quality)
for i in elements_cur:
if i.text and (i.text.split('.' + quality)[0] not in Music_name) and (not i.text.split('.' + quality)[1]):
#判断条件:有对应格式名,有字符串,格式名前面是名字,格式名后面是空值
Music_name.append(i.text.split('.' + quality)[0])
elements_best.append(i)
print(i.text)
return Music_name,elements_best
def save_music(elements_best):
if not elements_best:
print("当前页面无目标歌曲,可能为文件夹,直接保存")
else:
for element in elements_best:
element.click()
time.sleep(1)
print('保存到网盘')
element_save = check_elements('保存到网盘')
if element_save:
element_save.click()
time.sleep(Timewait)
else:
print('保存失败')
return
print('选择音乐文件夹')
element_music = check_elements('音乐')
if element_music:
element_music.click()
time.sleep(Timewait)
else:
print('选择失败')
return
element_sure = check_elements('确定')
if element_sure:
element_sure.click()
time.sleep(Timewait)
else:
print('选择确认按钮失败')
return
time.sleep(3)
element_success = check_elements('保存成功')
if element_success:
print('保存成功')
else:
print('保存失败')
return
# def iterFind(): #文件夹迭代 没写完,如果多个文件夹不好退出
# Music_name,elements_best = find_music()
# save_music(elements_best)
# FileElements =
def check_ready():
status = WebDriverWait(driver, 10).until(
lambda driver: driver.execute_script("return document.readyState") == "complete"
)
return status
def main(path,code):
# path = 'https://pan.baidu.com/s/1EFPt2AqkOFCrY_AdEYcmdQ'
# code = 'ad89'
url = path + '?pwd=' + code
driver.get(url)
s = check_ready()
if not s:
raise Exception("页面未加载")
try:
element_ex = check_elements('提取文件')
element_ex.click()
time.sleep(Timewait)
except:
pass
check_ready()
if not s:
raise Exception("页面未加载")
time.sleep(Timewait)
print("检查是否登陆")
if not check_login():
print("未登陆,尝试Cookie登陆")
loginwithCookie()
time.sleep(Timewait)
if not check_login():
print("Cookie登陆失败,尝试自动登陆")
loginwithCookie()
if not check_login():
raise Exception("自动登陆失败,请手动尝试")
print("已登陆")
运行这个脚本,如果是远程运行,在选择无头模式时输入yes/Y/y,本地运行可以输入n,则会跳出一个自动化网页,能够看到他是怎么自动化操作的;
首先输入百度云共享的连接,然后输入百度云共享的密码;接着就会自动将音乐文件保存到百度云的音乐文件夹中
6.获取 Hifini 账号
如果想用5中的脚本,建议使用Hifni里面的资源;每个资源都是由百度云共享的,包含链接和密码;