<span style="font-size:16px;"> 因为之前在大学养成的恶劣习惯。我习惯分享一首音乐。一月得有那么七八天要分享。或者更多,我这样从一六年干到了现在。</span>
可是我后来发现人工分享好累,后面居然开始有朋友说我推的歌不好玩。
这就有点过分了。所以决定用机械的方式,来做每日的一个定时推送。
方案A:
这是我在刚刚有这个打算时候想法:使用API拉到我的歌单列表,然后随机那首歌,取这首歌的热评,然后从热评随机拿一条作为分享的文案。而分享的目标点是尻尻空间。之所以这么做,是因为我发现如果是机械的推送,推送时的文案就要diff一点。所以有了这个的方案。:
可是这样在实际中容易发生问题:比如冷门歌曲或者新歌曲刚加入歌单之后并没有热门评论,亦或者请求api的时候返回结果为空了或者速度过慢导致推送不准时(强迫症希望每天的早上09:19分能够看到我的推送)可能需要写两个Crontable来控制这件事。
所以本着敏捷开发最基本的原则:先推出一个可以Work的版本。
所以最终方案暂定为以下流程:
- 获取用户所关联的所有歌单
- 随机随选一张歌单并选择其中的一首歌
- 获取该音乐的热门评论作为分享文案
- 推送至QQ空间
既然想清了能够Work的流程就可以开搞了
特别粗糙的实现:
鉴于之前推送基础,难点在于部署服务器端的登录分享。这里可能会用到一些模拟登录的操作。
所以接下来的篇章就是说如何登录了。
其实无论如何想要实现自动登录就要有一个键入账户和密码的过程,其次你才有可能去利用Cookie去在Cookie未过期的情况下实现登录,所以还不如做一个通过的登录。之前在做数据采集的时候正好处理过一些相关的东西:selenium。一款用于自动化测试的工具,可以模拟人为的条件去登陆。只要不是频繁操作,腾讯是不会放出一个滑块验证码来烦人的。当然,除非你是异地登录。
这样一来1.0版本就呼之欲出了:
import json import random import urllib.request import urllib.parse from selenium import webdriver import time base_url = r'http://music.163.com/api/' music_list_url = r'user/playlist/?offset=0&limit=64&uid=97752165' list_info_url = r'playlist/detail?id=' commet_url = r'v1/resource/comments/{0}/?rid={1}&\offset=0&total=fasle&limit=100' qzone_url = r'https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?' request_map = { 'site' : '网易云音乐' } head = { 'Host':'music.163.com', 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3', 'Accept-Encoding':'deflate', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests' : '1', 'Cache-Control':'max-age=0' } def http_util(url,head): res = urllib.request.urlopen(urllib.request.Request(url, None,head)).read().decode('utf-8') return res # 登录 def login_qzone(driver): print("##### start get Login Page #####") # 切换账号密码登录 time.sleep(2) driver.switch_to.frame('loginFrame') driver.find_element_by_id("switcher_plogin").click() # 等待Dom 加载 time.sleep(2) driver.find_element_by_id('u').send_keys('------') time.sleep(2) driver.find_element_by_id('p').send_keys('------') time.sleep(4) driver.find_element_by_id("login_button").click() print("#### Login Success ####") # 获取‘南宫鸢泽’关联的所有歌单 def user_base_list(): res = http_util(base_url + music_list_url,head) temp = json.loads(res) music_list = {} for i in temp['playlist']: music_list[i['name']] = (i['id']) return music_list # 随机挑选一个歌单 并选择其中的一首获取热评和歌曲信息 def music_list_hot(): id = random.choice(list(user_base_list().values())) res = http_util(base_url + list_info_url + str(id), head) temp = json.loads(res)['result'] comment_list = {} pic_list = {} artis_list = {} music_name = {} if len(temp['tracks']) > 0: for i in temp['tracks']: comment_list[i['id']] = (i['commentThreadId']) pic_list[i['id']] = (i['album']['picUrl']) music_name[i['id']] = (i['album']['name']) # 暂时只取一个歌手 artis_list[i['id']] = (i['album']['artists'][0]['name']) return comment_list,artis_list,pic_list,music_name # 随机取一首歌,并加载热评,随机选取一条 def get_end(): listKL = music_list_hot() comment_list = listKL[0] pic_list = listKL[2] artis_list = listKL[1] music_name = listKL[3] id = random.choice(list(comment_list.keys())) res = http_util(str(base_url + commet_url).format(comment_list[id],comment_list[id]) , head) temp = json.loads(res)['hotComments'] if len(temp) is 0: raise RuntimeError('#### 没有热评') return random.choice(temp)['content'],id,pic_list[id],artis_list[id],music_name[id] # -------- def ROOIKE(): # 获取分享内容 results = get_end() # 组装分享 if len(results[0]) > 120: raise RuntimeError('#### 热评的B话太多了') request_map['desc'] = results[0] request_map['url'] = "https://music.163.com/song?id=" + str(results[1]) + "&userid=97752165&from=qzone" request_map['title'] = "分享单曲:" + results[4] request_map['summary'] = results[3] request_map['pics'] = results[2] + "?imageView&thumbnail=120y120" urls = qzone_url + urllib.parse.urlencode(request_map) driver = webdriver.Chrome() driver.get(urls) # 获取登录session driver.find_element_by_id("changeAccounts").click() login_qzone(driver) # 跳出登录弹窗 driver.switch_to.default_content() print("##### start Push #####") time.sleep(5) driver.find_element_by_id("postButton").click() print("##### Success Push ####") # 遭遇异常再来一次.. while True: try: ROOIKE() break except ValueError: print("遭遇了异常和不测,再推一遍")
在这背后在加入CornTab来监督定时任务就好了。
我想有个V1.1
既然到这里了,就会发现上面的方案有很大的缺陷,比如拉去的异常再次执行时速度缓慢,频繁的请求会让网易云的API任务你就是个恶意的小鬼。所以想要稳定就要做到对象持久化。
所以有几个点可以更改:
1、预热消息体,在分享任务启动之前就做好消息组装,而不是函数强依赖。
2、加速登录流程(预热登录,利用Cookie作为登录Pass,不再依赖自动化工具的写入)
想到这里又要爆肝。不禁开始嘤嘤嘤