autoUpload/utils/paste_typing.py

245 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import random
import asyncio
from typing import Optional
class PasteTypingSimulator:
"""模拟用户复制粘贴行为的输入模拟器"""
def __init__(self, page=None):
self.page = page
self.config = {
'pre_paste_delay': (0.5, 1.2), # 粘贴前的停顿时间
'post_paste_delay': (0.8, 1.5), # 粘贴后的停顿时间
'review_probability': 0.7, # 检查内容的概率
'review_time': (1.5, 3.0), # 检查内容的时间
'adjust_probability': 0.4, # 调整格式的概率
'adjust_delay': (0.3, 0.8), # 调整时的停顿
'scroll_probability': 0.6, # 滚动查看的概率
'scroll_delay': (0.5, 1.0), # 滚动时的停顿
}
async def paste_text(self, selector: str, text: str = None) -> bool:
"""模拟用户粘贴文本的行为
Args:
selector: 输入框的选择器
text: 要粘贴的文本,如果不提供则使用剪贴板中的内容
"""
try:
# 1. 先等待目标元素并点击以获取焦点
element = await self.page.wait_for_selector(selector, timeout=5000)
await element.click()
await asyncio.sleep(random.uniform(0.2, 0.4))
# 2. 全选当前内容(如果有的话)并删除
await self.page.keyboard.press("Control+A")
await asyncio.sleep(random.uniform(0.1, 0.2))
await self.page.keyboard.press("Delete")
await asyncio.sleep(random.uniform(0.2, 0.4))
# 3. 如果提供了文本,使用更直接可靠的方法
if text:
# 直接使用element.fill方法作为备选方案
try:
await element.fill(text)
print("使用fill方法成功填充文本")
# 粘贴后的检查动作
await self._post_paste_actions()
return True
except Exception as fill_error:
print(f"fill方法失败尝试剪贴板方法: {fill_error}")
# 如果fill方法失败尝试剪贴板方法
# 使用更简单可靠的方式复制文本到剪贴板
await self.page.evaluate('(text) => {' +
'return navigator.clipboard.writeText(text);' +
'}', text)
await asyncio.sleep(random.uniform(0.3, 0.5))
# 4. 简化的粘贴操作直接使用Control+V
# 确保焦点在元素上
await element.click()
await asyncio.sleep(random.uniform(0.1, 0.2))
# 直接执行粘贴
await self.page.keyboard.press("Control+V")
await asyncio.sleep(random.uniform(0.4, 0.7))
# 验证文本是否已粘贴
try:
current_text = await element.input_value()
if not current_text or (text and text not in current_text):
print(f"警告: 粘贴可能失败。当前文本长度: {len(current_text)}")
# 最后的备选方案直接设置value
await element.evaluate(f'(element) => {{ element.value = "{text}"; }}', element)
await asyncio.sleep(0.2)
except Exception as verify_error:
print(f"验证粘贴时出错: {verify_error}")
# 粘贴后的检查动作
await self._post_paste_actions()
return True
except Exception as e:
print(f"粘贴文本时出错: {e}")
return False
async def _copy_to_clipboard(self, text: str):
"""将文本可靠地复制到剪贴板
这个方法使用多种方式尝试复制文本,确保文本能正确复制到剪贴板
"""
try:
# 方式1使用更安全的JavaScript避免模板字符串中的转义问题
await self.page.evaluate('(text) => {' +
'const textarea = document.createElement("textarea");' +
'textarea.style.position = "fixed";' +
'textarea.style.left = "-999999px";' +
'textarea.style.top = "-999999px";' +
'textarea.value = text;' +
'document.body.appendChild(textarea);' +
'textarea.focus();' +
'textarea.select();' +
'try {' +
'document.execCommand("copy");' +
'} catch (err) {' +
'console.error("复制失败:", err);' +
'}' +
'document.body.removeChild(textarea);' +
'}', text)
except Exception as e:
print(f"主要复制方法失败: {e}")
# 方式2备用方法 - 尝试使用navigator.clipboard API
try:
await self.page.evaluate('(text) => navigator.clipboard.writeText(text)', text)
except Exception as e2:
print(f"备用复制方法也失败: {e2}")
# 方式3最基本的方法 - 直接设置文本区域的值
await self.page.evaluate('(text) => {' +
'const textarea = document.createElement("textarea");' +
'textarea.value = text;' +
'document.body.appendChild(textarea);' +
'textarea.select();' +
'document.body.removeChild(textarea);' +
'}', text)
async def _prepare_input(self, selector: str):
"""准备输入区域"""
try:
# 等待元素出现并点击
element = await self.page.wait_for_selector(selector, timeout=5000)
await element.click()
# 模拟点击后的短暂停顿
await asyncio.sleep(random.uniform(0.3, 0.6))
# 清空现有内容
await self.page.keyboard.press("Control+A")
await asyncio.sleep(random.uniform(0.1, 0.2))
await self.page.keyboard.press("Delete")
await asyncio.sleep(random.uniform(0.2, 0.4))
except Exception as e:
print(f"准备输入区域失败: {e}")
raise
async def _pre_paste_actions(self):
"""模拟粘贴前的准备动作"""
# 模拟思考和准备时间
await asyncio.sleep(random.uniform(*self.config['pre_paste_delay']))
# 模拟按下 Ctrl 键,更真实的用户操作
await self.page.keyboard.down("Control")
await asyncio.sleep(random.uniform(0.1, 0.2)) # 增加延迟,更接近真实用户
async def _perform_paste(self):
"""执行粘贴操作,分离按键按下和释放,更接近真实用户行为"""
# 模拟按下 V 键
await self.page.keyboard.press("v")
await asyncio.sleep(random.uniform(0.1, 0.2))
# 释放 Ctrl 键
await self.page.keyboard.up("Control")
# 等待内容出现,给足够的时间让内容粘贴完成
await asyncio.sleep(random.uniform(0.4, 0.7))
# 验证粘贴是否成功(可选)
try:
# 可以添加验证逻辑来确认内容是否已粘贴
pass
except Exception:
# 即使验证失败也不中断流程
pass
async def _post_paste_actions(self):
"""模拟粘贴后的检查和调整动作"""
# 随机检查内容
if random.random() < self.config['review_probability']:
# 模拟鼠标滚动查看内容
if random.random() < self.config['scroll_probability']:
# 向下滚动一点
await self.page.mouse.wheel(0, 100)
await asyncio.sleep(random.uniform(0.3, 0.6))
# 停顿一下,像是在阅读
await asyncio.sleep(random.uniform(0.5, 1.0))
# 再滚动回来
await self.page.mouse.wheel(0, -100)
await asyncio.sleep(random.uniform(0.3, 0.6))
# 随机点击文本框内部的某个位置(模拟检查或准备编辑)
if random.random() < 0.3: # 30%的概率
element = await self.page.query_selector('textarea')
if element:
box = await element.bounding_box()
if box:
x = box['x'] + random.uniform(10, box['width'] - 10)
y = box['y'] + random.uniform(10, box['height'] - 10)
await self.page.mouse.click(x, y)
await asyncio.sleep(random.uniform(0.2, 0.5))
# 随机调整格式
if random.random() < self.config['adjust_probability']:
# 模拟删除多余空行
for _ in range(random.randint(1, 2)):
await self.page.keyboard.press("End")
await asyncio.sleep(random.uniform(0.1, 0.2))
await self.page.keyboard.press("Backspace")
await asyncio.sleep(random.uniform(*self.config['adjust_delay']))
async def paste_with_format_check(self, text: str, selector: str = None) -> bool:
"""带格式检查的粘贴方法"""
try:
if selector:
# 如果提供了选择器,先准备输入区域
element = await self.page.wait_for_selector(selector, timeout=5000)
await element.click()
await asyncio.sleep(random.uniform(0.2, 0.4))
# 清空现有内容
await self.page.keyboard.press("Control+A")
await asyncio.sleep(random.uniform(0.1, 0.2))
await self.page.keyboard.press("Delete")
await asyncio.sleep(random.uniform(0.2, 0.4))
# 可靠地复制文本到剪贴板
await self._copy_to_clipboard(text)
await asyncio.sleep(random.uniform(*self.config['pre_paste_delay']))
# 模拟真实的粘贴操作
await self._pre_paste_actions()
await self._perform_paste()
# 模拟粘贴后的检查
await asyncio.sleep(random.uniform(*self.config['post_paste_delay']))
# 随机检查内容
if random.random() < self.config['review_probability']:
await asyncio.sleep(random.uniform(*self.config['review_time']))
return True
except Exception as e:
print(f"格式检查粘贴失败: {e}")
return False