2025年12月22日星期一

文叔叔签到任务脚本

1.购买服务器阿里云:服务器购买地址https://t.aliyun.com/U/55RK8C若失效,可用地址

1.购买服务器

阿里云:

服务器购买地址

https://t.aliyun.com/U/55RK8C

若失效,可用地址

https://www.aliyun.com/daily-act/ecs/activity_selection?source=5176.29345612&userCode=49hts92d

腾讯云:

https://curl.qcloud.com/wJpWmSfU

若失效,可用地址

https://cloud.tencent.com/act/cps/redirect?redirect=2446&cps_key=ad201ee2ef3b771157f72ee5464b1fea&from=console

华为云

https://activity.huaweicloud.com/cps.html?fromacct=64b5cf7cc11b4840bb4ed2ea0b2f4468&utm_source=V1g3MDY4NTY=&utm_medium=cps&utm_campaign=201905

2.部署教程

2024年最新青龙面板跑脚本教程(一)持续更新中

3.代码如下

import htmlimport osimport reimport sysimport timefrom io import BytesIOimport tracebackimport ddddocrimport requestsfrom PIL import Imagefrom selenium import webdriverfrom selenium.common import NoSuchElementExceptionfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.common.action_chains import ActionChainsfrom selenium.webdriver.common.by import By

def send(push_token, title, text):    requests.get(f"https://www.pushplus.plus/send?token={push_token}&title={title}&content={text}&template=html",                 proxies=proxies)

def hide_user(user):    user = str(user)    if re.match(r'\d{11}', user):  # 匹配11位纯数字        return user[:3] + '****' + user[7:]    elif re.match(r'\S+@\S+\.\S+', user):  # 匹配邮箱格式        at_index = user.find('@')        return user[:2] + '*' * (at_index - 2) + user[at_index:]    else:        return user

def captcha(element):    # 通过element获取image_url    page_source = element.parent.page_source    p = 'background-image: url\("(.*?)"\);'    re_url_result = re.findall(p, page_source)
    p2 = 'background-position: -(.*?)px -(.*?)px; background-size: (.*?)px (.*?)px; width: (.*?)px; height: (.*?)px; left: (.*?)px; top: (.*?)px;'    re_pix_result = re.findall(p2, page_source)
    p3 = 'style="width: (.*?)px; height: (.*?)px; position:'    re_pix2_result = re.findall(p3, page_source)
    background_image_url = ''    block_image_url = ''    for r in re_url_result:        temp_url = html.unescape(r)        if 'img_index=0' in temp_url:            block_image_url = temp_url        elif 'img_index=1' in temp_url:            background_image_url = temp_url
    if len(re_pix_result) != 0:        if len(re_pix_result[0]) == 8:            pos_x, pos_y, pix_x, pix_y, width, height, left, top = map(intmap(float, re_pix_result[0]))        else:            raise Exception(f'获取验证码大小失败, re_pix_result[0]: {re_pix_result[0]}')    else:        raise Exception(f'获取验证码大小失败, re_pix_result: {re_pix_result}')
    if len(re_pix2_result) != 0:        if len(re_pix2_result[0]) == 2:            pic_pix_x, pic_pix_y = map(intmap(float, re_pix2_result[0]))        else:            raise Exception('获取验证码大小失败')    else:        raise Exception('获取验证码大小失败')
    if background_image_url == '' or block_image_url == '':        raise Exception('获取验证码链接失败')    # 获取网络图片    # (response.content)    block_response = requests.get(block_image_url, proxies=proxies)    img_block = Image.open(BytesIO(block_response.content))    background_response = requests.get(background_image_url, proxies=proxies)    img_bg = Image.open(BytesIO(background_response.content))
    # pos_x, pos_y, pix_x, pix_y, width, height, left, top    # (*70, *247, *345, *313, *60, *60, 25, 63)    img_block = img_block.resize((pix_x, pix_y))    img_block = img_block.crop((pos_x, pos_y, pos_x + width, pos_y + height))
    img_bg = img_bg.resize((pic_pix_x, pic_pix_y))
    img_block_bytes = BytesIO()    img_block.save(img_block_bytes, format='PNG')    img_block_bytes = img_block_bytes.getvalue()
    img_bg_bytes = BytesIO()    img_bg.save(img_bg_bytes, format='PNG')    img_bg_bytes = img_bg_bytes.getvalue()
    det = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)    res = det.slide_match(img_block_bytes, img_bg_bytes, simple_target=True)    return ((res['target'][2] + res['target'][0]) // 2) - left - 30

def sign_wss(user, password, token, msgs: list, show_user_string: str):    chrome_options = Options()    # 浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败    if not debug_flag:        chrome_options.add_argument('--headless')    chrome_options.add_argument('disable-infobars')  # 取消显示信息栏(Chrome 正在受到自动软件的控制)    chrome_options.add_argument("--disable-blink-features=AutomationControlled")  # 禁用 Chrome 的自动化控制检测    # chrome_options.binary_location = "C:\Program Files\Google\Chrome\Application\chrome.exe"    chrome_options.add_argument('--no-sandbox')    chrome_options.add_argument('--disable-dev-shm-usage')    b = webdriver.Chrome(options=chrome_options)
    b.get('https://www.wenshushu.cn/signin')    time.sleep(2)    b.refresh()    b.implicitly_wait(10)    print("正在登陆...")    b.find_element(by=By.XPATH, value='//*[contains(text(),"密码登录")]').click()    time.sleep(1)    b.find_element(by=By.XPATH, value='//*[@placeholder="手机号 / 邮箱"]').send_keys(user)    time.sleep(1)    b.find_element(by=By.XPATH, value='//*[@placeholder="密码"]').send_keys(password)    time.sleep(1)    b.find_element(by=By.XPATH, value='//*[@type="submit"]').click()    time.sleep(1)
    b.refresh()    time.sleep(1)
    try:        print("关闭广告和新手任务中...")        b.find_element(by=By.CLASS_NAME, value="btn_close").click()        time.sleep(1)    except NoSuchElementException:        print("无广告或其他弹窗")        pass
    b.implicitly_wait(10)    print(f"{show_user_string} 正在打卡...")    # headless 模式下icondaka直接click()好像有异常,需要换个玩法    # b.find_element(by=By.CLASS_NAME, value="icondaka").click()    ele = b.find_element(by=By.CLASS_NAME, value="icondaka")    ActionChains(b).move_to_element(ele).click(ele).perform()
    b.implicitly_wait(10)    time.sleep(5)
    # 获取页面源码    htm = b.page_source    try:        if '今日已打卡' not in htm or '打卡成功' in htm:            # if b.find_element(by=By.CLASS_NAME, value='tc-fg-item'):            #     print('发现验证码', b.page_source)            # else:            #     print(b.page_source)            print('疑似存在验证码,正在处理验证码')            iframe = b.find_element(by=By.XPATH, value="//iframe[@id='tcaptcha_iframe_dy']")            b.switch_to.frame(iframe)            # 获取验证码滑块和背景图            element_block = b.find_element(by=By.CLASS_NAME, value='tc-fg-item')            # element_background_image = b.find_element(by=By.CLASS_NAME, value='tc-bg-img')            iv = captcha(element_block)            element_slider = b.find_element(by=By.CLASS_NAME, value='tc-slider-normal')
            # 模拟鼠标点击并拖动滑块            action = ActionChains(b)            action.click_and_hold(element_slider).move_by_offset(iv, 0).release().perform()            print('验证码处理完成')    except Exception as e:        # print(b.page_source)        print(traceback.format_exc())
    time.sleep(5)    b.implicitly_wait(10)    b.switch_to.default_content()    htm = b.page_source
    if '今日已打卡' in htm or 'Signed in today' in htm or '' in htm:        htm = htm.replace('\n''')        names = re.compile('class="m-title5">(.*?)</div>').findall(htm)        values = re.compile('class="re-num m-text9">(.*?)</div>').findall(htm)        result = ''        for i in range(len(names)):            if names[i] == '手气不好':                continue            result += names[i] + ':' + values[i] + '</br>'            print('%s:%s' % (names[i], values[i].strip()))        msg = (show_user_string + '文叔叔签到成功,', result)    else:        msg = (show_user_string + '文叔叔签到失败,', htm)        print(htm)    msgs.append(msg)
    b.close()

if __name__ == '__main__':    sys.stdout.reconfigure(encoding='UTF-8')
    proxies = {        'http'None,        'https'None    }
    users = os.environ.get('USER')    password = os.environ.get('PASSWORD')    push_token = os.environ.get('PUSH_MESSAGE')    show_user = os.environ.get('SHOW_USER')  # 0: 完全不显示(默认),1:显示部分(例如:131****1234),2:完全显示    debug_flag = os.environ.get('DEBUG')    if show_user is None:        show_user = 0
    if users is None:        exit()    if password is None:        exit()    if push_token is None:        push_token = ""    msgs = []    if debug_flag is None:        debug_flag = False    else:        debug_flag = True
    for user in users.split(';'):        show_user_string = ''        if str(show_user) == '1':            show_user_string = hide_user(user)        elif str(show_user) == '2':            show_user_string = user        retry = 0        while retry < 5:            success = True            try:                sign_wss(user, password, push_token, msgs, show_user_string)            except Exception as e:                print("签到{user}账户时出现异常:{error_message}".format(user=show_user_string, error_message=traceback.format_exc()))                print(f"已重试次数: {retry + 1}")                success = False            finally:                retry = retry + 1            if success:                break
    push_text = ''    for msg in msgs:        push_text = push_text + msg[0] + msg[1]
    send(push_token, '文叔叔签到结果', push_text)

解析

这是一个 文叔叔(wenshushu.cn)自动签到/打卡脚本,支持:

  1. 从环境变量读取账号、密码(可多账号)

  2. 使用 Selenium + Chrome(可无头) 登录文叔叔

  3. 点击"打卡"按钮完成签到

  4. 如果出现 腾讯滑块验证码(tcaptcha),就用 ddddocr 做滑块缺口匹配,计算拖动距离并拖动完成验证

  5. 解析签到结果(奖励名称/数值)汇总成 HTML 文本

  6. 通过 PushPlus 推送签到结果通知

  7. 每个账号支持最多 5 次重试,提高成功率

主要方法

1) send(push_token, title, text)

推送通知到 PushPlus:

  • 通过 GET 请求调用 PushPlus 的 send 接口

  • template=html,所以传入的 text 支持 HTML(如 </br> 换行)

2) hide_user(user)

账号脱敏展示(用于日志/通知):

  • 11 位手机号:131****1234

  • 邮箱:保留前两位 + **** + @xxx.com

  • 其他格式:原样返回

3) captcha(element)

这是脚本里最关键的"验证码识别"方法:计算滑块应该拖动的水平距离

它做的事情可以概括成 4 步:

  1. 从页面源码解析验证码图片 URL

    • img_index=0:滑块小图(缺口块)

    • img_index=1:背景大图

    • 从 background-image: url("...") 中提取两张图:

  2. 解析验证码布局参数

    • background-position / background-size / width / height / left / top 等,用于裁剪与缩放

    • 这些参数决定"从滑块原图裁哪一块"以及"背景图缩放到什么尺寸"

  3. 下载两张图片并做处理

    • requests.get() 拉取滑块图与背景图

    • PIL.Image 缩放、裁剪成可用于识别的 PNG bytes

  4. 用 ddddocr 做滑块匹配

    • DdddOcr(det=False, ocr=False).slide_match(...) 得到缺口位置

    • 最后计算出拖动距离(返回一个 x 偏移量),供 Selenium 拖拽使用

总结一句:captcha() 的输出就是"滑块要拖多少像素"。

4) sign_wss(user, password, token, msgs: list, show_user_string: str)

单个账号签到主流程(核心方法):

  • 启动 Chrome(根据 debug_flag 决定是否 headless)

  • 打开 https://www.wenshushu.cn/signin

  • 切换到"密码登录",输入账号密码,提交登录

  • 关闭可能出现的广告弹窗(找不到就忽略)

  • 点击 "打卡" 按钮(用 ActionChains 避免 headless 下 click 不稳定)

  • 判断页面是否已打卡成功:

    • 找到滑块块元素 .tc-fg-item → 调 captcha() 算距离

    • 找到滑块按钮 .tc-slider-normal → ActionChains 拖动

    • 如果疑似触发验证码:进入 iframe#tcaptcha_iframe_dy

  • 再次回到主页面,解析签到结果:

    • 用正则提取奖励项 m-title5 与数值 re-num m-text9

    • 拼装 HTML 并写入 msgs 列表(供最后汇总推送)

  • 关闭浏览器

脚本入口(__main__

  • 从环境变量读取:

    • USER:账号列表(; 分隔多账号)

    • PASSWORD:密码

    • PUSH_MESSAGE:PushPlus token

    • SHOW_USER:0不显示/1脱敏/2全显示

    • DEBUG:是否开启可视化(不加 headless)

  • 对每个账号最多重试 5 次执行 sign_wss()

  • 汇总所有账号的 msgs 生成一段 HTML

  • 调 send() 推送《文叔叔签到结果》



注意

本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。


历史脚本txt文件获取>>
服务器搭建,人工服务咨询>>

没有评论:

发表评论

史上比较全的AI出海工具清单(域名 / 建站 / AI API / SEO / 外链 / 收录 / 数据分析)

域名在哪里查?怎么买才不踩坑?Cloudflare 到底是干嘛的?Vercel 一键部署听起来很爽,结果环境变量没配对直接报错。站好不容易上线了,又卡在收录...这篇文章,我把日常真正在用的一套工具链整理出来了。 新手在刚开始准备出海的时候,大多数都会遇到同一个问题:不是不会...