233 lines
8.0 KiB
Python
233 lines
8.0 KiB
Python
import time
|
|
import random
|
|
import re
|
|
import asyncio
|
|
from typing import Dict, List, Optional
|
|
|
|
class EnhancedHumanTypingSimulator:
|
|
def __init__(self, page=None):
|
|
# 保留原方案的简单配置
|
|
self.base_config = {
|
|
'min_typing_speed': 5,
|
|
'max_typing_speed': 15,
|
|
'pause_probability': 0.1,
|
|
'chunk_input': True,
|
|
'max_chunk_length': 50
|
|
}
|
|
|
|
# 新增高级特性配置
|
|
self.advanced_config = {
|
|
# 人类状态模拟
|
|
'energy_level': random.uniform(0.7, 1.0),
|
|
'typing_proficiency': random.uniform(0.6, 0.9),
|
|
'emotion_state': random.uniform(0.8, 1.0),
|
|
|
|
# 错误处理
|
|
'base_error_rate': random.uniform(0.02, 0.05),
|
|
'error_correction_speed': random.uniform(0.3, 0.8),
|
|
|
|
# 速度控制
|
|
'speed_variance': random.uniform(0.1, 0.2),
|
|
'burst_speed_probability': 0.1
|
|
}
|
|
|
|
self.page = page
|
|
self.typing_session = {
|
|
'start_time': None,
|
|
'chars_typed': 0,
|
|
'last_break_time': time.time()
|
|
}
|
|
|
|
async def type_text(self, text: str, selector: str = None) -> bool:
|
|
"""增强版的文本输入方法"""
|
|
try:
|
|
if selector:
|
|
# 等待并点击元素
|
|
await self._prepare_input(selector)
|
|
|
|
# 初始化会话
|
|
self.typing_session['start_time'] = time.time()
|
|
|
|
# 智能分段
|
|
chunks = self._smart_split_text(text)
|
|
|
|
for chunk in chunks:
|
|
# 获取当前状态
|
|
current_state = self._get_current_state()
|
|
|
|
# 输入当前段落
|
|
await self._type_chunk(chunk, current_state)
|
|
|
|
# 段落间自然停顿
|
|
await self._natural_pause(current_state)
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"输入文本时出错: {e}")
|
|
return False
|
|
|
|
def _smart_split_text(self, text: str) -> List[str]:
|
|
"""智能文本分段"""
|
|
paragraphs = text.split('\n')
|
|
chunks = []
|
|
|
|
for para in paragraphs:
|
|
if len(para) <= self.base_config['max_chunk_length']:
|
|
if para.strip():
|
|
chunks.append(para)
|
|
continue
|
|
|
|
sentences = re.split(r'([。!?,:;])', para)
|
|
current_chunk = ''
|
|
|
|
for sent in sentences:
|
|
if len(current_chunk) + len(sent) < self.base_config['max_chunk_length']:
|
|
current_chunk += sent
|
|
else:
|
|
if current_chunk.strip():
|
|
chunks.append(current_chunk)
|
|
current_chunk = sent
|
|
|
|
if current_chunk.strip():
|
|
chunks.append(current_chunk)
|
|
|
|
return chunks
|
|
|
|
def _get_current_state(self) -> Dict:
|
|
"""获取当前输入状态"""
|
|
typing_duration = time.time() - self.typing_session['start_time']
|
|
fatigue = min(typing_duration / 300, 0.7)
|
|
|
|
self.advanced_config['energy_level'] *= (1 - fatigue * 0.1)
|
|
self.advanced_config['emotion_state'] *= random.uniform(0.98, 1.02)
|
|
|
|
return {
|
|
'energy_level': max(0.3, self.advanced_config['energy_level']),
|
|
'emotion_state': max(0.4, min(1.0, self.advanced_config['emotion_state'])),
|
|
'typing_proficiency': self.advanced_config['typing_proficiency'],
|
|
'current_error_rate': self._calculate_error_rate(fatigue)
|
|
}
|
|
|
|
async def _type_chunk(self, chunk: str, state: Dict):
|
|
"""输入文本块"""
|
|
for char in chunk:
|
|
typing_speed = self._calculate_typing_speed(state)
|
|
|
|
if random.random() < state['current_error_rate']:
|
|
await self._handle_typing_error(char, state)
|
|
else:
|
|
await self._type_char(char, typing_speed)
|
|
|
|
self.typing_session['chars_typed'] += 1
|
|
await self._micro_pause(state)
|
|
|
|
def _calculate_typing_speed(self, state: Dict) -> float:
|
|
"""计算实时打字速度"""
|
|
base_speed = random.uniform(
|
|
self.base_config['min_typing_speed'],
|
|
self.base_config['max_typing_speed']
|
|
)
|
|
|
|
speed = base_speed * (
|
|
0.7 + state['energy_level'] * 0.3 +
|
|
state['emotion_state'] * 0.2 +
|
|
state['typing_proficiency'] * 0.3
|
|
)
|
|
|
|
speed *= random.uniform(
|
|
1 - self.advanced_config['speed_variance'],
|
|
1 + self.advanced_config['speed_variance']
|
|
)
|
|
|
|
return speed
|
|
|
|
def _calculate_error_rate(self, fatigue: float) -> float:
|
|
"""计算当前错误率"""
|
|
base_rate = self.advanced_config['base_error_rate']
|
|
error_rate = base_rate * (1 + fatigue)
|
|
error_rate *= random.uniform(0.8, 1.2)
|
|
return min(error_rate, 0.15)
|
|
|
|
async def _handle_typing_error(self, char: str, state: Dict):
|
|
"""处理打字错误"""
|
|
error_types = ['typo', 'double_hit', 'delay']
|
|
error_type = random.choice(error_types)
|
|
|
|
if error_type == 'typo':
|
|
wrong_char = self._get_similar_char(char)
|
|
await self._type_char(wrong_char, self._calculate_typing_speed(state))
|
|
await asyncio.sleep(random.uniform(0.2, 0.5))
|
|
await self._press_key("Backspace")
|
|
await self._type_char(char, self._calculate_typing_speed(state))
|
|
|
|
elif error_type == 'double_hit':
|
|
await self._type_char(char, self._calculate_typing_speed(state))
|
|
await self._type_char(char, self._calculate_typing_speed(state))
|
|
await asyncio.sleep(random.uniform(0.1, 0.3))
|
|
await self._press_key("Backspace")
|
|
|
|
else: # delay
|
|
await asyncio.sleep(random.uniform(0.3, 0.8))
|
|
await self._type_char(char, self._calculate_typing_speed(state))
|
|
|
|
async def _natural_pause(self, state: Dict):
|
|
"""自然停顿"""
|
|
base_pause = random.uniform(0.5, 1.5)
|
|
|
|
if state['energy_level'] < 0.5:
|
|
base_pause *= 1.3
|
|
if state['emotion_state'] < 0.6:
|
|
base_pause *= 1.2
|
|
|
|
await asyncio.sleep(base_pause * random.uniform(0.8, 1.2))
|
|
|
|
async def _micro_pause(self, state: Dict):
|
|
"""字符间的微小停顿"""
|
|
pause_time = random.uniform(0.05, 0.15)
|
|
if state['energy_level'] < 0.5:
|
|
pause_time *= 1.2
|
|
await asyncio.sleep(pause_time)
|
|
|
|
def _get_similar_char(self, char: str) -> str:
|
|
"""获取相似字符"""
|
|
similar_chars = {
|
|
'的': '地得',
|
|
'了': '着啦',
|
|
'和': '与跟',
|
|
'我': '我我',
|
|
'是': '市师',
|
|
'在': '再在',
|
|
'有': '又有',
|
|
'都': '都读',
|
|
'好': '号毫'
|
|
}
|
|
return random.choice(similar_chars.get(char, char + char))
|
|
|
|
async def _prepare_input(self, selector: str):
|
|
"""准备输入"""
|
|
try:
|
|
await self.page.wait_for_selector(selector, timeout=5000)
|
|
await self.page.click(selector)
|
|
await asyncio.sleep(random.uniform(0.3, 0.8))
|
|
except Exception as e:
|
|
print(f"准备输入失败: {e}")
|
|
raise
|
|
|
|
async def _type_char(self, char: str, speed: float):
|
|
"""输入单个字符"""
|
|
try:
|
|
delay = 1000 / speed # 转换为毫秒
|
|
await self.page.keyboard.type(char, delay=delay)
|
|
except Exception as e:
|
|
print(f"输入字符失败: {e}")
|
|
raise
|
|
|
|
async def _press_key(self, key: str):
|
|
"""按键操作"""
|
|
try:
|
|
await self.page.keyboard.press(key)
|
|
except Exception as e:
|
|
print(f"按键操作失败: {e}")
|
|
raise
|