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

2017-06-19 13:24:17 +08:00
 denonw

上周做了个同步歌单的脚本(/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, 有什么问题大家多多指教, 乐意接受批评.

3990 次点击
所在节点    Python
16 条回复
bearqq
2017-06-19 14:22:59 +08:00
for _ in range(0,retry_times):
try:
dosomething()
break#执行成功,跳出 for
exception:
continue
else:#for 执行完毕未跳出,即错误次数超出
raise RetryException()
return
return "success"

我一般这么 retry
-。-
denonw
2017-06-19 14:27:02 +08:00
@bearqq 恩,这也是一种思路。 我用了 while, 你用的 for.
bearqq
2017-06-19 14:40:03 +08:00
@denonw 格式果然崩了。。。
之前没想过用 wrapper 来 retry,学习啦
wq2016
2017-06-19 16:21:48 +08:00
有趣~
crashX
2017-06-19 17:33:50 +08:00
感觉功能做反了。
natforum
2017-06-19 17:35:09 +08:00
很强势
HypoChen
2017-06-19 17:40:07 +08:00
感觉功能做反了+1
denonw
2017-06-19 17:43:30 +08:00
@HypoChen
@crashX
后面会补上这个反向功能~
willhunger
2017-06-19 17:44:00 +08:00
然而 QQ 音乐 web 端有添加网易云和虾米音乐的歌单的功能
infun
2017-06-19 17:46:15 +08:00
QQ 音乐 那么难用。。。
AsherG
2017-06-19 17:49:36 +08:00
感觉挺不错,关注一个
denonw
2017-06-19 17:55:18 +08:00
@willhunger 是的。。但是那个歌单我怎么都复制不出来= =. 所以只能自己搞一个了
mingyun
2017-06-19 23:52:35 +08:00
感觉功能做反了 +1
weaming
2017-06-20 00:12:53 +08:00
感觉功能做反了 +1
furch
2017-06-20 10:59:33 +08:00
感觉功能做反了 +1
Antidictator
2017-06-20 15:24:06 +08:00
网易云音乐的还没有 QQ 音乐的那么全。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/369522

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX