GitHub Actions定时任务实战:Python自动化签到与验证码识别
1. 项目概述:当定时任务遇上验证码
做自动化运维或者日常办公的朋友,肯定都遇到过定时任务的需求。比如,每天凌晨备份数据库,每周一生成一份报表,或者像我最近遇到的一个更“接地气”的需求:每天自动登录某个需要验证码的网站完成签到,获取积分或奖励。手动操作不仅枯燥,还容易忘记。传统的解决方案,要么是在自己的服务器上跑个cron任务,要么用一些云函数服务。但这些方案要么有服务器成本,要么配置起来略显繁琐。
直到我把目光投向了GitHub Actions。这个原本为CI/CD(持续集成/持续部署)而生的工具,其免费、稳定且功能强大的定时任务(schedule)触发机制,让它成为了执行周期性任务的绝佳选择。更妙的是,它提供了一个干净、隔离的Linux运行环境,我们可以在这个环境里运行任何脚本,包括处理最让人头疼的验证码识别。
这个项目的核心,就是利用GitHub Actions的schedule触发器,结合Python脚本和验证码识别技术,实现一个全自动、零成本的签到机器人。它完全运行在GitHub的服务器上,你不需要为此支付一分钱,也不需要维护任何服务器,只需要有一个GitHub账号和一个代码仓库即可。
听起来是不是很心动?接下来,我就带你从零开始,拆解这个项目的每一个技术环节,分享我踩过的坑和积累的经验,让你也能轻松搭建属于自己的“赛博劳工”。
2. 核心思路与技术选型解析
2.1 为什么选择GitHub Actions?
在决定技术方案前,我评估过几种常见的方案:
- 自建服务器 + Cron:最传统,但需要服务器成本(电费或云主机费用)和运维精力(系统更新、网络维护)。
- 云函数(如AWS Lambda, 腾讯云SCF):无服务器架构,按需付费。但对于高频次(如每天一次)的定时任务,虽然费用极低,但配置和调试环境有时不如GitHub Actions直观,且可能有冷启动延迟。
- 第三方定时任务服务:一些网站提供HTTP调用式的定时任务,但通常功能单一,难以执行复杂的、需要依赖库的脚本。
GitHub Actions的独特优势在于:
- 完全免费:对于公开仓库,每月有2000分钟的免费额度。一个每天运行1分钟的签到任务,一个月仅消耗约30分钟,绰绰有余。私有仓库也有一定免费额度。
- 环境可靠:GitHub提供稳定、干净的
ubuntu-latest等标准运行环境,无需担心系统环境差异。 - 集成度高:代码、配置、执行日志全部在GitHub仓库内管理,版本清晰,协作方便。
- 功能强大:不仅仅是定时触发,还可以由
push、pull_request等事件触发,方便调试和扩展。
因此,对于这类轻量级、周期性、且希望零成本、易维护的自动化任务,GitHub Actions几乎是目前的最优解。
2.2 验证码处理方案选型
验证码是自动签到最大的拦路虎。处理验证码通常有以下几种思路,我们需要根据目标网站验证码的复杂程度进行选择:
- OCR识别:适用于简单的数字、字母扭曲、干扰线较少的图片验证码。这是成本最低、实现最快的方案。
- 机器学习/深度学习模型:适用于复杂的字符粘连、扭曲变形、背景干扰强的验证码。需要收集数据、训练模型,门槛较高。
- 第三方打码平台:调用商业API,由人工或高精度模型进行识别,按次收费。识别率高,但会产生费用,且依赖外部服务。
- 绕过策略:检查是否有其他无需验证码的API接口可以完成签到,或者验证码是否在首次登录后的Cookie有效期内不再校验。
对于大多数个人网站的签到场景,验证码通常不会设计得极其复杂(否则用户体验太差),因此方案1(OCR)往往是首选。如果OCR失败,再考虑方案4(寻找替代接口),最后才是方案3。方案2对于一次性任务来说投入产出比太低。
在本项目中,我将以最通用的OCR方案为例进行详解,并会分享当OCR失效时的排查思路和备选方案。
2.3 整体架构设计
整个项目的运行流程可以概括为以下几步,这构成了我们编写GitHub Actions工作流和本地脚本的基本逻辑:
- 定时触发:GitHub Actions 根据设定的Cron表达式(如
0 0 * * *代表每天UTC时间0点)启动一个虚拟机运行器。 - 环境准备:运行器拉取我们的代码仓库,并按照我们的配置安装必要的环境,如Python、Chrome浏览器(用于模拟浏览器行为)等。
- 执行签到脚本:运行我们编写好的Python脚本。脚本的核心任务是: a. 模拟访问目标网站登录页面。 b. 定位并下载验证码图片。 c. 调用OCR库识别验证码。 d. 填充识别结果、用户名、密码,并提交表单。 e. 检查返回结果,判断签到是否成功。
- 结果通知:将脚本执行的成功/失败结果,通过邮件、钉钉、Telegram Bot或GitHub Issues等方式通知我们。
- 日志留存:整个过程的详细日志会保存在GitHub Actions的运行记录中,方便日后排查问题。
3. 环境与工具准备详解
3.1 创建GitHub仓库与敏感信息保护
首先,你需要在GitHub上创建一个新的仓库(Public或Private均可)。代码将存放在这里。
一个至关重要的安全步骤是:如何处理账号密码等敏感信息?绝对不要将用户名、密码、API密钥等明文写在代码或配置文件中!一旦提交到公开仓库,这些信息就泄露了。
GitHub提供了Secrets功能来解决这个问题。它允许你在仓库设置中存储加密的敏感信息,然后在GitHub Actions工作流文件中通过${{ secrets.KEY_NAME }}的方式引用,在运行时注入环境变量。这样,敏感信息只会出现在GitHub的服务器端,不会暴露在代码历史记录里。
实操心得:即使你的仓库是私有的,也强烈建议使用Secrets。这是一个良好的安全习惯。你可以为签到任务创建如
SITE_USERNAME、SITE_PASSWORD等Secret。
3.2 核心工具链选择:Selenium与OCR
我们的Python脚本需要完成“模拟浏览器”和“识别图片”两大任务。
浏览器自动化:SeleniumSelenium是Web自动化测试的标准工具,它可以驱动真实的浏览器(如Chrome, Firefox)进行点击、输入、跳转等操作,完美模拟人类行为。相比于
requests库直接发送HTTP请求,Selenium能更好地处理由JavaScript动态生成的内容(比如很多验证码图片的src就是JS生成的),虽然速度稍慢,但成功率更高。- 配套工具:我们需要安装
selenium库,以及对应的浏览器驱动chromedriver。幸运的是,GitHub Actions的常用环境里通常已经预装了Chrome,我们只需要用apt安装chromedriver即可。
- 配套工具:我们需要安装
验证码识别:Tesseract与pytesseractTesseract是一个开源的OCR引擎,由Google维护,识别英文和数字效果不错。
pytesseract是它的Python封装库。- 安装:在GitHub Actions环境中,我们需要安装Tesseract引擎及其语言包,然后在Python中安装
pytesseract和图像处理库Pillow。 - 局限性:Tesseract对简单的验证码有效,但如果验证码有复杂的背景、扭曲、粘连,识别率会急剧下降。这时就需要先对图片进行预处理。
- 安装:在GitHub Actions环境中,我们需要安装Tesseract引擎及其语言包,然后在Python中安装
3.3 图像预处理:提升OCR识别率的秘诀
直接拿到的验证码图片丢给Tesseract,效果往往很差。我们必须先对图片进行“清洗”。以下是我常用的预处理流水线,通过Pillow库实现:
- 灰度化:将彩色图转为灰度图,减少颜色维度。
image.convert('L') - 二值化:设定一个阈值,将灰度图转为纯黑白图,突出字符。
point(lambda x: 0 if x < 128 else 255, '1')。阈值128需要根据具体图片调整。 - 降噪:去除孤立的黑白点。可以使用中值滤波
ImageFilter.MedianFilter()或腐蚀膨胀等形态学操作(需OpenCV)。 - 裁剪:如果验证码图片周围有大量空白或干扰边框,可以将其裁剪掉,只保留字符区域。
预处理没有标准答案,需要你下载几个目标网站的验证码图片,用图像编辑软件或写个小脚本反复尝试不同的参数组合,观察处理后的效果,直到字符清晰、背景干净为止。
避坑指南:很多验证码为了防OCR,会增加干扰线或干扰点。对于细小的干扰点,二值化配合降噪通常能解决。对于颜色与字符接近的干扰线,可以尝试在灰度化前,先分析图片的RGB或HSV通道,找到验证码字符与背景/干扰线差异最大的那个通道进行提取,这招往往有奇效。
4. GitHub Actions工作流配置实战
4.1 编写核心工作流文件
在你的GitHub仓库根目录下创建.github/workflows/auto-signin.yml文件。YAML的语法缩进非常严格,建议使用空格而非Tab。
name: Daily Auto Sign-in # 工作流的名称,会在Actions页面显示 on: schedule: # 每天UTC时间0点(北京时间早上8点)运行。Cron表达式在线工具很多,可以方便生成。 - cron: '0 0 * * *' workflow_dispatch: # 允许手动触发,方便测试 push: branches: [ main ] # 当代码推送到main分支时也运行,用于调试 jobs: signin: runs-on: ubuntu-latest # 使用最新的Ubuntu系统作为运行环境 steps: # 步骤1:检出代码 - name: Checkout repository uses: actions/checkout@v4 # 步骤2:设置Python环境 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.10' # 指定Python版本 # 步骤3:安装系统依赖(Chrome, Tesseract等) - name: Install system dependencies run: | sudo apt-get update sudo apt-get install -y chromium-browser chromium-chromedriver tesseract-ocr tesseract-ocr-eng tesseract-ocr-chi-sim # 安装Chrome、驱动、Tesseract及中英文语言包 # 步骤4:安装Python依赖包 - name: Install Python dependencies run: | python -m pip install --upgrade pip pip install selenium pillow pytesseract requests # 步骤5:执行签到脚本,并传入Secrets作为环境变量 - name: Run sign-in script env: # 将仓库Secrets注入为环境变量,脚本中可以用 os.environ 读取 USERNAME: ${{ secrets.SITE_USERNAME }} PASSWORD: ${{ secrets.SITE_PASSWORD }} run: python scripts/auto_signin.py # 假设脚本放在 scripts 目录下 # 步骤6:(可选)发送通知,这里以生成一个GitHub Issue为例 - name: Create Issue on Failure if: failure() # 只有当前面的步骤失败时,才运行此步骤 uses: actions/github-script@v6 with: script: | github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: `自动签到任务失败 - ${new Date().toLocaleString()}`, body: `签到脚本执行失败,请检查日志。\n\n工作流运行链接:${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}` })4.2 Cron表达式与时区陷阱
GitHub Actions的schedule使用的是协调世界时(UTC)。如果你在中国,需要将你的本地时间转换为UTC时间。例如,你想在北京时间早上8点执行,那么UTC时间就是0 0 * * *(即UTC 0点)。
你可以使用schedule语法来设置更复杂的定时,比如0 8,20 * * *表示每天UTC时间8点和20点各执行一次。务必使用在线Cron表达式生成器进行验证。
注意事项:GitHub Actions的定时触发并非毫秒级精确。官方文档说明,它可能在预定时间后的几分钟内触发。这对于签到任务来说通常可以接受。如果你需要非常精确的执行,可能需要考虑其他方案。
4.3 依赖管理与缓存优化
上面的工作流每次运行都会从头安装所有依赖,这可能会消耗一些时间(1-2分钟)。为了加速执行,我们可以利用GitHub Actions的缓存功能。
可以为Python的pip包和系统的apt包设置缓存。但需要注意的是,缓存键(key)的设计要合理,当依赖文件(如requirements.txt)或系统版本变化时,缓存应自动失效并更新。
# 在“安装Python依赖包”步骤前加入缓存步骤示例 - name: Cache pip packages uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- # 在“安装系统依赖”步骤前加入缓存步骤示例(谨慎使用,因为系统包更新不频繁但体积大) - name: Cache apt packages uses: actions/cache@v3 with: path: /var/cache/apt/archives key: ${{ runner.os }}-apt-${{ hashFiles('**/apt-packages.txt') }} # 需要一个记录包列表的文件对于轻量级任务,是否缓存带来的时间节省可能并不明显,但了解这个机制对构建复杂工作流很有帮助。
5. 签到脚本开发与核心代码解析
现在,我们来编写核心的Python脚本scripts/auto_signin.py。这个脚本将完成所有的自动化操作。
5.1 脚本结构与基础配置
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import time import logging from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException, NoSuchElementException import pytesseract from PIL import Image, ImageFilter, ImageEnhance import requests from io import BytesIO # 配置日志,方便在GitHub Actions中查看详细过程 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) # 从环境变量读取敏感信息(在GitHub Actions中通过Secrets注入) USERNAME = os.environ.get('SITE_USERNAME') PASSWORD = os.environ.get('SITE_PASSWORD') LOGIN_URL = 'https://example.com/login' # 替换为目标网站登录页地址 SIGNIN_URL = 'https://example.com/signin' # 替换为目标网站签到页地址,如果和登录不同 if not USERNAME or not PASSWORD: logger.error("未找到用户名或密码环境变量,请检查GitHub Secrets设置。") exit(1) def init_driver(): """初始化Selenium WebDriver,配置无头模式等选项""" options = webdriver.ChromeOptions() options.add_argument('--headless') # 无头模式,不显示图形界面,适合服务器 options.add_argument('--no-sandbox') # 解决在容器内运行的权限问题 options.add_argument('--disable-dev-shm-usage') # 解决共享内存问题 options.add_argument('--disable-gpu') # 禁用GPU,在无头模式下有时需要 options.add_argument('--window-size=1920,1080') # 设置窗口大小,确保元素正常加载 # 注意:在GitHub Actions环境中,chromedriver通常已在PATH中 driver = webdriver.Chrome(options=options) driver.implicitly_wait(10) # 设置隐式等待时间(秒) return driver5.2 验证码获取、预处理与识别函数
这是整个脚本的技术核心,其效果直接决定成功率。
def get_and_recognize_captcha(driver, captcha_element): """ 从页面中获取验证码图片元素,进行预处理,然后识别。 :param driver: WebDriver实例 :param captcha_element: 验证码图片的WebElement对象或定位器 :return: 识别出的验证码字符串 """ try: # 1. 获取验证码图片的src属性(可能是Base64或URL) if isinstance(captcha_element, tuple): # 如果传入的是定位器,如 (By.ID, 'captcha_img') img_element = WebDriverWait(driver, 10).until( EC.presence_of_element_located(captcha_element) ) else: img_element = captcha_element img_src = img_element.get_attribute('src') logger.info(f"获取到验证码图片源: {img_src[:50]}...") # 只打印前50字符 # 2. 下载图片 if img_src.startswith('data:image'): # 处理Base64格式的图片 import base64 header, data = img_src.split(',', 1) image_data = base64.b64decode(data) else: # 处理URL格式的图片,注意可能需要处理相对路径和Cookie session = requests.Session() # 将浏览器的Cookie同步到requests session,用于需要登录态才能访问的验证码 for cookie in driver.get_cookies(): session.cookies.set(cookie['name'], cookie['value']) response = session.get(img_src if img_src.startswith('http') else driver.current_url.rsplit('/', 1)[0] + '/' + img_src) image_data = response.content # 3. 使用Pillow打开图片并进行预处理 image = Image.open(BytesIO(image_data)) # 保存原始图片用于调试(可选) # image.save('captcha_raw.png') # 预处理流水线开始 # a. 转为灰度图 image = image.convert('L') # b. 提高对比度(根据实际情况调整因子) enhancer = ImageEnhance.Contrast(image) image = enhancer.enhance(2.0) # 增强2倍对比度 # c. 二值化(阈值需要根据具体图片调整,这里是一个示例) threshold = 150 image = image.point(lambda x: 0 if x < threshold else 255) # d. 降噪(中值滤波) image = image.filter(ImageFilter.MedianFilter(size=3)) # e. 可选:裁剪掉多余白边 # bbox = image.getbbox() # if bbox: # image = image.crop(bbox) # 保存处理后的图片用于调试(可选) # image.save('captcha_processed.png') # 4. 使用Tesseract进行OCR识别 # 配置Tesseract参数,例如只识别数字:`--psm 7 digits` # psm 7 表示将图像视为单行文本 custom_config = r'--oem 3 --psm 7 -c tessedit_char_whitelist=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' captcha_text = pytesseract.image_to_string(image, config=custom_config) captcha_text = captcha_text.strip() # 去除首尾空白字符 logger.info(f"验证码识别结果: '{captcha_text}'") return captcha_text except Exception as e: logger.error(f"验证码识别过程出错: {e}") # 可以在这里加入重试逻辑,或者返回空字符串/None触发后续处理 return None5.3 完整的签到流程整合
将上述函数组合起来,形成完整的自动化流程。
def perform_signin(): """执行完整的签到流程""" driver = None try: driver = init_driver() logger.info("浏览器初始化成功。") # Step 1: 访问登录页面 logger.info(f"正在访问登录页面: {LOGIN_URL}") driver.get(LOGIN_URL) time.sleep(2) # 等待页面加载,可配合WebDriverWait优化 # Step 2: 定位页面元素(这里需要你根据目标网站的实际HTML结构修改) # 使用浏览器的开发者工具(F12)查看元素的id, name, class, xpath等。 username_input = driver.find_element(By.ID, 'username') # 替换为实际的元素定位方式 password_input = driver.find_element(By.ID, 'password') captcha_img = driver.find_element(By.ID, 'captchaImage') # 验证码图片元素 captcha_input = driver.find_element(By.ID, 'captcha') submit_button = driver.find_element(By.XPATH, '//button[@type="submit"]') # Step 3: 识别验证码 captcha_text = get_and_recognize_captcha(driver, captcha_img) if not captcha_text: logger.error("验证码识别失败,退出。") # 可以尝试刷新验证码重试 refresh_btn = driver.find_element(By.LINK_TEXT, '换一张') refresh_btn.click() time.sleep(1) captcha_text = get_and_recognize_captcha(driver, captcha_img) if not captcha_text: raise ValueError("重试后验证码识别仍失败。") # Step 4: 填写表单并提交 username_input.send_keys(USERNAME) password_input.send_keys(PASSWORD) captcha_input.send_keys(captcha_text) time.sleep(0.5) # 短暂停顿,模拟人类操作 submit_button.click() # Step 5: 等待登录后页面跳转或出现成功元素,并检查签到状态 # 方法一:等待特定元素出现(例如用户昵称) try: WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, 'user-nickname')) ) logger.info("登录成功!") except TimeoutException: logger.warning("登录后预期元素未出现,可能登录失败或页面结构不同。") # 方法二:如果签到是独立页面,则导航过去 if SIGNIN_URL: driver.get(SIGNIN_URL) time.sleep(2) # 查找并点击签到按钮 signin_btn = driver.find_element(By.XPATH, '//a[contains(text(), "签到")]') signin_btn.click() time.sleep(2) # 检查签到成功提示(根据网站实际提示修改) success_msg = driver.find_element(By.ID, 'success-message') if "签到成功" in success_msg.text: logger.info("每日签到成功!") else: logger.warning("签到操作完成,但未检测到明确成功提示。") # Step 6: 可选,截图保存作为凭证 driver.save_screenshot('signin_success.png') logger.info("流程执行完毕,已截图。") except NoSuchElementException as e: logger.error(f"未在页面上找到预期元素,可能是网站结构已更改: {e}") if driver: driver.save_screenshot('error_element_not_found.png') except TimeoutException as e: logger.error(f"页面加载或元素等待超时: {e}") except Exception as e: logger.error(f"签到流程执行过程中发生未知错误: {e}", exc_info=True) # exc_info会打印详细堆栈 finally: if driver: driver.quit() logger.info("浏览器已关闭。") if __name__ == '__main__': perform_signin()6. 调试、监控与高级技巧
6.1 本地调试与问题排查
在将脚本提交到GitHub之前,强烈建议在本地进行调试。
- 环境准备:在本地安装Python、Chrome、chromedriver(需与Chrome版本匹配)、Tesseract。
- 模拟运行:在本地命令行运行
python scripts/auto_signin.py。确保USERNAME和PASSWORD已设置为环境变量(如export SITE_USERNAME='your_name')。 - 禁用无头模式:在
init_driver()函数中,注释掉options.add_argument('--headless')这一行。这样浏览器窗口会弹出来,你可以直观地看到脚本每一步的操作,这对于调试元素定位和验证码识别流程至关重要。 - 保存中间图片:利用代码中注释掉的
image.save(...)功能,将原始和处理后的验证码图片保存下来,用肉眼检查预处理效果,调整阈值、对比度等参数。 - 使用
pdb或IDE调试:在关键步骤设置断点,检查变量状态。
6.2 GitHub Actions日志分析与监控
提交代码后,GitHub Actions会自动运行。你需要密切关注其运行日志。
- 查看日志:在仓库的
Actions标签页下,点击最近的工作流运行,可以查看每一步的详细输出。 - 排查失败:如果运行失败,日志会显示错误信息。常见问题包括:
- 依赖安装失败:网络问题或包名错误。
- 元素定位失败:网站HTML结构变化,需要更新脚本中的定位器(如
By.ID的值)。这是脚本失效的最常见原因。 - 验证码识别率低:OCR返回空字符串或错误结果。需要优化预处理参数,或者考虑验证码是否已升级。
- 超时:网络缓慢或网站响应慢,可以适当增加
WebDriverWait和time.sleep的等待时间。
6.3 提升稳定性的高级策略
- 重试机制:对于网络请求、验证码识别、点击按钮等可能瞬时失败的操作,加入重试逻辑(如
retrying库)。 - 验证码识别备用方案:
- 多引擎备用:除了Tesseract,可以集成
ddddocr(一个基于深度学习的开源OCR库,对某些验证码效果更好)作为备选。先尝试A,失败后尝试B。 - 人工兜底:如果连续多次识别失败,可以将验证码图片和上下文信息通过通知(如邮件)发送给你,进行人工识别,然后将结果通过另一个接口回传给脚本。这需要更复杂的架构。
- 多引擎备用:除了Tesseract,可以集成
- 心跳与健康检查:可以定期(比如每周一次)运行一个简单的“检查登录状态”的任务,确保Cookie未失效。如果失效,则触发完整的登录流程更新Cookie,并保存到GitHub Secrets或仓库的加密文件中(需谨慎)。
- 使用更稳定的定位方式:优先使用
id、name定位,其次是用css selector,最后才是xpath。xpath虽然强大,但容易因页面微小变动而失效。
6.4 通知渠道扩展
除了示例中失败时创建GitHub Issue,你还可以集成更及时的通知:
- 邮件通知:使用
actions/setup-python安装smtplib库,配置邮箱SMTP信息(通过Secrets存储),在任务完成或失败时发送邮件。 - 钉钉/飞书群机器人:在脚本最后,通过
requests库向群机器人的Webhook地址发送一个POST请求,包含执行结果。 - Server酱、PushPlus等推送服务:这类服务提供了简单的API,可以将消息推送到微信。
将通知步骤放在工作流文件的最后,根据if: success()或if: failure()条件来触发不同的通知内容。
7. 常见问题与排查技巧实录
即使按照上述步骤操作,在实际部署中你仍可能会遇到各种问题。下面是我在实践中总结的一些典型问题及其解决方法,整理成速查表,方便你遇到问题时快速对照。
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
GitHub Actions 运行失败,报错Chrome failed to start | Chrome或chromedriver未正确安装,或缺少依赖库。 | 1. 检查工作流中Install system dependencies步骤的日志,确认chromium-browser和chromium-chromedriver安装成功。2. 确保 options.add_argument('--no-sandbox')和--disable-dev-shm-usage已添加,这对容器环境至关重要。3. 尝试指定chromedriver路径: driver = webdriver.Chrome(executable_path='/usr/bin/chromedriver', options=options)。 |
| 脚本在本地成功,在Actions上失败,提示找不到元素 | 1. 页面加载速度慢,元素还未出现。 2. 网站检测到自动化脚本,返回了不同的页面结构。 | 1.增加等待时间:将time.sleep(2)适当加长,或更优解是使用WebDriverWait配合EC.presence_of_element_located。2.添加User-Agent:在ChromeOptions中添加 options.add_argument('user-agent=Mozilla/5.0 ...')模拟真实浏览器。3.禁用自动化特征:添加 options.add_experimental_option("excludeSwitches", ["enable-automation"])和options.add_experimental_option('useAutomationExtension', False)。 |
| 验证码识别结果始终为空或错误百出 | 1. 图片预处理参数不适合当前验证码。 2. 验证码图片本身是动态生成的,每次请求的图片链接不变(缓存)。 3. Tesseract语言包未安装。 | 1.本地调试预处理:在本地保存图片,手动调整灰度化、二值化的阈值、对比度增强因子等,直到肉眼看着清晰。 2.处理缓存:在获取验证码图片的URL后附加一个随机参数,如 img_src + '?t=' + str(time.time()),强制浏览器获取新图片。3.检查语言包:确保工作流中安装了 tesseract-ocr-eng(英文)和tesseract-ocr-chi-sim(简体中文),如果验证码含中文必须安装后者。 |
| 登录提交后,页面提示“验证码错误” | 1. OCR识别错误。 2. 网站有防重放机制,验证码一次有效,识别过程耗时过长导致过期。 3. 提交的表单数据缺少隐藏字段(如 csrf_token)。 | 1.提高识别率:优化预处理,或引入备用OCR引擎。 2.加速流程:减少不必要的 sleep,识别后立即提交。3.检查网络请求:在本地有浏览器界面时,使用开发者工具的“网络(Network)”选项卡,记录一次手动登录的正确请求,对比你的脚本提交的数据(特别是所有 input字段),查漏补缺。 |
| 任务偶尔成功,大部分时间失败 | 1. 网站服务器不稳定,响应超时。 2. 存在IP访问频率限制。 3. 会话(Cookie)管理问题。 | 1.加入重试:在网络请求和关键操作步骤外包裹重试装饰器。 2.降低频率:如果定时任务太密集,考虑拉长执行间隔。 3.保持会话:确保 driver实例在整个流程中未意外重启,所有操作在同一个driver上下文中进行,以维持Cookie。 |
| 如何知道签到是否真的成功了? | 脚本缺乏明确的成功校验逻辑。 | 在提交签到请求后,主动寻找成功提示。例如: 1. 等待某个包含“成功”、“获得XX积分”等文本的元素出现。 2. 检查页面URL是否跳转到特定成功页面。 3. 在关键步骤后截图( driver.save_screenshot),人工复查Actions的运行日志和截图。 |
终极调试技巧:当所有逻辑都正确但脚本就是不工作时,在Actions工作流中临时禁用无头模式并启用VNC(虽然GitHub Actions原生不支持直接显示界面,但可以通过
xvfb等虚拟帧缓冲器配合截图)。更简单粗暴的方法是:在脚本的每一个关键步骤之后(如打开页面、找到元素、识别验证码、提交表单),都用driver.save_screenshot('step1.png')保存一张截图。然后去Actions的日志里下载这些截图,像看连环画一样复盘整个自动化过程,你能清晰地看到脚本“眼中”的页面是什么样子,问题往往就藏在这些截图里。
通过这个项目,你不仅学会了一个具体的自动化签到技能,更重要的是掌握了一套基于GitHub Actions的通用定时任务解决方案,以及处理Web自动化和简单验证码识别的完整方法论。这套方法稍加修改,就能应用到数据抓取、监控报警、定期备份等无数场景中。自动化不是为了偷懒,而是为了把宝贵的时间和精力从重复劳动中解放出来,投入到更有创造性的工作中去。