selenium自动收藏网易云曲目

seleniumpythoncode

selenium 初使用。

**版本信息**

|       Python       |    3.6.0     |
| :----------------: | :----------: |
|      Selenium      |    3.4.3     |
| Chromedriver_mac64 |     2.33     |
|       Chrome       | 62.0.3202.94 |

 

废话部分

很久以来自己的朋友群中经常会分享网易云音乐的歌曲……然而对其整理并不是很轻松,自从有了Faya之后自动存取链接部分是简单实现了。

关于歌曲页面可以参考这里,存储部分代码大概这样:

def save(msg):

    url_reg = re.compile(r'(http://music.163.com[^\s]*)')
    wyyurl = re.findall(url_reg, msg)[0].replace('/#','')

    if not wyyurl:
    	return

    headers = {
        'Host': 'music.163.com',
        'Referer': 'http://music.163.com/',
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
    }

    tit_re = re.compile('<title>(.+)</title>')
    songinfo = re.findall(tit_re, requests.get(wyyurl , headers = headers).text)
    songinfo = songinfo[0].replace(' - 网易云音乐', '')
    with open('songinfo.txt', 'a+', encoding='utf-8') as lt:
        lt.write(','+songinfo)
    with open('songurl.txt', 'a+', encoding='utf-8') as songurl:
        songurl.write(','+wyyurl)

因为是之后按照时间顺序读取,图省事就直接写进了 txt,其实这样不是很好。非常 ugly

但是就算这样还是需要定时进行手工收藏,所以一直以一种类似于 osascript 的方法进行更新,代码如下:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os

with open ('songurl.txt' ,'r') as l:
    songs = l.read().split(',')
for song in songs:
    print(song)
    os.system("open -a \"Google Chrome\" %s"%song)
    input() #用于暂停打开

(当然仅限 mac 使用)

这样的好处是可以再看一遍所有链接指向的歌曲,看看最近都分享了啥。但是不及时,经常过很久才会想起来更新一次,因为 Faya 现在运行在 RaspberryPi 上,从上面同步文件也有点小烦琐。其实就是懒

正题

那么就是 selenium 的正式使用了( ̄ △  ̄) python 和 chrome 的安装并不多说,这里额外需求的是pip3 install selenium,以及去 https://sites.google.com/a/chromium.org/chromedriver/安装 chromedriver。

之后可以使用以下简单的代码测试自己是否正确配置了环境。

#!/user/bin/env python
# -*-coding:utf-8-*-


from selenium import webdriver
import time

options = webdriver.ChromeOptions()
options.add_argument('lang=zh_CN.UTF-8')
options.add_argument('user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"')

driver = webdriver.Chrome(chrome_options=options)

driver.get("https://www.google.com")

driver.find_element_by_name('q').send_keys('selenium')
driver.find_element_by_name('btnK').click()
time.sleep(20) #20秒后关闭
driver.close()

如果正常就可以看到如下 python 打开了一个浏览器,输入了selenium然后点击了搜索。

我们的目标当然是http://music.163.com

要收藏第一步当然是要登录。

因为不记得密码,这里选择的方案是使用第三方(qq)登陆后存取 cookies 用来下次登录。

前半部分和之前一样,代码如下:

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument('lang=zh_CN.UTF-8')
options.add_argument('user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"')
driver = webdriver.Chrome(chrome_options=options)

driver.get("http://music.163.com")
input() #在这时手动登录,登陆完成后按任意键继续

cookies = driver.get_cookies() #获取当前cookies

print(cookies)

with open('xxx.xx')…… #这里可以以任意自己喜欢的方式储存当前的cookies

同样因为图省事,在这里选择直接复制print(cookies)的结果写入 script 中。

其中cookies的结果大致是:

c = [{'domain': '.music.163.com',......'},
{'domain': '.music.163.com',......'},
{'domain': '.music.163.com',......'},
{'domain': '.music.163.com',......'},
{'domain': '.music.163.com',......'}]

这样list的格式,因而要

for each in cookies:
    driver.add_cookie(each)

要注意在使用 selenium 的add_cookie之前需要保证当前页面和cookies是同一个 domain,也就是说第一遍需要不带cookies访问,再添加完之后重新访问一遍。

登录完之后和所有爬虫一样就是分析 html 了,随便打开一首歌的页面,对于收藏按钮右键检查元素:

右上选中的部分就是这部分的 html 了。

内容为

<a
  data-res-id="32364761"
  data-res-type="18"
  data-count="-1"
  data-fee="0"
  data-payed="0"
  data-pl="320000"
  data-dl="320000"
  data-cp="1"
  data-toast="false"
  data-st="0"
  data-res-action="fav"
  class="u-btni u-btni-fav "
  href="javascript:;"
>
  <i>收藏</i>
</a>

仔细一看这边的内容中<a data-res-id="32364761" >表示这部分的 id 是动态生成的,并不可靠,可没有其他可以简单查找到的其他特征,css 也是复合的,只好选择使用 xpath 了。

如果选择得到结果: //*[@id="content-operation"]/a[3]看起来还行。去 selenium 试试看。

得到报错结果如下:

raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//*[@id="content-operation"]/a[3]"}

看来是没用找到元素,难道是 xpath 错了么?

经过一段时间研究……其实是 iframe 的原因。

再次查看 html,可以看到如下内容:

整个页面都在这层 iframe 之下,因而我们要做的就是切换到这个部分再进行搜索。 具体代码:
driver.switch_to.frame('contentFrame')

之后我们再运行上面的搜索也就没有问题了,可以直接使用click()方法模拟点击。

之后正常跳出选择框,检查元素,这边貌似有个xtag的 class 可以直接用来定位,要注意的是xtag后有个空格……理由不是很懂。

然而这时候很自信的写了: pop = driver.find_elements_by_class_name('xtag ')

再次得到无法定位的错误

raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"class_name","selector":"xtag "}

这是为什么呢?明明有那么好用的 class_name,明明也没有多出新的 iframe,可是怎么会变成这样呢?

 

 

 

*两个小时之后*

经过反复的研究分析,最终发现一切的错误只需要等 3 秒种。

如下图所示,窗口的元素是在点击后生成的,在点击X之后移除,因而在点击后直接查询,这时它并没有生成完整,稍微等待一下加载再进行查询就没问题了。

因而代码如下:

time.sleep(3)
pop = driver.find_elements_by_class_name('xtag ')
pop[1].click()

这里还有一个要注意的点,在使用driver.find_element_by_XXXX等方法时,要注意单复数,比如find_element_by_XXXXfind_elements_by_XXXX是不同的,后者返回的是一个 list,也就不能直接使用click()send_keys等方法。

终于……我们看到了收藏成功的提示。感动……╮( ̄ ▽  ̄)╭

当然如果这首曲子在歌单中已经有了会返回已存在的提示。

完整的工作 pipeline 如下:

#!/user/bin/env python
# -*-coding:utf-8-*-


from selenium import webdriver
import time
import os

options = webdriver.ChromeOptions()
options.add_argument('lang=zh_CN.UTF-8')
options.add_argument('user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"')
driver = webdriver.Chrome(chrome_options=options)

#未储存cookies的情况:
driver.get("http://music.163.com")
input() #在这时手动登录,登陆完成后按任意键继续

"""
已储存cookies的情况:
driver.get("http://music.163.com")
cookies = "………" (替换此处)
for each in cookies:
    driver.add_cookie(each)
"""

with open ('songurl.txt' ,'r') as l:
    songs = l.read().split(',')
    #从本地读取歌曲URL

for song in songs:
    driver.get(song)
    time.sleep(3)
    title = driver.title
    try:
        print('now ' + title)
        driver.switch_to.frame('contentFrame')
        driver.find_element_by_xpath('//*[@id="content-operation"]/a[3]').click()
        print('click ok')
        time.sleep(3)
        pop = driver.find_elements_by_class_name('xtag ')
        pop[1].click() #此处是选择下面一个歌单
        print('finished ' + title)
    except:
        print('skip ' + title)
        #遇到错误跳过

    time.sleep(2)

 

下一步就是优化和搬运到 RaspberryPi 上了,可以继续参考这篇文章

最后是收集生成的歌单:http://music.163.com/#/m/playlist?id=754282245

♪♪ 有兴趣可以去听听看,都是良曲的说 ♪♪