Python 脚本分享 - 如何同步网易云音乐歌单到 QQ 音乐

上周做了个同步歌单的脚本(/t/369133), 在这里分享下写脚本的一些经验以及遇到的一些坑。 项目地址: https://github.com/Denon/syncPlaylist

爬取流程

整个爬虫的流程大致如下

  1. 爬取网易云音乐的歌单列表
  2. 登录 qq 音乐
  3. 搜索步骤 1 中找到的歌曲
  4. 添加到 qq 歌单中

爬取网易云音乐的歌单列表

这个步骤比较简单, 正常访问该歌单的手机端页面爬取就好, 观察页面可以发现所有歌曲都在<span class="detail">...</span> 里面. 那么直接用 requests + beautifulsoup 获取下就好.

登录 qq 音乐

其实一开始的思路是想通过发送模拟请求来登录的. 但看来看去没有找到怎么做(有人知道的话谢谢分享下). 考虑到后面比较复杂的操作, 最后就直接用 selenium 来做了. 用 selenium 来做就比较简单了. 唯一要注意的是, 登录 qq 的那个弹出框是在一个 iframe 里面.

def login_qq():
    # 切换 iframe
    browser.switch_to.frame("frame_tips")
    wait.until(lambda browse: browser.find_element_by_id("switcher_plogin"))
    sleep(0.5)
    browser.find_element_by_id("switcher_plogin").click()
    user_input = browser.find_element_by_id("u")
    user_input.send_keys("account")
    pwd_input = browser.find_element_by_id("p")
    pwd_input.send_keys("password")
    submit = browser.find_element_by_id("login_button")
    submit.click()
    # 登录成功以后要切换回来
    browser.switch_to.default_content()

搜索歌曲以及添加到歌单中

这里找到 qq 音乐的搜索 url 然后把关键字填入就好. 搜索到歌曲以后, 我这里比较偷懒, 只选择把第一个搜索到的结果添加进去. 添加的操作实际上分为三步:

  1. 鼠标移到歌曲上
  2. 点击 '+' 图标
  3. 点击歌单 这里我测试了好几种方法. 最后发现还是直接使用 javascript 来操作成功率比较高. 另外一方面, 可以直接在浏览器 console 里面直接测试 js 操作, 测试比较方便
def add_song():
    # 点击出歌单
    browser.execute_script("document.getElementsByClassName('songlist__list')[0].firstElementChild.getElementsByClassName('list_menu__add')[0].click()")
    sleep(0.5)
    # 通过 data-dirid 来选择歌单
    browser.find_element_by_css_selector("a[data-dirid='{}']".format(playlist_id)).click()
    return

打包 exe 执行文件

选择使用 py2exe 来打包. 这里有个坑就是由于我们用到了 selenium, selenium 里面的某些函数依赖了两个 js 文件, 需要把这两个 js 文件添加到打包的脚本里面

from distutils.core import setup
import py2exe
from glob import glob

setup( console=[“run.py”], data_files=[ (r’.’, glob(r’D:\myproject\syncPlaylist\config.json’)), (r’.’, glob(r’D:\ProgramData\Anaconda3\envs\python27\Lib\site-packages\selenium\webdriver\remote\getAttribute.js’)), (r’.’, glob(r’D:\ProgramData\Anaconda3\envs\python27\Lib\site-packages\selenium\webdriver\remote\isDisplayed.js’)) ] )

关于重试

在执行脚本过程中发现, 偶尔会出现点击登录以后 qq 登录还是没成功的情况, 以及添加歌曲时, 脚本偶尔会出错. 这里为了不中断整个脚本执行, 有必要加上重试这个操作, 因此写了一个重试的装饰器

def retry(retry_times=0, exc_class=Exception, notice_message=None):
    """retry_times: 重试次数
    exc_class: 捕捉的异常 class
    notice_message: 发生异常时候输出的错误信息, 为 None 时则不输出
    """
    def wrapper(f):
        @functools.wraps(f)
        def inner_wrapper(*args, **kwargs):
            current = 0
            while True:
                try:
                    return f(*args, **kwargs)
                except exc_class as e:
                    if current >= retry_times:
                        raise RetryException()
                    if notice_message:
                        print notice_message
                    current += 1
        return inner_wrapper
    return wrapper

总结以及剩余的问题

说实话, 本来以为写这个脚本难度不是很大. 但前前后后差不多花了两三天的时间 T_T. 问题在于之前爬虫这方面不是很熟悉以及项目结构在一开始比较混乱(其实就是懒= =). 平时也比较少写这种技术分享的 blog, 有什么问题大家多多指教, 乐意接受批评.


Python 脚本分享 - 如何同步网易云音乐歌单到 QQ 音乐

17 回复

for _ in range(0,retry_times):
try:
dosomething()
break#执行成功,跳出 for
exception:
continue
else:#for 执行完毕未跳出,即错误次数超出
raise RetryException()
return
return "success"

我一般这么 retry
-。-


我无法理解你的问题

恩,这也是一种思路。 我用了 while, 你用的 for.

格式果然崩了。。。
之前没想过用 wrapper 来 retry,学习啦

感觉功能做反了。

很强势

感觉功能做反了+1



后面会补上这个反向功能~

然而 QQ 音乐 web 端有添加网易云和虾米音乐的歌单的功能

QQ 音乐 那么难用。。。

感觉挺不错,关注一个

是的。。但是那个歌单我怎么都复制不出来= =. 所以只能自己搞一个了

感觉功能做反了 +1

感觉功能做反了 +1

感觉功能做反了 +1

网易云音乐的还没有 QQ 音乐的那么全。

回到顶部