注意:这篇文章上次更新于214天前,文章内容可能已经过时。
这是一个 2.0 版的免补签工具
from playwright.sync_api import sync_playwright
import json
import time
from bs4 import BeautifulSoup
from email.mime.text import MIMEText
from email.header import Header
from smtplib import SMTP_SSL
import traceback
class kq():
def __init__(self, username, password, receiver=None):
self.username = username
self.password = password
self.x_value = 0
if receiver:
self.receiver = receiver
def send_email(self, msg, receiver):
host_server = '' # 邮箱smtp服务器
user = '' # user 为发件人的邮箱号码
pwd = '' #pwd 为邮箱的授权码
sender_mail = '' #发件人的邮箱
#邮件的正文内容
mail_content = msg
#添加机器人签名
mail_sign = \
'''
\n
---
No need to reply!
Contact me: w-gx@qq.com
'''
#邮件标题
mail_title = '【打卡消息推送】'
#ssl登录
smtp = SMTP_SSL(host_server)
#set_debuglevel()是用来调试的。参数值为1表示开启调试模式,参数值为0关闭调试模式
smtp.set_debuglevel(0)
smtp.ehlo(host_server)
smtp.login(user, pwd)
msg = MIMEText(mail_content + mail_sign, "plain", 'utf-8')
msg["Subject"] = Header(mail_title, 'utf-8')
msg["From"] = sender_mail
msg["To"] = receiver
smtp.sendmail(sender_mail, receiver, msg.as_string())
smtp.quit()
def run(self, playwright):
retry = 0
browser = playwright.chromium.launch(headless=True)
context = browser.new_context()
# Open new page
page = context.new_page()
# Add response handler
page.on('response', lambda response: self.handle_response(response))
page.goto("")
user_input_box = page.locator("xpath=/html/body/div/form/div[1]/div/div/div/div[2]/div[2]/input")
pwd_input_box = page.locator("xpath=/html/body/div/form/div[1]/div/div/div/div[2]/div[3]/input")
user_input_box.fill(self.username)
pwd_input_box.fill(self.password)
login_button = page.locator("xpath=/html/body/div/form/div[1]/div/div/div/div[2]/div[4]/input")
login_button.click()
# Wait for navigation or action to complete
page.wait_for_timeout(5000)
while True:
x_percent = (360 - self.x_value) / 360
print(self.x_value)
# Wait for the slider and track elements to appear and calculate the move distance based on the track width and x_value percentage
slider_handle = page.wait_for_selector('.captcha-control-button')
slider_track = page.wait_for_selector('.captcha-control-wrap') # Assuming this is the slider track
# Get bounding box of slider handle and track
slider_handle_box = slider_handle.bounding_box()
print(slider_handle_box['width'])
slider_track_box = slider_track.bounding_box()
print(slider_track_box['width'])
# Calculating the move distance based on the track width and the percentage obtained from x_value
move_distance = slider_track_box['width'] * x_percent
# Starting point for the movement
start_x = slider_handle_box['x'] + slider_handle_box['width'] / 2
start_y = slider_handle_box['y'] + slider_handle_box['height'] / 2
# Ending X coordinate based on the calculated distance
end_x = start_x + move_distance - slider_handle_box['width'] / 2
# Simulate the drag and drop action
page.mouse.move(start_x, start_y)
page.mouse.down()
page.mouse.move(end_x, start_y, steps=20) # Use steps to simulate a more realistic drag action
page.mouse.up()
time.sleep(3)
try:
daka_button = page.locator("xpath=/html/body/div/body/div[2]/div[2]/div[2]/div[2]/a[1]")
daka_button.click()
break
except:
retry += 1
if retry >= 20:
print("Failed to click the daka button")
break
print("Verification failed, try again.")
continue
page.wait_for_timeout(5000)
msg = '【打卡成功】\n'
msg += self.parse_daka(page.content())
print(msg)
if self.receiver:
self.send_email(msg, self.receiver)
context.close()
browser.close()
def parse_daka(self, html_content):
# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(html_content, 'html.parser')
# 初始化结果列表
result = []
# 找到表格
table = soup.find('table', {'class': 'kq-message-table'})
# 获取表头中的所有th标签,用于列名称
headers = []
for th in table.find('thead').find_all('th'):
# 每个th标签,获取文本并消除空格和换行符
headers.append(' '.join(th.stripped_strings))
# 遍历表格的每一行
for row in table.find('tbody').find_all('tr'):
# 获取行中的所有td标签
cells = row.find_all('td')
# 每一行的数据都以字典形式存在,字典的键来自标题,值来自单元格数据
data = {}
for index, cell in enumerate(cells):
key = headers[index]
# 提取每个单元格的文本作为数据
data[key] = cell.get_text(strip=True)
# 将每行数据添加至结果列表
result.append(data)
# 将结果列表转换为JSON格式
json_data = json.dumps(result, ensure_ascii=False, indent=4)
return json_data
def handle_response(self, response):
if response.url == 'https://xxxx/attendance_api/services/login/imageRoute':
json_body = response.json()
self.x_value = int(json_body["data"]["imageVerificationVo"]["x"])
if response.url == 'https://xxxx/attendance_api/services/login/verifyRoute':
json_body = response.json()
if json_body["code"] == 0:
self.verification = 1
print("Verification successful")
else:
self.verification = 0
print("Verification failed")
with sync_playwright() as playwright:
username = ""
password = ""
receiver = ""
kq = kq(username, password, receiver)
retry = 0
while retry < 5:
try:
kq.run(playwright)
break
except Exception as e:
retry += 1
msg = "【打卡失败】\n" + traceback.format_exc()
kq.send_email(msg,receiver)
continue