431 lines
17 KiB
Python
431 lines
17 KiB
Python
# Source: https://github.com/LinYuanovo/AutoTaskScripts/blob/main/web/%E5%98%A4%E5%98%A4%E6%80%AA%E4%B9%8B%E5%AE%B6.py
|
||
# Raw: https://raw.githubusercontent.com/LinYuanovo/AutoTaskScripts/main/web/%E5%98%A4%E5%98%A4%E6%80%AA%E4%B9%8B%E5%AE%B6.py
|
||
# Repo: LinYuanovo/AutoTaskScripts
|
||
# Path: web/嘤嘤怪之家.py
|
||
# UploadedAt: 2025-08-27T15:31:08+08:00
|
||
# SHA256: 9e2e2ee0614b254534faf4e169de569e3d9bbc058a01ef97defe9b330fc9dd90
|
||
# Category: web版/账密
|
||
# Evidence: web/H5关键词 + username/password/login
|
||
|
||
"""
|
||
作者: 临渊
|
||
日期: 2025/6/8
|
||
name: 嘤嘤怪之家
|
||
入口: 网站 (https://yyg.app/)
|
||
功能: 登录、签到、评论(每日上限30积分)
|
||
变量: yyg='账号&密码' 多个账号用换行分割
|
||
DDDD_OCR_URL (dddd_ocr地址)
|
||
定时: 一天两次
|
||
cron: 10 9,10 * * *
|
||
------------更新日志------------
|
||
2025/6/8 V1.0 初始化,完成签到功能
|
||
2025/6/10 V1.1 添加评论功能
|
||
2025/6/11 V1.2 优化代码结构,使用session管理cookie,添加查询积分功能(不一定成功)
|
||
2025/7/23 V1.3 更新域名
|
||
2025/7/28 V1.4 修改头部注释,以便拉库
|
||
2025/8/27 V1.5 增加尝试获取最新域名
|
||
"""
|
||
|
||
import requests
|
||
import os
|
||
import re
|
||
import urllib.parse
|
||
import time
|
||
import random
|
||
import logging
|
||
import traceback
|
||
from datetime import datetime
|
||
from bs4 import BeautifulSoup
|
||
|
||
DDDD_OCR_URL = os.getenv("DDDD_OCR_URL") or "" # dddd_ocr地址
|
||
DEFAULT_GUIDE_URL = "https://yyg.autos/" # 默认发布地址
|
||
DEFAULT_HOST = "yyg.app" # 默认域名
|
||
|
||
class AutoTask:
|
||
def __init__(self, site_name):
|
||
"""
|
||
初始化自动任务类
|
||
:param site_name: 站点名称,用于日志显示
|
||
"""
|
||
self.site_name = site_name
|
||
self.setup_logging()
|
||
|
||
def setup_logging(self):
|
||
"""
|
||
配置日志系统
|
||
"""
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format='%(asctime)s - %(levelname)s\t- %(message)s',
|
||
datefmt='%Y-%m-%d %H:%M:%S',
|
||
handlers=[
|
||
# logging.FileHandler(f'{self.site_name}_{datetime.now().strftime("%Y%m%d")}.log', encoding='utf-8'), # 保存日志
|
||
logging.StreamHandler()
|
||
]
|
||
)
|
||
|
||
def check_cookie(self):
|
||
"""
|
||
检查cookie
|
||
:return: 用户名和密码
|
||
"""
|
||
try:
|
||
# 从环境变量获取cookie
|
||
cookie = os.getenv(f"yyg")
|
||
if not cookie:
|
||
logging.error(f"[检查cookie]没有找到环境变量yyg")
|
||
return
|
||
# 多个账号用换行分割
|
||
cookies = cookie.split('\n')
|
||
for cookie in cookies:
|
||
# 解析cookie字符串,提取用户名和密码
|
||
username, password = cookie.split('&')
|
||
yield username, password
|
||
except Exception as e:
|
||
logging.error(f"[检查cookie]发生错误: {str(e)}\n{traceback.format_exc()}")
|
||
raise
|
||
|
||
def get_host(self):
|
||
"""
|
||
获取最新域名
|
||
:return: 域名
|
||
"""
|
||
try:
|
||
url = DEFAULT_GUIDE_URL
|
||
response = requests.get(url)
|
||
response.encoding = 'utf-8'
|
||
soup = BeautifulSoup(response.text, 'html.parser')
|
||
links = soup.find_all('a')
|
||
for link in links:
|
||
link_text = link.get_text(strip=True)
|
||
if re.search(r'访问最新域名', link_text):
|
||
href_value = link.get('href')
|
||
if href_value.startswith("http"):
|
||
host = href_value.split("//")[1]
|
||
logging.info(f"[获取最新域名]最新域名: {host}")
|
||
return host
|
||
else:
|
||
return href_value
|
||
return DEFAULT_HOST
|
||
except Exception as e:
|
||
logging.error(f"[获取最新域名]发生错误: {str(e)}\n{traceback.format_exc()}")
|
||
return DEFAULT_HOST
|
||
|
||
def get_captcha_img(self, host, session, type):
|
||
"""
|
||
获取验证码图片
|
||
:param host: 域名
|
||
:param session: 会话对象
|
||
:param type: 验证码类型
|
||
:return: 验证码base64
|
||
"""
|
||
max_retries = 3 # 最大重试次数
|
||
retry_count = 0
|
||
|
||
while retry_count < max_retries:
|
||
try:
|
||
url = f"https://{host}/wp-content/themes/zibll/action/captcha.php?type=image&id={type}"
|
||
headers = {
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
|
||
}
|
||
response = session.get(url, headers=headers).json()
|
||
if 'img' in response:
|
||
return response['img']
|
||
else:
|
||
retry_count += 1
|
||
logging.warning(f"[获取验证码]第{retry_count}次获取失败,正在重试...")
|
||
continue
|
||
except Exception as e:
|
||
retry_count += 1
|
||
if retry_count >= max_retries:
|
||
logging.error(f"[获取验证码]重试次数已达上限")
|
||
return None
|
||
continue
|
||
|
||
return None
|
||
|
||
def get_captcha_text(self, img):
|
||
"""
|
||
获取验证码文字
|
||
:param img: 验证码图片base64
|
||
:return: 验证码
|
||
"""
|
||
try:
|
||
url = DDDD_OCR_URL
|
||
payload = {
|
||
'image': img
|
||
}
|
||
response = requests.post(url, data=payload).json()
|
||
if response['code'] == 200:
|
||
return response['data']
|
||
else:
|
||
logging.error(f"[获取验证码]发生错误: {response['message']}")
|
||
return None
|
||
except Exception as e:
|
||
logging.error(f"[获取验证码]发生错误: {str(e)}\n{traceback.format_exc()}")
|
||
return None
|
||
|
||
def login_in(self, host, username, password, text, session):
|
||
"""
|
||
登录
|
||
:param host: 域名
|
||
:param username: 用户名
|
||
:param password: 密码
|
||
:param text: 验证码
|
||
:param session: 会话对象
|
||
:return: 是否成功
|
||
"""
|
||
max_retries = 3 # 最大重试次数
|
||
retry_count = 0
|
||
|
||
while retry_count < max_retries:
|
||
try:
|
||
url = f"https://{host}/wp-admin/admin-ajax.php"
|
||
headers = {
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36',
|
||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||
}
|
||
payload = f"username={username}&password={password}&canvas_yz={text}&remember=forever&action=user_signin"
|
||
response = session.post(url, headers=headers, data=payload)
|
||
response_json = response.json()
|
||
|
||
if response_json['error'] == 0:
|
||
logging.info(f"[登录]成功")
|
||
return True
|
||
else:
|
||
logging.error(f"[登录]失败: {response_json['msg']}")
|
||
if "重新获取" in response_json['msg'] or "请输入图形验证码" in response_json['msg']:
|
||
retry_count += 1
|
||
if retry_count >= max_retries:
|
||
logging.error(f"[登录]验证码重试次数已达上限")
|
||
return False
|
||
|
||
img = self.get_captcha_img(host, session, "img_yz_signin")
|
||
while not img:
|
||
img = self.get_captcha_img(host, session, "img_yz_signin")
|
||
text = self.get_captcha_text(img)
|
||
if not text:
|
||
logging.error(f"[登录]获取验证码文字失败")
|
||
return False
|
||
continue
|
||
return False
|
||
|
||
except requests.RequestException as e:
|
||
logging.error(f"[登录]发生网络错误: {str(e)}\n{traceback.format_exc()}")
|
||
return False
|
||
except Exception as e:
|
||
logging.error(f"[登录]发生未知错误: {str(e)}\n{traceback.format_exc()}")
|
||
return False
|
||
|
||
return False
|
||
|
||
def sign_in(self, host, session):
|
||
"""
|
||
执行签到
|
||
:param host: 域名
|
||
:param session: 会话对象
|
||
:return: 是否签到成功
|
||
"""
|
||
try:
|
||
url = f"https://{host}/wp-admin/admin-ajax.php"
|
||
headers = {
|
||
'Host': host,
|
||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
|
||
}
|
||
payload = "action=user_checkin"
|
||
response = session.post(url, headers=headers, data=payload)
|
||
response_json = response.json()
|
||
# 处理响应
|
||
logging.info(f"[签到]{response_json['msg']}")
|
||
return response_json['msg']
|
||
except requests.RequestException as e:
|
||
logging.error(f"[签到]发生网络错误: {str(e)}\n{traceback.format_exc()}")
|
||
return False
|
||
except Exception as e:
|
||
logging.error(f"[签到]发生未知错误: {str(e)}\n{traceback.format_exc()}")
|
||
return False
|
||
|
||
def get_post_id(self, host, session, retry_count=0):
|
||
"""
|
||
获取帖子id
|
||
:param host: 域名
|
||
:param session: 会话对象
|
||
:param retry_count: 重试次数
|
||
:return: 帖子id列表
|
||
"""
|
||
try:
|
||
url = f"https://{host}/category/pcgame?orderby=modified" # 最新排列
|
||
headers = {
|
||
'Host': host,
|
||
'Referer': f"https://{host}/category/pcgame",
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
|
||
}
|
||
response = session.get(url, headers=headers)
|
||
soup = BeautifulSoup(response.text, 'html.parser')
|
||
|
||
post_ids = []
|
||
for i in range(3,7):
|
||
# 找到所有posts元素
|
||
links = soup.select(f'body > main > div > div > div:nth-child(2) > posts:nth-child({i}) > div.item-body.flex.xx.flex1.jsb > h2 > a')
|
||
if links: # 如果找到了链接
|
||
link = links[0] # 获取第一个链接
|
||
# 提取帖子ID
|
||
if link.get('href'):
|
||
# 从URL中提取ID (例如从 https://yyg.boats/14819.html 提取 14819)
|
||
post_id = link['href'].split('/')[-1].replace('.html', '')
|
||
post_ids.append(post_id)
|
||
|
||
# 如果没有获取到帖子ID且重试次数小于3次,则重试
|
||
if not post_ids and retry_count < 3:
|
||
logging.info(f"[获取帖子ID]未获取到帖子ID,第{retry_count + 1}次重试")
|
||
time.sleep(random.randint(5, 10))
|
||
return self.get_post_id(host, session, retry_count + 1)
|
||
elif not post_ids and retry_count >= 3:
|
||
post_ids = ['14818', '14817', '14816', '14815']
|
||
logging.warning(f"[获取帖子ID]三次未获取到帖子ID,使用默认帖子ID: {post_ids}")
|
||
|
||
logging.info(f"[获取帖子ID]成功获取到 {len(post_ids)} 个帖子ID: {post_ids}")
|
||
return post_ids
|
||
|
||
except requests.RequestException as e:
|
||
logging.error(f"[获取帖子ID]发生网络错误: {str(e)}\n{traceback.format_exc()}")
|
||
return []
|
||
except Exception as e:
|
||
logging.error(f"[获取帖子ID]发生未知错误: {str(e)}\n{traceback.format_exc()}")
|
||
return []
|
||
|
||
def submit_comment(self, host, session, captcha, post_id, retry_count=0):
|
||
"""
|
||
提交评论
|
||
:param host: 域名
|
||
:param session: 会话对象
|
||
:param captcha: 验证码
|
||
:param post_id: 帖子ID
|
||
:param retry_count: 重试次数
|
||
"""
|
||
try:
|
||
url = f"https://{host}/wp-admin/admin-ajax.php"
|
||
headers = {
|
||
'Host': host,
|
||
'Referer': f"https://{host}/{post_id}.html",
|
||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
|
||
}
|
||
payload = f"comment=%E6%84%9F%E8%B0%A2%E5%88%86%E4%BA%AB&canvas_yz={captcha}&comment_post_ID={post_id}&comment_parent=0&action=submit_comment"
|
||
response = session.post(url, headers=headers, data=payload)
|
||
response_json = response.json()
|
||
logging.info(f"[评论帖子{post_id}]{response_json['msg']}")
|
||
|
||
if "图形验证码错误" in response_json['msg']:
|
||
if retry_count >= 3:
|
||
logging.warning(f"[评论{post_id}]验证码错误重试次数已达上限,跳过此评论")
|
||
return False
|
||
|
||
logging.info(f"[评论帖子{post_id}]验证码错误,第{retry_count + 1}次重试")
|
||
submit_comment_captcha = self.get_captcha_img(host, session, "submit_comment")
|
||
while not submit_comment_captcha:
|
||
submit_comment_captcha = self.get_captcha_img(host, session, "submit_comment")
|
||
submit_comment_text = self.get_captcha_text(submit_comment_captcha)
|
||
if not submit_comment_text:
|
||
logging.error(f"[评论帖子{post_id}]获取验证码文字失败")
|
||
return False
|
||
return self.submit_comment(host, session, submit_comment_text, post_id, retry_count + 1)
|
||
|
||
return response_json['msg']
|
||
except Exception as e:
|
||
logging.error(f"[评论帖子{post_id}]发生未知错误: {str(e)}\n{traceback.format_exc()}")
|
||
return False
|
||
|
||
def get_user_balance(self, host, session):
|
||
"""
|
||
获取用户积分
|
||
:param host: 域名
|
||
:param session: 会话对象
|
||
:return: 用户积分
|
||
"""
|
||
try:
|
||
url = f"https://{host}/user/balance"
|
||
headers = {
|
||
'Host': host,
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
|
||
}
|
||
response = session.get(url, headers=headers)
|
||
# 正则匹配积分记录里第一个,即 积分: xxx
|
||
balance = re.search(r'积分: (\d+)', response.text)
|
||
if balance:
|
||
return balance.group(1)
|
||
else:
|
||
logging.error(f"[获取用户积分]未找到积分记录")
|
||
return 0
|
||
except Exception as e:
|
||
logging.error(f"[获取用户积分]发生未知错误: {str(e)}\n{traceback.format_exc()}")
|
||
return 0
|
||
|
||
def do_task(self, host, session):
|
||
"""
|
||
执行任务
|
||
:param host: 域名
|
||
:param session: 会话对象
|
||
"""
|
||
# 执行签到
|
||
self.sign_in(host, session)
|
||
# 获取帖子id
|
||
post_ids = self.get_post_id(host, session)
|
||
# 获取评论验证码图片(四次)
|
||
for post_id in post_ids:
|
||
captcha = self.get_captcha_img(host, session, "submit_comment")
|
||
while not captcha:
|
||
captcha = self.get_captcha_img(host, session, "submit_comment")
|
||
text = self.get_captcha_text(captcha)
|
||
if not text:
|
||
logging.error(f"[评论{post_id}]获取验证码文字失败")
|
||
continue
|
||
# 提交评论
|
||
self.submit_comment(host, session, text, post_id)
|
||
time.sleep(random.randint(16, 30))
|
||
# 获取用户积分
|
||
balance = self.get_user_balance(host, session)
|
||
logging.info(f"[账号]当前积分: {balance}")
|
||
|
||
def run(self):
|
||
"""
|
||
运行任务
|
||
"""
|
||
try:
|
||
logging.info(f"【{self.site_name}】开始执行任务")
|
||
host = self.get_host()
|
||
|
||
for index, (username, password) in enumerate(self.check_cookie(), 1):
|
||
logging.info("")
|
||
logging.info(f"------【账号{index}】开始执行任务------")
|
||
|
||
# 创建会话
|
||
session = requests.Session()
|
||
|
||
# 获取登录验证码图片
|
||
login_in_img = self.get_captcha_img(host, session, "img_yz_signin")
|
||
while not login_in_img:
|
||
login_in_img = self.get_captcha_img(host, session, "img_yz_signin")
|
||
# 获取登录验证码文字
|
||
login_in_text = self.get_captcha_text(login_in_img)
|
||
if not login_in_text:
|
||
logging.error(f"[{self.site_name}]获取登录验证码文字失败")
|
||
continue
|
||
|
||
# 登录
|
||
if self.login_in(host, username, password, login_in_text, session):
|
||
# 执行任务
|
||
self.do_task(host, session)
|
||
|
||
logging.info(f"------【账号{index}】执行任务完成------")
|
||
logging.info("")
|
||
|
||
except Exception as e:
|
||
logging.error(f"【{self.site_name}】执行过程中发生错误: {str(e)}\n{traceback.format_exc()}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
auto_task = AutoTask("嘤嘤怪之家")
|
||
auto_task.run()
|