整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

B站每日自动签到&传统单节点网站的 Serverless 上云

么是函数?刚刚考完数学没多久的我,脑力里立马想到的是自变量、因变量、函数值,也就是y=f(x)。当然,在计算机里,函数function往往指的是一段被定义好的代码程序,我们可以通过传参调用这个定义好的函数,实现我们所需要的功能。那么,今天的函数计算FC又是什么?

云计算时代的当下,容器化技术与各种工具发展的DevOps,已经把开发与运维的工作进行了新的统筹,开发人员在完成代码的编写后,无需考虑环境,直接提交到各种流水线就可以完成测试、开发、部署,项目构建微服务,由容器完成环境的封装。但是往往我们最终还是需要投入精力到业务上线的集群,是私有云环境还是公有云?是裸金属服务器还是云实例ECS?是自购还是租用?

当然,DevOps的落地,服务器\集群的运维,这些都是需要投入大量的资源与精力,DevOps是一条捷径,但不是唯一的出路。因此函数计算FC的出现,带来了无服务Serverless的架构,让开发者在开发和部署的时候,不在有部署服务复杂的感觉,对服务器的无感化,可以使开发者真正的关注在自己的代码上。阿里云Serverless函数式极简编程可专注于业务创新,无采购和部署成本、提供监控报警等完备的可观测能力。函数计算是事件驱动的全托管计算服务,真正的无需去考虑服务器的运维管理,只需要完成开发的代码进行上传,函数计算会通过角色策略去规划计算资源,弹性的方式执行函数,最后高效的执行部署。优雅!

Serverless 将会有那些适用场景?是个人?还是生产?那么这次我将部署两种不同方向的应用对Serverless进行测评

一、通知系统与Webhook,Trigger触发与Chat机器人。许多系统中涉及到的push类功能,例如邮件、短信、Webhook。当然Webhook的能力不只是信息通知,不过这里所指的通知功能必然是需要基础设施也就是服务器来支撑运行,如果将这些功能直接由Serverless来操作,我们便无需支付运维一台服务器,节省了大量的工作与费用。同样,我们可以利用Serverless事件驱动模型实现定时自动触发任务,自动签到自动发送。

二、其次当然是Web类的应用。基于各类Web框架的应用部署,构建基于Java、Python、PHP等语言的站点,Serverless很容易实现如wordpress这样的博客应用的上线。配合其他云产品,Codeup、OSS、RDS等,更能实现高可用高性能的Web应用,如官方提供的Kod云盘系统。

一、使用Serverless实现B站每日登陆签到

作为一个老b站用户,b站等级无疑是妥妥的"名片",当然我早已是六级大佬的一员了。b站升级所需的经验值是关键,登陆、投币、观看都会积累经验。为了可以快速升级,这次我将使用阿里云Serverless,实现每日b站的登陆经验Get,观看视频经验Get,观看直播银瓜子Get(白嫖的直播送礼道具),并且配合钉钉机器人,实现Webhook的消息推送。

1.创建Serverless服务及函数

进入函数计算FC控制台,选择【服务及函数】,点击【创建服务】

在【创建服务】的页面中,输入服务名称,并选择启用日志功能,日志更能可以帮助我们更好的排查错误

这里我当然需要标注服务的功能,即实现bilibili的日常登陆签到。

进入【创建函数】页面,选择【使用标准Runtime从零创建】

配置函数名称,选择运行环境为Python3,并且选择从文件夹上传代码。

注意,这里如果有依赖包需要提前下载到代码包下,我这里需要用到requests包 ,在本地需要执行 pip install -t . <模块名称>。不过后续也可以在控制台处执行下载命令

这里的Python功能实现的脚本是定时触发类的,因此我们选择请求处理程序类型为【处理事件请求】

下方配置触发器,选择定时触发器,输入名称,选择【指定时间】,我这里选择的是每日的23点进行脚本的运行

Python脚本内容

# -*- coding: utf8 -*-
import requests 
import json
import time
import re
import sys
import codecs
from bs4 import BeautifulSoup
from json.decoder import JSONDecodeError


# B站登陆Cookie
cookie = ""
# Webhook地址
webhook = "https://oapi.dingtalk.com/robot/send?access_token=xxxx"
# 自动观看的BV号,杰伦新专-最伟大的作品
bid = 'BV1ua411p7iA' 

uid=re.match('(?<=DedeUserID=).*?(?=;)',cookie)
sid=re.match('(?<=sid=).*?(?=;)',cookie)
csrf=re.match('(?<=bili_jct=).*',cookie)

# 部分编码问题
sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())

# bv转为av
def bv_to_av(bv):
    headers={   
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
    }
    r = requests.get('https://api.bilibili.com/x/web-interface/view', {'bvid': bv}, headers=headers)
    response = decode_json(r)
    try:
        return str(response['data']['aid'])
    except (KeyError, TypeError):
        return '883409884'

# json解析
def decode_json(r):
    try:
        response = r.json()
    except JSONDecodeError:
        return -1
    else:
        return response


# 自定义钉钉机器人推送
def pushinfo(info,specific):
# 定义推送内容,格式参考https://open.dingtalk.com/document/group/message-types-and-data-format
# 注意机器人的关键词
    data = {
     "msgtype": "text",
      "text": {
      "title":"Taoreset",
          "content": "【Taoreset-Serverless推送】\n"+info+specific
     }
 }

    headers = {'content-type': 'application/json'} # 请求头
    r = requests.post(webhook, headers=headers, data=json.dumps(data))
    r.encoding = 'utf-8'
    print (r.text)

# 阿b登录,得登陆经验
def login():
    headers={   
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
    'Cookie':cookie
    }
    response = requests.session().get('http://api.bilibili.com/x/space/myinfo',headers=headers)
    rejson = json.loads(response.text)
    code = rejson['code']
    msg = rejson['message']
    if code == 0:
        print('登录成功')
        return True
    else:
        print('登录失败:'+msg)
        return False

# 获取用户信息
def get_user_info():
    headers = {
        'Cookie':cookie
    }
    response = requests.session().get('http://api.bilibili.com/x/space/myinfo?jsonp=jsonp',headers=headers)
    rejson = json.loads(response.text)
    code = rejson['code']
    msg = rejson['message']
    if code == 0:
        userInfo=['账号:'+str(rejson['data']['silence']),
        '硬币:'+str(rejson['data']['coins']),
        '经验:'+str(rejson['data']['level_exp']['current_exp'])+"/"+str(rejson['data']['level_exp']['next_exp']),
        '等级:'+str(rejson['data']['level']),
        '昵称:'+str(rejson['data']['name'])
        ]
        print(userInfo[0]) 
        print (userInfo[1])
        print(userInfo[2])
        print(userInfo[3])
        print(userInfo[4])
        return userInfo
    else:
        print("用户信息获取失败:"+msg)
        return "用户信息获取失败:"+msg

# 直播签到,赚银瓜子儿
def do_sign():
    headers = {
        'Cookie':cookie
    }
    response = requests.session().get('https://api.live.bilibili.com/sign/doSign',headers=headers)
    rejson = json.loads(response.text)
    code = rejson['code']
    msg = rejson['message']

    if code == 0:
        print('直播签到成功!') 
        return True
    else:
        print("直播签到失败:"+msg)
        return False

# 看BV号视频,得观看经验
def watch():
        aid=bv_to_av(bid)
        headers = {
            'Cookie':cookie
        }
        response = requests.session().get('http://api.bilibili.com/x/web-interface/view?aid='+str(aid),headers=headers)
        rejson = json.loads(response.text)
        code = rejson['code']
        #print(response.text)
        if code == 0:
            cid = rejson['data']['cid']
            duration = rejson['data']['duration']
        else:
            print('视频信息解析失败')
            return False
        payload = {
            'aid': aid,
            'cid': cid,
            'jsonp': "jsonp",
            'mid': uid,
            'csrf': csrf,
            'played_time': 0,
            'pause': False,
            'realtime': duration,
            'dt': 7,
            'play_type': 1,
            'start_ts': int(time.time()),
        }
        response = requests.session().post('http://api.bilibili.com/x/report/web/heartbeat',data=payload,headers=headers)
        rejson = json.loads(response.text)
        code = rejson['code']
        if code == 0:
            time.sleep(5)
            payload['played_time'] = duration - 1
            payload['play_type'] = 0
            payload['start_ts'] = int(time.time())
            response = requests.session().post('http://api.bilibili.com/x/report/web/heartbeat',data=payload,headers=headers)
            rejson = json.loads(response.text)
            code = rejson['code']
            if code == 0:
                print(f"av{aid}观看成功")
                return True
        print(f"av{aid}观看失败 {response}")
        return False



def main(*args):
    if login():
        ui = get_user_info()
        desp='直播签到:'+str(do_sign())+'\n\n'+'观看视频:'+str(watch())+'\n\n'+ui[0]+'\n\n'+ui[1]+'\n\n'+ui[2]+'\n\n'+ui[3]+'\n\n'+ui[4]+'\n\n'
        pushinfo('哔哩哔哩签到成功',desp)
    else:
        pushinfo('哔哩哔哩签到失败','')

    

if __name__ == '__main__':
    main()

2.修改函数配置

完成函数的创建后就进入了函数管理的界面。函数代码这里就显示了我们上传的代码文件,所有代码执行的本地路径都在/code目录下。

如果有依赖模块提示没有,在下方的控制台终端输入命令也可以完成模块安装下载,所有工作目录下的代码修改,完成后都需要点击部署代码进行部署上传。

pip install -t . requests bs4

其余需要修改一下函数的配置,点击【函数配置】,找到【环境信息】编辑,修改【请求处理程序】,修改函数入口为<要执行的代码文件名.执行的函数名>,我这里脚本的文件名为bilibiliSignin.py,代码里的主函数为main,因此函数入口就为<bilibiliSignin.main>

3.完成函数功能测试

选择【测试函数】,即可立即对函数进行触发,点击测试函数进行测试

完成测试后下方就会显示日志输出内容,方便查看结果和排错

完成效果,定时触发23点准时完成签到,并由钉钉的机器人推送消息

欸嘿,大伙一起吧Serverless脚本跑起来,早日迎接B站六级会员!!!

脚本参考Github ,by sanshuifeibing

二、Serverless农产品电商网站上云改造

这里我拿隔壁软件专业(俺是网络技术的)的一个大作业项目作为部署的案例。项目是非常简单并且功能单一的,但是也是非常经典的前后端分离项目,由于我开发不太会,项目具体技术就不献丑了。之前据说是有什么版权的,我就不放源代码了hhhh

在改造之前,我相信是很多中小型公司业务的经典AllinOne结构,把业务涉及到的所有服务中间件运行在一台服务器/虚拟机上,虽然现在看可能完完全全是实验室环境,但是实际看到的依然有很多项目是这么做的。坏处也不用多提,部署运维难、难以进行资源的扩容、后续改造复杂、性能差没有应用高可用技术等等。

Serverless的农产品电商平台上云,项目比较简单,规划就在同一地域了。主要是将前端HTML页面与后端Jar包运行分别由两个单独的Serverless函数完成运行计算。其余支撑服务上云,分别用对应的云产品实现,这里Redis服务由于我自己ECS上有运行redis服务,就不再单独购买云数据库Redis版了,当然推荐使用阿里云的Redis云数据库产品。

1.Serverless服务创建

进入函数计算控制台,选择【服务及函数】,点击【创建服务】

输入创建服务的名称与描述,开启日志功能,点击确定,完成服务的创建

服务创建完成后,进入【服务详情】,找到【网络配置】,点击【编辑】

选择允许访问VPC,选择自定义配置,选择VPC、vSwitch、安全组,这里需要和后续其他支撑的云产品(数据库等)保持在同一VPC下。因此需要做好云上网络的规划,也要看一下产品是否在地域下有没有库存。

2.前端页面Serverless函数创建

完成服务创建后,点击【创建函数】,进入函数的创建页面

选择【使用自定义运行时平滑迁移WebServer】,输入函数名称,选择运行环境为【Nginx】,上传前端html代码与nginx的配置文件,选择监听端口为80,即为原nginx中间件的服务端口。

完成函数创建后,进入到函数详情界面,可以在函数代码中对代码进行编辑修改,代码改动后需要点击部署代码重新上传。上述也提到了,这里需要将Nginx服务配置一同上传,其中需要拷贝一份/etc/nginx/mime.types文件到当下目录,避免mime文件类型映射错误

这里放nginx关键配置,根据自己的业务情况修改,注意配置中端口监听需要与函数创建监听端口保持一致,同时网页代码的路径设置为/code

http {
    include mime.types;  #注意引入此文件
    keepalive_timeout  900;
    server {
        listen       80;        
        server_name  localhost;
        location / {
            root   /code;
            index  index.html index.htm;
            proxy_set_header    Host $host; 
            proxy_set_header    X-Real-IP $remote_addr;  
            proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;
       }
        location /users {
            proxy_pass http://serverless.后端函数访问地址.run;      
#Nginx反代传给后端的函数,地址在后续后端函数创建完可以拷贝
        }
        location /items {
            proxy_pass http://serverless.后端函数访问地址.run; 
#Nginx反代传给后端的函数,地址在后续后端函数创建完可以拷贝
        }

可以在调用日志查询相关日志

当函数触发运行,Serverless将会根据访问请求自动起实例,在这里可以手动登陆实例,去进行中间件服务的日志、运行环境的一些查看与排错。

3.后端Serverless函数创建

回到函数服务界面,再次点击【创建函数】,进入函数的创建页面

选择【使用自定义运行时平滑迁移WebServer】,输入函数名称,选择运行环境为【Java】,上传打包好的Jar包文件。

根据具体业务修改【启动命令】与【监听端口】。我这里需要监听业务端口为8080,并且需要在运行时传入数据库连接的参数,这里所配置的数据库源用户名密码连接地址,在后续的云数据库RDS中会进行相关设置。

后端函数创建完成后进入函数详情界面,其余功能与上述前端函数相同,不再复述

点击【触发器管理】,此处HTTP触发器提供了公网与内网访问地址,我们拷贝内网地址在前端中间件反代配置处填入此域名,实现访问前端触发后端函数

4.云数据库RDS MySQL Serverless创建与迁移导入

原有Allinone架构中没有做到数据库服务的独立与高可用,在此次云化部署,我们将选用阿里云公测中的云数据库Serverless版本。RDS MySQL Serverless提供了CPU、内存的实时弹性能力,具有资源用量低、简单易用、弹性灵活和价格低廉等优点,合理优化使用成本,进一步降本增效。

RDS MySQL Serverless实例创建

进入云数据库RDS控制台,点击【创建实例】,开始创建实例的流程,在【基础资源】设置界面,选择Serverless版,其余根据实际进行选择

Serverless RDS创建时【实例配置】需要注意网络VPC的设置,要与Serverless服务所设定的VPC一致,实现内网数据互通。确定订单后,等待实例创建完成即可

RDS数据库用户设置

完成实例创建,选择管理实例。在左侧任务栏选择【账号管理】,点击【创建账号】创建数据库账户供电商平台后端进行连接。

输入账户名、密码,选择为普通账户,点击确定完成用户创建

RDS 业务数据库创建

选择左侧栏中【数据库管理】,选择【创建数据库】

输入农产品电商业务所需的库名,并且授权账户给前一步设置的用户,点击创建完成库的设置。

RDS 服务连接地址

我们的Serverless函数中所需要连接库的地址,在rds实例中【数据库】连接处可以查到,不过需要提前设定白名单

我们将内网地址进行拷贝,并且也完成了连接用户、密码、库的配置,就可以配置到Serverless函数或者是后端代码中了

RDS 业务数据库的导入恢复

数据库的上云关键是数据内容的迁移,这次部署的业务数据库很少又很小,因此使用简单的备份SQL脚本文件作为迁移的方式。MySQL/Mariadb的数据导出有多种方式,可以根据实际需求进行备份导出,当然大型业务库有专用的备份迁移工具,这里不细说了。

进入DMS数据管理服务,选择【数据库开发】,在【数据变更】下点击【数据导入】。

根据具体备份方式导入数据库,我这里选择上传备份的sql脚本,提交申请开始导入数据

数据导入完成,数据迁移完成

5.Redis服务上云与静态资源CDN

Redis服务上云,前文也提到了,这里Redis服务由于我自己ECS上有运行redis服务,就不再单独购买云数据库Redis版了,ECS也处于同一VPC之下,可以实现内网互通。当然推荐使用阿里云的Redis云数据库产品。

静态资源的CDN,包括css\js\图片的加速,原有架构中已经存放在阿里的CDN服务上了,我这里就不多做改动了。

6.公网业务访问域名配置

最后一步,用户最终访问的是前端Serverless函数,如同阿里云给出的提示,访问默认的公网地址不会做任何中间件解析,而是直接下载首页html静态文件,因此我们需要自己配置访问域名。

回到函数计算控制台,选择【域名管理】,点击【添加自定义域名】

输入自定的域名,配置路由,选择对应函数的服务名称、函数名称、版本号LATEST(最新)

将需要解析的CNAME值,拷贝

拷贝CNAME记录值,点击【云解析DNS控制台】,进入解析设置,点击【添加记录】

选择记录类型【CNAME】,输入主机头,填入拷贝的记录值,确认完成添加

7.农产品电商项目Serverless上云效果

首页,访问效果,前端函数无误

农产品详情页访问

用户注册功能测试,数据库连接与写入无误

RDS中数据已成功写入

用户登陆测试

订单提交测试,后端函数无误

三、使用Serverless应用模板快速构建litemall电商应用系统

1.基于官方模板创建应用

Serverless应用提供了大量的官方应用模板,我们可以根据给出的模板来修改自己的业务,因此熟悉模板的部署也很重要。

进入到函数计算FC的控制台页面,点击【应用】,选择【通过模板创建应用】,选择【商城案例】

通过详情查看部署模板的信息,以及查询源代码,点击立即创建可以快速体验Serverless应用的创建,本地部署可以通过ServerlessDev工具进行部署

2.对创建应用进行配置

点击立即创建后,我们进行应用的初始化配置。

部署类型有两种:1.通过第三方代码仓库部署,2.直接部署

两者区别就是使用自己的仓库代码后续可以通过push更新项目发布,而直接部署需要手动配置。

这里就可以看到,我们的交付触发也是以Git仓库push提交为主,每次提交会自动触发部署。

如是自己配置应用,需要根据业务配置s.yaml文件,参考:https://www.serverless-devs.com/fc/yaml/readme

这里我选择Gitee仓库进行部署,但是需要进行仓库第三方应用的授权

点击前往授权,跳转到gitee的站点进行OAuth授权请求,点击同意授权

阿里用户在第一次使用FC函数计算时,需要对角色策略进行添加的,我这里已经使用过FC了,若提示需要添加策略,按照提示点击添加即可。


其他高级配置,需要根据业务进行修改,这里注意地域的选定,后续的其他弹性资源都会在此地域下,我这里选择本地杭州。

完成配置后点击创建,代码已经新建上传到我的Gitee仓库了。这里提供的s.yaml可以作为配置的参考,后续根据所部署的业务去修改yaml

3.应用部署上线

应用创建完成,首次自动进行部署,这里部署状态可以看到正在部署

查看部署日志,如果部署出现错误也可以从日志信息中查询报错。

部署经历了前置环境、资源同步、资源检查、执行部署这四个步骤后,我们的电商应用就完成了部署

首次部署完成,也是最新latest的一次部署版本,可以通过部署历史自由的进行回滚

4.访问部署上线的电商应用

访问测试的域名,就可以看到我们上线的litemall电商系统,进入电商应用的后台管理

litemall电商系统是一个开源的前后端分离带微信小程序的电商系统,具有电商平台基础的会员管理、商城管理、商品管理、推广管理、系统管理、配置管理、统计报表。

litemall电商系统,需要配置最小开发环境有以下:

MySQL

JDK1.8或以上

Maven

Nodejs

5.更换业务域名访问

同样,当我们正常上线了FC的业务时,Serverless用的是默认访问地址

函数计算上线提供的域名是以..http://fc.aliyuncs.com//proxy///[action?queries]为默认的,若是正常业务访问我们必然要修改访问的域名。

进入到函数计算FC的首页,点击高级功能下的域名管理,这里可以看到我们上线电商应用时的默认域名已经路由信息

我们选择添加自定义域名

输入域名的名称,也就是购买备案的域名下的自定义二级域名

点击路由配置,选择服务名称,这里是我们部署的电商系统litemall,选择函数名称与版本

拷贝公网CNAME地址,后续在DNS域名管理处添加解析

进入到域名管理下,添加一条记录,记录类型选择CNAME,输入主机记录,将刚刚拷贝的公网CNAME地址粘到记录值,点击添加即可

回到函数计算FC,在最后点击创建即可,回到主页看到我们新绑定的域名

最后,拿手机访问我自定义配置的公网地址

电商服务正常上线,公网地址正常访问主页

商品的详情购买页面

最后

Serverless相对其他方案来说,也是非常容易上手并高效的技术方案。上面的部署测试,其实还有很多需要改进的地方,例如第二个农产品电商上云项目,真正可靠的云上业务还需要负载均衡、高可用多地容灾、安全等其他云产品的引入我想把案例的重点放在Serverless服务器无感化上,本人也使用过不少阿里云的技术产品,深知对底层基础设施运维难度。

虽然这几个月学习生活比较繁忙,但是还是对社区的各种活动非常感兴趣,也想做一些更好的测试。这次的Serverless无论是对个人用户,还是企业用户。都是一种非常不错的选择,弹性资源与按需付费,更加节省资源与Money,更加优雅!

原文链接:http://click.aliyun.com/m/1000350415/

本文为阿里云原创内容,未经允许不得转载。

如题,最近闲着没事,研究出了模拟客户端签到的脚本。

使用

注册账号,过程略。网址:https://console.cloud.tencent.com/

进入云函数控制台的函数服务:https://console.cloud.tencent.com/scf/index?rid=1

  1. 单击“新建”
  2. 如图(BDUSS及STOKEN获取见附录,粘贴代码时留下它原来的第一行)

私信小编01即可获取大量python学习资料

python代码:


from requests import session
from hashlib import md5
from random import random
from time import sleep
class Tieba():
def __init__(self, BDUSS, STOKEN):
    self.BDUSS = BDUSS
    self.STOKEN = STOKEN
    self.count = [0, 0, 0] # 签到成功, 已经签到, 总
    self.session = session()
    self.session.headers.update(
        {'Accept': 'text/html, */*; q=0.01',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
        'Connection': 'keep-alive',
        'Host': 'tieba.baidu.com',
        'Referer': 'http://tieba.baidu.com/i/i/forum',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
        'Chrome/71.0.3578.98 Safari/537.36',
        'X-Requested-With': 'XMLHttpRequest'}
    )
def set_cookie(self):
    self.session.cookies.update({'BDUSS': self.BDUSS, 'STOKEN': self.STOKEN})
def fetch_tbs(self):
    r = self.session.get('http://tieba.baidu.com/dc/common/tbs').json()
    if r['is_login'] == 1: self.tbs = r['tbs']
    else: raise Exception('获取tbs错误!以下为返回数据:' + str(r))
def fetch_likes(self):
    self.rest = set()
    self.already = set()
    r = self.session.get('https://tieba.baidu.com/mo/q/newmoindex?').json()
    if r['no'] == 0:
        for forum in r['data']['like_forum']:
            self.count[2] += 1
            if forum['is_sign'] == 1:
                self.already.add(forum['forum_name'])
            else:
                self.rest.add(forum['forum_name'])
    else: raise Exception('获取关注贴吧错误!以下为返回数据:' + str(r))
def sign(self, forum_name):
    data = {
        'kw': forum_name,
        'tbs': self.tbs,
        'sign': md5(f'kw={forum_name}tbs={self.tbs}tiebaclient!!!'.encode('utf8')).hexdigest()
    }
    r = self.session.post('http://c.tieba.baidu.com/c/c/forum/sign', data).json()
    if r['error_code'] == '160002':
        print(f'"{forum_name}"已签到!')
        self.count[1] += 1
        return True
    elif r['error_code'] == '0':
        print(f'"{forum_name}"签到成功,您是第{r["user_info"]["user_sign_rank"]}个签到的用户!') # Modify!
        self.count[0] += 1
        return True
    else:
        print(f'"{forum_name}"签到失败!以下为返回数据:{str(r)}')
        return False
def loop(self, n):
    print(f'* 开始第{n}轮签到 *')
    rest = set()
    self.fetch_tbs()
    for forum_name in self.rest:
        sleep(random() * 3)
        flag = self.sign(forum_name)
        if not flag: rest.add(forum_name)
    self.rest = rest
def main(self, max):
    self.set_cookie()
    self.fetch_likes()
    n = 0
    if self.already:
        print('* 已经签到的贴吧 *')
        for forum_name in self.already:
            print(f'"{forum_name}"已签到!')
            self.count[1] += 1
    while n < max and self.rest:
        n += 1
        self.loop(n)
    print('* 本日签到报告 *')
    print(f'共{self.count[2]}个贴吧,其中签到成功{self.count[0]}个,已经签到{self.count[1]}个,签到失败{len(self.rest)}个。')
    if self.rest:
        print('* 签到失败列表 *')
        for forum_name in self.rest:
            print(f'"{forum_name}"签到失败!')
def main_handler(*args):
with open('BDUSS.txt') as f: BDUSS = f.read()
with open('STOKEN.txt') as f: STOKEN = f.read()
task = Tieba(BDUSS, STOKEN)
task.main(3)
# 不需要推送的删除以下几行
if task.rest:
    message = f'以下为未完成列表:'
    for forum in task.rest: message += '\n' + forum
    data = {'text': '签到未完全完成!', 'desp': message}
    post('https://sc.ftqq.com/你的key.send', data=data)```

如图:

回到此界面,点击"测试"(应该会自动部署,如提示请先部署就先部署一下)

  1. 如若成功,应该能看到“测试成功”,输出签到的详细信息,可以进入下一步;如若失败,查看是否复制粘贴出错,无误仍出错请参照附录。
  2. 设置自动执行
    表达式:
    0 0 1 * * * *


附录/Q&A

  1. BDUSS及STOKEN的获取(以Edge浏览器为例)登录任一百度产品,例如百度一下如图打开cookie界面

在"baidu.com/Cookie"下找到"BDUSS",复制其内容


在"passport.baidu.com/Cookie"下找到"STOKEN",复制其内容

  1. [b]注意:粘贴到txt文件中时只能一行,如果出现行号为"2"的空行请删除,否则会出错
  • 常见错误超时
    a. 是否进行了教程的步骤5?
    b. 尝试删除"index.py"的第65行(sleep(random() * 3))和第5行(from time import sleep)
    c. 仍然超时则关注贴吧个数过多,可以自行修改代码及增加触发器,分次签到,这里不再深入。获取关注贴吧错误
    a. 两个txt文件最后是否有空行
    b. 是否复制了正确的项到正确的txt,完成后有没有保存
    c. 如果这无法解决问题,那重新登录后再次获取
    d. 再不行的话我也没办法{:301_971:}
  • 指一算,入职已经一月有余,也参与了一两个项目,以前做学校项目的时候,对一个功能的取舍,很多时候都取决于“那个某某应用都有,那我们也放一下吧”之类的想法;但在“每个功能对于当前产品的受众是否有用,对产品未来的发展是否有积极意义”这一点上,却没有进行长足的思考。

    例如:在近期的一个项目的设计中涉及到的“签到”功能,起初我并没有把这个功能放在心上,然而在对其进行设计的过程中,我发现,“签到”并非像大部分人想的那样,只是一个“放之四海而皆准”的惯用功能而已。

    什么是“签到”

    在互联网的浪潮来临以前,当人们说起“签到”二字时,联想起的不是每日打卡上线拿到的奖励,更多的可能是被惩罚支配的恐惧,不论是上课点名所占据的平时分,乃至每日上班的出勤率,都和签到的次数息息相关。

    早期的签到多以人工手写登记的方式为主,用于记录人员的到场情况,便于登记者对提供的内容进行调整。

    传统“签到”的使用场景

    随着互联网的发展,“签到”逐渐在原先的意义基础上,弱化了“惩罚”的概念,扩宽了签到的时间维度(例如可以补签),同时增加演变出了促进用户活跃、刺激产品推广等更多新的用途。

    综合来说,大部分的签到功能,本质上都是一种通过奖励(物质or心理)强化用户行为,从而培养用户使用习惯,最终达到推广产品/培养忠实用户的功能。

    为什么要“签到”

    在讨论一件事物的外化形态的时候,需要先弄明白其产生的本质原因。对于现在的App来说,“签到”功能随处可见。然而这样设计的原因是什么,又能够帮助使用者达到什么目的呢?

    常见“签到”功能的目的

    目的1:促进用户活跃度

    说到“签到”,最常见的类型大概就是奖励型“签到”了。这一类型,大多都会给用户一些物质或精神奖励,来刺激用户重复启动产品,培养用户习惯,从而提升产品的活跃度。

    许多产品还可以设置定时的签到提醒,在设定好的时间,通过推送提醒用户打卡,以防止用户错过各式各样的签到福利,举个栗子:

    京东签到领“京豆”(左)&阴阳师签到奖励(右)

    在京东商城中,通过连续启动app签到可以兑换京豆,京豆在实际消费中可直接用于支付订单,每100京豆可抵1元现金使用。而在游戏《阴阳师》中,每日登录摇签都会获得对应的奖励,而不论是商城还是游戏,相信大家都有过“本来只想看看消息,没想到一打开就根本停不下来”的经历。这种时候,设计者便达到了他的目的。

    目的2:增加使用,提高营收

    仅仅是简单地让用户打开App,并不能满足产品设计者。一个产品存在的价值,只有在它真正被用户使用的时候才能得到足够的体现。于是“签到”功能又承载了增加用户对产品业务的使用,提高产品营收的目标。

    例如:上文提到的签到奖励“京豆”,还有《阴阳师》签到获得的红色达摩(注:快速升级式神经验的工具),都是为了让用户买的更多,玩的更多,刺激用户对产品的重复使用。

    目的3:促进分享

    以上提到的内容,主要是通过对“签到”行为给予积极的反馈,使用户在多巴胺的影响下产生预期的行为。而接下来这种签到形式,则是利用了用户自我实现层面的心理需求。

    英语流利说的打卡页面

    这种类型的签到往往是一个长期的、需要坚持的过程,并且可以为用户带来积极正向的影响。一些具有自我成长意义的签到内容可以满足用户的自我实现需求(自身技能的提高)以及尊重需求(分享后得到他人的夸赞、认同),同时也可以提高产品的曝光度。看着累计打卡天数的一天天增加,内心也会觉得很有成就感。

    怎么做”签到”

    在了解了“签到”产生的背景和原因之后,当我们要设计一个属于自己的签到功能的时候,需要考虑的内容有哪些呢?

    “签到”功能的设计要素

    因素1:启动方式

    在以智能手机为媒介的互联网时代,打开“签到”的方式已经不仅仅局限于纸笔书写了,针对不同类型的产品,也有分别适用的签到方式。例如:对于单纯只需要用户登录便可以达到促活目的的产品(例如电商、游戏产品),可以在登录后自动弹出签到窗口。

    对于具有一定工具性,用户需要完成任务才能体现平台价值的(例如学习平台、运动软件)则可以在任务完成后跳出签到内容,也可以将签到放在希望引导用户观看的一些内容界面,增加打开次数。

    因素2:计算方式

    大部分的“签到”累积天数的方式不外乎累计型和连续型两种,累计型签到指的是:断签后依然计算总天数,连续型则指断签后需要重新开始的计算方式。对于需要用户做比较长时间心理建设的签到内容(例如:学习等),使用累计型的会更合适,否则用户很容易出现“破罐破摔”,一旦断签就彻底放弃的情况。

    因素3:签到周期

    一般来说,我们可以把签到周期分为两种:

    • 一种是每天签到获得的奖励量级基本都相同,只有时间上可见的增加,这样用户可能很容易感到疲劳。
    • 另一种是在一定的时间周期内,连续签到所获得的奖励内容存在叠加与翻倍关系,每完成一个短周期,就可以获得一个大奖,增强新鲜感,但这样,一旦用户断签,带来的损失感也要比前一种大得多,用户不一定愿意重新开始,而是会彻底放弃。

    因素4:反馈内容

    对于签到的奖励内容该如何设定,这往往和产品希望培养的用户行为直接相关,例如:希望用户多购买东西,就可以多送优惠券,希望用户多分享,就可以赠送一些满足用户虚荣心的等级积分。根据反馈内容的不同,我们还可以将签到分成以下几种常见类型:

    (1)物质激励型

    物质激励型包含两种:

    • 一种是通过返还虚拟货币/积分等可消费用具,刺激用户使用,例如:上图提到的京豆、阴阳师,这种签到激励往往耗时较长,且有一定的运营成本,奖励的价值往往都比较低。同时过程中缺乏新意,且只能满足用户较低层次的需求。
    • 另一种则是先让用户付出少量的物质财富,保持一定周期的签到后可以获得更多的奖励。若失败,自己的付出会成为完成的人获得的奖励,例如:蜗牛睡眠中的“3天周期睡眠/早起挑战”。

    蜗牛睡眠挑战计划

    这类签到方式,具有游戏化的四个基本特性:目标、规则、反馈、自愿参与,让用户更容易坚持下去,甚至沉迷。但从心理学上来说,“失去”往往会带来不良的情感体验。

    (2)精神激励型

    这类签到,主要是利用等级、称号、曝光率等精神奖励刺激用户使用,例如:Keep的运动徽章,微博话题的签到热度排行等等,主要利用了人们对于自我实现和尊重等处于马斯洛需求三角上层的需求,大多数时候能带来分享效应,但对于获得奖励的门槛往往要求较高(例如:一定要完成学习/运动目标才可以签到)。

    Keep运动徽章(左)& 微博超级话题签到排行(右)

    (3)体验驱动型

    这类签到,则是将“签到”作为产品的一种属性,使产品拥有了每天都需要被打开的原因。例如:网易蜗牛读书,每天免费读书一小时,虽然没有签到功能,但是将其内化成了用户每天必须打开它的理由。

    蜗牛读书,每天一小时阅读时长免费领

    这类将“签到”基因植入产品的方法可以很好地提高整个产品的活跃度,但这种功能是建立在用户有需求的基础上的,如果用户没有阅读的需求或倾向,免费时长再长可能也没有吸引力。

    万物皆“签到”?我觉得不行

    虽然“签到”已经成为了一个十分常见的功能,但并不意味着所有的产品都适合增加这个功能,以刺激用户无限地唤起主页面。

    结合上文的一些分析,我们可以得出,适合设置“签到”功能的产品大多都具有以下特点:

    适合设计签到的产品特点以及产品类型

    例如以下几类应用:

    1. 阅读类

    这里的阅读类包括了小说阅读、漫画阅读平台,也包括了内容比较碎片化的新闻资讯阅读平台。对于内容产出者和平台来说,用户的活跃度是至关重要的,对于传统内容平台来说决定了作者的收入,对于资讯平台而言,越多的用户也能带来更多的广告投资,所以需要更多刺激用户活跃度的设置。

    2. 金融类

    金融类产品的收益曲线是每天都在变化的,即使没有签到奖励,用户大多时候每天都会进行查看,可以利用这一点给用户一些优惠/活动,吸引用户进行投资。

    3. 社区类

    社区产品想要长期活下去,就需要提高用户自主运营参与的程度,在获得用户活跃度保持社区活力的同时,还可以利用LBS技术,进行精准的运营活动地域投放。

    4. 电商类

    电商类产品都希望用户尽量多地浏览商品,增加下单内容,签到和常用的大促手段有异曲同工之妙,都是为用户创造更多的购买可能。

    相比之下,以下类型的应用则不太适合使用“签到”功能:

    不适合强加“签到”功能的应用特点

    例如:杀毒软件、天气类软件,这类软件大多数时候是在后台中完成自己的任务,对用户的生活主要起到辅助作用的,用户对这类产品没有重复唤起的需求,太过强调自己的存在感反而可能招致用户厌烦。

    设想一下如果一个杀毒软件有事没事总是弹出提醒让你每日签到,恐怕会被立马送进回收站吧…

    综上,这就是我在前辈们的思考基础上对于“签到”这个功能得出的一些想法,欢迎对设计感兴趣的朋友们一起来讨论呀~

    参考内容:

    兰道锅,在设计签到功能的时候,我在想什么? | 人人都是产品经理. (2018). Retrieved from http://www.woshipm.com/pd/1025518.html

    马璐,如何让签到成为提升用户活跃度的利器 | 人人都是产品经理(2018). Retrieved from http://www.woshipm.com/pd/992382.html

    活动盒子,“打卡签到”:APP用户运营制胜法宝 | 人人都是产品经理. (2018). Retrieved from http://www.woshipm.com/operate/491571.html

    作者:rt,公众号:未知素设计

    本文由 @未知素设计 原创发布于人人都是产品经理。未经许可,禁止转载

    题图来自 Pexels,基于 CC0 协议