Python + GitHub Actions 实现 CSDN 自动签到与抽奖(非 selenium 版本)

爬虫实战 同时被 2 个专栏收录
12 篇文章 20 订阅


>>> 如果你想直接使用,不想看废话,请直接看【6x00】如何使用部分<<<


【1x00】技术栈

  • 语言:Python
  • 功能:签到、抽奖(可选)
  • 自动签到:GitHub Actions
  • 签到结果通知:Server 酱(可选)、企业微信(可选)、钉钉(可选)

【2x00】代码实现签到与抽奖

对签到和抽奖的过程抓包分析,可以揪出以下两个链接:

  • 签到:https://me.csdn.net/api/LuckyDraw_v2/signIn
  • 抽奖:https://me.csdn.net/api/LuckyDraw_v2/goodLuck

在这里插入图片描述

两者的请求方式都是 post,且两者的 Request headers 和 Request Payload 都一样。重要参数有三个:cookieusernameuuid,cookie 参数暂时没办法解决,只能自己登陆后复制过来,username 是你的 id,可以在【个人中心】—【个人资料】—【基本信息】—【用户 ID】查看,uuid 就是 cookie 里面第一个参数 uuid_tt_dd 的值。用 Python 实现基本的签到与抽奖功能如下(此代码可直接运行,需要将 CSDN_ID 和 COOKIE 换成你的,COOKIE 可以在已经登录的任意页面 F12 获取):

import requests
import json


CSDN_ID = ''         # 你的 CSDN ID
COOKIE = ''          # 你的 cookie
IF_LUCK_DRAW = True  # 是否开启抽奖


class CSDN:
    def __init__(self):
        self.UUID = COOKIE.split(';', 1)[0].split('=', 1)[1]
        self.USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36'
        self.SIGN_IN_URL = 'https://me.csdn.net/api/LuckyDraw_v2/signIn'
        self.LUCKY_DRAW_URL = 'https://me.csdn.net/api/LuckyDraw_v2/goodLuck'
        self.DRAW_TIMES = 0  # 可抽奖次数
        self.HEADERS = {
            'accept': 'application/json, text/plain, */*',
            'accept-encoding': 'gzip, deflate, br',
            'accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7',
            'content-length': '243',
            'content-type': 'application/json;charset=UTF-8',
            'cookie': COOKIE,
            'origin': 'https://i.csdn.net',
            'referer': 'https://i.csdn.net/',
            'sec-fetch-dest': 'empty',
            'sec-fetch-mode': 'cors',
            'sec-fetch-site': 'same-site',
            'user-agent': self.USER_AGENT
        }
        self.DATA = {
            'ip': '',
            'platform': 'pc-my',
            'product': 'pc',
            'user_agent': self.USER_AGENT,
            'username': CSDN_ID,
            'uuid': self.UUID
        }

    def csdn_sign_in(self):
        response = requests.post(url=self.SIGN_IN_URL, headers=self.HEADERS, data=self.DATA)
        result = json.loads(response.text)
        # print(result)

        if result['code'] == 200:
            if not result['data']['isSigned'] and result['data']['signed']:
                keep_count = result['data']['keepCount']
                total_count = result['data']['totalCount']
                total_signed_count = result['data']['totalSignedCount']
                # self.STAR = result['data']['star']
                self.DRAW_TIMES = result['data']['drawTimes']
                print('签到成功!你已连续签到 {} 天,累计签到 {} 天,当前已有 {} 人签到。'.format(keep_count, total_count, total_signed_count))
            elif result['data']['isSigned']:
                print('你今天已经签到过了哟!')
            else:
                print('签到失败!')
        elif result['code'] == 400102:
            print('签到失败!{} 用户不存在或者 cookie 错误!请检查 CSDN ID 或尝试重置 cookie!'.format(CSDN_ID))
        else:
            print('签到失败!')

    def csdn_luck_draw(self):
        if self.DRAW_TIMES != 0:
            response = requests.post(url=self.LUCKY_DRAW_URL, headers=self.HEADERS, data=self.DATA)
            result = json.loads(response.text)
            # print(result)

            if result['code'] == 200:
                if result['data']['can_draw']:
                    prize_title = result['data']['prize_title']
                    print('抽奖成功!恭喜你获得{}'.format(prize_title))
                elif not result['data']['can_draw']:
                    print('抽奖机会已经用完了哟!')
                else:
                    print('抽奖失败!')
            elif result['code'] == 400102:
                print('抽奖失败!{} 用户不存在或者 cookie 错误!请检查 CSDN ID 或尝试重置 cookie!'.format(CSDN_ID))
            else:
                print('抽奖失败!')


def run():
    c = CSDN()
    c.csdn_sign_in()
    if IF_LUCK_DRAW:
        c.csdn_luck_draw()


if __name__ == '__main__':
    run()


【3x00】签到结果通知

【03x01】Server 酱

Server 酱首页:sc.ftqq.com

什么是 Server 酱?简单来说,登录 Server 酱后,会分配给你一个 SCKEY,通过向专门的 URL 发送 get 或者 post 请求,你的微信就会收到对应的消息,对于不使用企业微信和钉钉的用户来说,这种方法是最方便的。目前有标准版和 Turbo 版,据说标准版会下线,目前还能使用,所以暂时使用的是标准版。若被弃用,我会第一时间更新。实现效果:

在这里插入图片描述


【03x02】企业微信

如果你是一个企业微信群的群主,那么可以在群名右键添加一个机器人,添加成功后会给你一个机器人的 URL 地址,向这个 URL 发送 post 请求就可以在群里看到对应的消息,支持 markdown、text、图片等多种格式,具体可以查看机器人配置说明。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


【03x03】钉钉

和企业微信类似,如果你是一个钉钉群的群主,依次右键【群设置】—【群智能助手】—【添加机器人】,选择自定义(通过webhook接入自定义访问)机器人,安全设置里面选择【加签】,添加完成后你会得到一个机器人的 Webhook URL 和一个加签的密钥,之后使用特定的算法将一些参数和 URL 组成新的 URL,再向这个 URL 发送 post 请求就可以在群里收到消息了,具体使用方法参见官方文档:https://developers.dingtalk.com/document/app/custom-robot-access

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


【4x00】自动签到

这里自动签到使用的是 GitHub Actions,这是 GitHub 提出的一项持续集成服务,它提供了一个高效易用的 CI/CD 工作流,能够帮助我们自动构建、测试和部署我们的代码,简单来说,对于我们的自动签到程序,它能够每天定时运行一遍,以达到每天签到的目的,如果有 cookie,密钥之类的参数,可以在仓库的 【Settings】—【Secrets】里面添加。定时任务和运行环境等其他参数统一在一个 .yml 文件中配置。

GitHub Actions 官方文档:https://docs.github.com/cn/actions


【5x00】完整代码

CSDN 签到抽奖主程序代码 CSDN.py

注:代码不能直接运行,这是放到 GitHub Actions 上执行的代码,使用方法见【6x00】,如果要本地运行,则需要将配置项目 1 2 3 中 os.environ["xxxxx"] 换成各自对应的值!

# ====================================
# --*-- coding: utf-8 --*--
# @Time    : 2021-05-29
# @Author  : TRHX • 鲍勃
# @Blog    : www.itrhx.com
# @CSDN    : itrhx.blog.csdn.net
# @FileName: CSDN.py
# @Software: PyCharm
# ====================================

import requests
import json
import os
import time
import hmac
import hashlib
import base64
import urllib.parse

# ==============  1.CSDN 个人信息 ============== #
CSDN_ID = os.environ["CSDN_ID"]            # 必填!CSDN 的 ID
COOKIE = os.environ["COOKIE"]              # 必填!已登录的 cookie

# ==============  2.功能开关配置项 ============== #
# 填 on 则开启,开启的同时也需要配置3中的选项,不填或填其他则关闭
IF_LUCK_DRAW = os.environ["IF_LUCK_DRAW"]  # 选填!是否开启抽奖
IF_SEVER = os.environ["IF_SEVER"]          # 选填!是否开启 server 酱通知
IF_WECHAT = os.environ["IF_WECHAT"]        # 选填!是否开启企业微信通知
IF_DING = os.environ["IF_DING"]            # 选填!是否开启钉钉通知

# ==============  3.消息通知配置项 ============== #
SEVER_SCKEY = os.environ["SEVER_SCKEY"]    # 选填!server 酱的 SCKEY
WECHAT_URL = os.environ["WECHAT_URL"]      # 选填!企业微信机器人地址
DING_URL = os.environ["DING_URL"]          # 选填!钉钉机器人地址
DING_SECRET = os.environ["DING_SECRET"]    # 选填!钉钉机器人加签 SECRET

# ==============  4.准备发送的消息 ============== #
TEXT = ''
DESP = ''


class CSDN:
    def __init__(self):
        self.DRAW_TIMES = 0  # 可抽奖次数
        self.UUID = COOKIE.split(';', 1)[0].split('=', 1)[1]
        self.USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36'
        self.SIGN_IN_URL = 'https://me.csdn.net/api/LuckyDraw_v2/signIn'
        self.LUCKY_DRAW_URL = 'https://me.csdn.net/api/LuckyDraw_v2/goodLuck'
        self.HEADERS = {
            'accept': 'application/json, text/plain, */*',
            'accept-encoding': 'gzip, deflate, br',
            'accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7',
            'content-length': '243',
            'content-type': 'application/json;charset=UTF-8',
            'cookie': COOKIE,
            'origin': 'https://i.csdn.net',
            'referer': 'https://i.csdn.net/',
            'sec-fetch-dest': 'empty',
            'sec-fetch-mode': 'cors',
            'sec-fetch-site': 'same-site',
            'user-agent': self.USER_AGENT
        }
        self.DATA = {
            'ip': '',
            'platform': 'pc-my',
            'product': 'pc',
            'user_agent': self.USER_AGENT,
            'username': CSDN_ID,
            'uuid': self.UUID
        }

    def csdn_sign_in(self):
        global TEXT, DESP
        response = requests.post(url=self.SIGN_IN_URL, headers=self.HEADERS, data=self.DATA)
        result = json.loads(response.text)
        # print(result)

        if result['code'] == 200:
            if not result['data']['isSigned'] and result['data']['signed']:
                keep_count = result['data']['keepCount']
                total_count = result['data']['totalCount']
                total_signed_count = result['data']['totalSignedCount']
                # self.STAR = result['data']['star']
                self.DRAW_TIMES = result['data']['drawTimes']
                TEXT = 'CSDN 签到成功!'
                DESP = 'CSDN 签到成功!你已连续签到 {} 天,累计签到 {} 天,当前已有 {} 人签到。'.format(keep_count, total_count, total_signed_count)
                print('签到成功!你已连续签到 {} 天,累计签到 {} 天,当前已有 {} 人签到。'.format(keep_count, total_count, total_signed_count))
            elif result['data']['isSigned']:
                TEXT = 'CSDN 签到失败!'
                DESP = 'CSDN 签到失败!你今天已经签到过了哟!'
                print('你今天已经签到过了哟!')
            else:
                TEXT = 'CSDN 签到失败!'
                print('签到失败!')
        elif result['code'] == 400102:
            TEXT = 'CSDN 签到失败!'
            DESP = 'CSDN 签到失败!{} 用户不存在或者 cookie 错误!请检查 CSDN ID 或尝试重置 cookie!'.format(CSDN_ID)
            print('签到失败!{} 用户不存在或者 cookie 错误!请检查 CSDN ID 或尝试重置 cookie!'.format(CSDN_ID))
        else:
            TEXT = 'CSDN 签到失败!'
            print('签到失败!')

    def csdn_luck_draw(self):
        if self.DRAW_TIMES != 0:
            global TEXT, DESP
            response = requests.post(url=self.LUCKY_DRAW_URL, headers=self.HEADERS, data=self.DATA)
            result = json.loads(response.text)
            print(result)

            if result['code'] == 200:
                if result['data']['can_draw']:
                    prize_title = result['data']['prize_title']
                    TEXT += 'CSDN 抽奖成功!'
                    DESP += '抽奖成功!恭喜你获得{}'.format(prize_title)
                    print('抽奖成功!恭喜你获得{}'.format(prize_title))
                elif not result['data']['can_draw']:
                    TEXT += 'CSDN 抽奖失败!'
                    DESP += 'CSDN 抽奖失败!抽奖机会已经用完了哟!'
                    print('抽奖机会已经用完了哟!')
                else:
                    TEXT += 'CSDN 抽奖失败!'
                    print('抽奖失败!')
            elif result['code'] == 400102:
                TEXT = 'CSDN 抽奖失败!'
                DESP = 'CSDN 抽奖失败!{} 用户不存在或者 cookie 错误!请检查 CSDN ID 或尝试重置 cookie!'.format(CSDN_ID)
                print('抽奖失败!{} 用户不存在或者 cookie 错误!请检查 CSDN ID 或尝试重置 cookie!'.format(CSDN_ID))
            else:
                TEXT += 'CSDN 抽奖失败!'
                print('抽奖失败!')


class Notice:
    @staticmethod
    def sever():
        requests.get('https://sc.ftqq.com/{}.send?text={}&desp={}'.format(SEVER_SCKEY, TEXT, DESP))

    @staticmethod
    def wechat():
        data = {
            'msgtype': 'text',
            'text': {
                'content': DESP
            }
        }
        headers = {'content-type': 'application/json'}
        requests.post(url=WECHAT_URL, headers=headers, data=json.dumps(data))

    @staticmethod
    def ding():
        timestamp = str(round(time.time() * 1000))
        secret = DING_SECRET
        secret_enc = secret.encode('utf-8')
        string_to_sign = '{}\n{}'.format(timestamp, secret)
        string_to_sign_enc = string_to_sign.encode('utf-8')
        hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
        sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
        headers = {'Content-Type': 'application/json'}
        complete_url = DING_URL + '&timestamp=' + timestamp + "&sign=" + sign
        data = {
            "text": {
                "content": DESP
            },
            "msgtype": "text"
        }
        requests.post(url=complete_url, data=json.dumps(data), headers=headers)


def run():
    c = CSDN()
    n = Notice()
    c.csdn_sign_in()

    if IF_LUCK_DRAW == 'on':
        c.csdn_luck_draw()
    if IF_SEVER == 'on':
        n.sever()
    if IF_WECHAT == 'on':
        n.wechat()
    if IF_DING == 'on':
        n.ding()


if __name__ == '__main__':
    run()

GitHub Actions 工作流配置文件 csdn-sign-in.yml

# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: CSDN Sign In

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  schedule:
    - cron: 0 16 * * *

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python 3.9
      uses: actions/setup-python@v2
      with:
        python-version: 3.9
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install requests
        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
    - name: CSDN
      run: |
        python3 CSDN.py 
      env: 
        CSDN_ID: ${{ secrets.CSDN_ID }}
        COOKIE: ${{ secrets.COOKIE }}
        IF_LUCK_DRAW: ${{ secrets.IF_LUCK_DRAW }}
        IF_SEVER: ${{ secrets.IF_SEVER }}
        IF_WECHAT: ${{ secrets.IF_WECHAT }}
        IF_DING: ${{ secrets.IF_DING }}
        SEVER_SCKEY: ${{ secrets.SEVER_SCKEY }}
        WECHAT_URL: ${{ secrets.WECHAT_URL }}
        DING_URL: ${{ secrets.DING_URL }}
        DING_SECRET: ${{ secrets.DING_SECRET }}

所需环境 requirements.txt

requests

【6x00】如何使用

【06x01】方法一:直接 Fork 代码(推荐)

代码地址:https://github.com/TRHX/CSDNSignIn

点击右上角【Fork】代码到你的账户,然后点击【Settings】—【Secret】—【New repository secret】,依次填入以下 Name 以及对应的 Value:

Name是否为必填项含义Value 示例
CSDN_IDCSDN 的 IDqq_36759224
COOKIE已登录的 cookieuuid_tt_dd=10_287647…
IF_LUCK_DRAW是否开启抽奖on
IF_SEVER是否开启 server 酱通知on
IF_WECHAT是否开启企业微信通知on
IF_DING是否开启钉钉通知on
SEVER_SCKEY取决于 IF_SEVERserver 酱的 SCKEYSCU165692T…
WECHAT_URL取决于 IF_WECHAT企业微信机器人地址https://qyapi.weixin.qq.com/cgi-bin/webhook/…
DING_URL取决于 IF_DING钉钉机器人地址https://oapi.dingtalk.com/robot/send…
DING_SECRET取决于 IF_DING钉钉机器人加签 SECRETSEC1cb948ba…

举例:我需要开启抽奖和 server 酱通知,则需要填的有:

NameValue
CSDN_IDqq_36759224
COOKIEuuid_tt_dd=10_287647…
IF_LUCK_DRAWon
IF_SEVERon
SEVER_SCKEYSCU165692T…

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如果你需要更改每天签到的时间,则需要更改 .github/workflows/csdn-sign-in.yml 文件里面 cron: 0 16 * * * 的值,这里使用的是 Linux crontab 定时任务命令,0 16 * * * 表示每天凌晨运行一次,注意这里使用的是 UTC 国际标准时间,与北京时间相差 8 个小时,所以 UTC 时间 16 点也就是北京时间的 24 点。


注意:

  • cookie 的值需要你登录后 F12 查看复制过来,cookie 的有效期是多久暂时不得而知,等下次失效了我再回来告诉你们有效期是多久,如果我没说,那就代表一直没失效!😁

  • 经过测试,cookie 的有效期在 45 天左右,失效后需要重新复制一个新 cookie 过来!

  • 如果你开启了自动抽奖,程序会识别你当前还有多少次抽奖次数,如果抽奖次数为0,则仍然不执行抽奖任务!


【06x01】方法二:自己上传代码

【5x00】完整代码中的 CSDN.py 以及所需环境 requirements.txt 上传到自己的 GitHub 仓库,点击仓库的 【Actions】,选择【Python application】,点击【Set up this workflow】创建工作流,之后会出来一个 python-app.yml,参考【5x00】完整代码中的 csdn-sign-in.yml,添加定时任务和参数配置,编辑完成后点击【Start commit】提交。

当然也可以直接在本地编写好配置文件 csdn-sign-in.yml 直接和源码一起提交,GitHub 会自动识别,注意路径为:.github\workflows\csdn-sign-in.yml

以上步骤完成之后,再参考步骤【06x01】,完成参数设置即可。
在这里插入图片描述
在这里插入图片描述

相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值