为了偷懒我决定写一个定时推送... - KelovpString

/ 0评 / 1
<span style="font-size:16px;">&nbsp; &nbsp; 因为之前在大学养成的恶劣习惯。我习惯分享一首音乐。一月得有那么七八天要分享。或者更多,我这样从一六年干到了现在。</span> 

    可是我后来发现人工分享好累,后面居然开始有朋友说我推的歌不好玩。

     

    这就有点过分了。所以决定用机械的方式,来做每日的一个定时推送。

方案A:

    这是我在刚刚有这个打算时候想法:使用API拉到我的歌单列表,然后随机那首歌,取这首歌的热评,然后从热评随机拿一条作为分享的文案。而分享的目标点是尻尻空间。之所以这么做,是因为我发现如果是机械的推送,推送时的文案就要diff一点。所以有了这个的方案。:

    可是这样在实际中容易发生问题:比如冷门歌曲或者新歌曲刚加入歌单之后并没有热门评论,亦或者请求api的时候返回结果为空了或者速度过慢导致推送不准时(强迫症希望每天的早上09:19分能够看到我的推送)可能需要写两个Crontable来控制这件事。

    所以本着敏捷开发最基本的原则:先推出一个可以Work的版本。

    所以最终方案暂定为以下流程:

  1. 获取用户所关联的所有歌单
  2. 随机随选一张歌单并选择其中的一首歌
  3. 获取该音乐的热门评论作为分享文案
  4. 推送至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,不再依赖自动化工具的写入)

    想到这里又要爆肝。不禁开始嘤嘤嘤

    

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注