高级交互操作
在网页自动化中,除了基本的点击和输入操作外,有时我们需要模拟更复杂的用户交互行为,如拖拽、悬停、键盘组合键等。DrissionPage 提供了丰富的高级交互功能,让您能够精确模拟各种复杂的用户行为。本教程将详细介绍这些高级交互操作的使用方法。
鼠标操作
鼠标悬停
悬停操作是指将鼠标移动到元素上方但不点击,常用于触发下拉菜单、工具提示等交互效果:
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.get('https://example.com')
# 鼠标悬停在元素上
menu_item = page.ele('#dropdown-menu')
menu_item.hover()
# 等待子菜单显示后点击子菜单项
page.wait.ele_display('.submenu-item')
page.ele('.submenu-item').click()
鼠标拖拽
拖拽操作可以用于各种场景,如滑块控件、拖放上传、排序等:
# 基本拖拽:将一个元素拖到另一个元素位置
source = page.ele('#drag-source')
target = page.ele('#drop-target')
source.drag_to(target)
# 带偏移量的拖拽:将元素拖动指定的距离
slider = page.ele('#slider')
# 水平拖动100像素,垂直不变
slider.drag_to(slider, offset_x=100, offset_y=0)
# 自定义拖拽路径
source = page.ele('#drag-source')
# 先向右移动,再向下移动,最后到达目标位置
source.drag_to(
target,
steps=10, # 分10步完成
path='curved' # 使用曲线路径
)
精确点击
有时需要点击元素的特定位置,而不是默认的中心点:
# 点击元素左上角
button = page.ele('#button')
button.click(inner_offset_x=-button.rect.width/2 + 5, inner_offset_y=-button.rect.height/2 + 5)
# 点击元素右下角
button.click(inner_offset_x=button.rect.width/2 - 5, inner_offset_y=button.rect.height/2 - 5)
# 相对于页面坐标点击
page.click_at(x=100, y=200)
多次点击
双击或连续多次点击:
# 双击元素
element = page.ele('#double-click-target')
element.click(2) # 点击2次
# 三击全选文本
text_element = page.ele('#text-field')
text_element.click(3) # 点击3次,通常用于全选文本
键盘操作
键盘按键
模拟键盘按键可以触发各种快捷键和操作:
# 按下单个键
page.press_key('F5') # 刷新页面
# 按下组合键
page.press_key('Ctrl+A') # 全选
page.press_key('Ctrl+C') # 复制
page.press_key('Ctrl+V') # 粘贴
# 常用组合键
page.press_key('Alt+Tab') # 切换窗口
page.press_key('Ctrl+Shift+I') # 打开开发者工具
page.press_key('Ctrl+Shift+N') # 打开隐身窗口
在元素上使用键盘操作
针对特定元素执行键盘操作:
input_field = page.ele('#search-input')
# 聚焦元素后按键
input_field.focus()
page.press_key('Hello World') # 输入文本
page.press_key('Enter') # 按回车确认
# 使用键盘修改输入
input_field.input('Hello ')
page.press_key('World')
page.press_key('Ctrl+A') # 全选文本
page.press_key('Backspace') # 删除所选内容
模拟输入法和特殊字符
对于中文等需要使用输入法的场景或特殊字符:
# 中文输入
input_field = page.ele('#name-input')
input_field.input('张三', by_js=False) # 使用by_js=False可以支持输入法
# 输入特殊字符
message_field = page.ele('#message')
message_field.input('Hello 世界!✨🎉 Special chars: ©®™')
高级表单操作
文件上传
处理文件上传有多种方式:
# 方法1:直接使用上传方法(推荐)
file_input = page.ele('input[type=file]')
file_input.upload('C:/path/to/file.jpg')
# 方法2:多文件上传
file_input = page.ele('input[multiple][type=file]')
file_input.upload(['C:/path/to/file1.jpg', 'C:/path/to/file2.jpg'])
# 方法3:如果上传按钮被隐藏或样式改变
# 先找到真正的input元素
file_input = page.ele('input[type=file]', timeout=0.1)
if not file_input:
# 如果找不到,可能是隐藏的,使用JavaScript修改其可见性
page.run_js('''
var input = document.querySelector('input[type=file]');
input.style.opacity = 1;
input.style.display = 'block';
input.style.visibility = 'visible';
''')
file_input = page.ele('input[type=file]')
file_input.upload('C:/path/to/file.jpg')
操作富文本编辑器
许多网站使用富文本编辑器,如TinyMCE、CKEditor等:
# 方法1:通过iframe访问编辑器(如果在iframe中)
editor_frame = page.get_frame('#editor-iframe')
editor_body = editor_frame.ele('body')
editor_body.input('Hello, this is rich text content.')
# 方法2:使用JavaScript设置内容
page.run_js('''
document.querySelector('.rich-editor').innerHTML = 'Hello, <b>bold text</b> and <i>italic text</i>.';
''')
# 方法3:操作可编辑div
editor_div = page.ele('[contenteditable=true]')
editor_div.input('Hello World')
# 添加格式化文本
editor_div.run_js('''
this.innerHTML += '<br><b>This is bold</b> and <i>this is italic</i>';
''')
处理复杂下拉菜单
现代网站中的下拉菜单通常不是标准的<select>元素,而是自定义实现:
# 自定义下拉菜单操作
# 1. 点击打开下拉菜单
dropdown = page.ele('.custom-dropdown')
dropdown.click()
# 2. 等待下拉选项显示
page.wait.ele_display('.dropdown-options')
# 3. 点击特定选项
page.ele('.dropdown-options .option[data-value="option2"]').click()
# 或通过文本选择
page.ele('text=选项2', base_ele=page.ele('.dropdown-options')).click()
滚动操作
页面滚动
控制页面滚动对于加载延迟内容或操作特定区域的元素很重要:
# 基本滚动
page.scroll.down(300) # 向下滚动300像素
page.scroll.up(200) # 向上滚动200像素
# 滚动到页面特定位置
page.scroll.to_top() # 滚动到顶部
page.scroll.to_bottom() # 滚动到底部
page.scroll.to(x=0, y=500) # 滚动到指定坐标
# 平滑滚动(模拟真实用户)
page.scroll.to_bottom(smooth=True)
元素滚动
有些元素有自己的滚动条,需要单独控制:
# 滚动元素使其可见
target = page.ele('#deep-content')
target.scroll.to_see() # 滚动到元素可见
# 操作有滚动条的元素
scroll_container = page.ele('.scrollable-container')
# 在容器内滚动
scroll_container.scroll.down(200)
scroll_container.scroll.to_bottom()
# 水平滚动
scroll_container.scroll.right(100)
scroll_container.scroll.left(50)
滚动并等待加载
针对无限滚动加载的页面:
# 滚动到底部并等待新内容加载
def scroll_and_wait():
# 记录当前元素数量
initial_count = len(page.eles('.item'))
# 滚动到底部
page.scroll.to_bottom()
# 等待新元素加载
def check_new_content():
new_count = len(page.eles('.item'))
return new_count > initial_count
# 等待条件满足或超时
try:
page.wait.until(check_new_content, timeout=5)
return True
except:
return False
# 连续滚动加载多页内容
items = []
for _ in range(5): # 尝试加载5页
# 获取当前页面的items
items.extend([item.text for item in page.eles('.item')])
# 滚动并检查是否有新内容
if not scroll_and_wait():
print("没有更多内容了")
break
print(f"共加载了 {len(items)} 个项目")
复杂交互场景
拖拽排序
实现列表元素拖拽排序:
from DrissionPage import ChromiumPage
import time
page = ChromiumPage()
page.get('https://example.com/sortable-list')
# 获取所有可排序的项目
items = page.eles('.sortable-item')
# 将第一项拖到最后一项之后
first_item = items[0]
last_item = items[-1]
first_item.drag_to(last_item, offset_y=10) # 稍微偏下以确保放到后面
# 将第三项向上移动一位
items = page.eles('.sortable-item') # 重新获取排序后的项目
items[2].drag_to(items[1], offset_y=-5) # 稍微偏上以确保放到前面
# 验证排序结果
items = page.eles('.sortable-item')
item_texts = [item.text for item in items]
print("排序后的顺序:", item_texts)
滑块控件操作
操作滑块控件调整值:
# 获取滑块元素
slider = page.ele('.slider-handle')
# 获取滑块初始位置和轨道宽度
track = page.ele('.slider-track')
track_width = track.rect.width
# 拖动到50%位置
slider.drag_to(slider, offset_x=track_width/2 - slider.rect.width/2, offset_y=0)
# 精确拖动到特定值
def move_slider_to_value(value, min_val=0, max_val=100):
slider = page.ele('.slider-handle')
track = page.ele('.slider-track')
# 计算需要移动的距离
track_width = track.rect.width
value_range = max_val - min_val
position = (value - min_val) / value_range * track_width
# 计算当前位置
current_position = slider.rect.x - track.rect.x
# 计算需要拖动的距离
offset = position - current_position
# 执行拖动
slider.drag_to(slider, offset_x=offset, offset_y=0)
# 将滑块移动到75%的位置
move_slider_to_value(75)
颜色选择器操作
操作网页中的颜色选择器:
# 点击颜色选择器打开面板
color_picker = page.ele('#color-picker')
color_picker.click()
# 等待颜色面板出现
page.wait.ele_display('.color-panel')
# 方法1:点击预设颜色
page.ele('.color-swatch[data-color="#FF5733"]').click()
# 方法2:操作HSL颜色选择器
hue_slider = page.ele('.hue-slider')
# 将色相调到中间位置
hue_slider.drag_to(hue_slider, offset_x=hue_slider.rect.width/2, offset_y=0)
# 在色板上选择饱和度和亮度
color_area = page.ele('.color-area')
# 选择右上角(高饱和度,高亮度)
color_area.click(inner_offset_x=color_area.rect.width/2 - 10, inner_offset_y=-color_area.rect.height/2 + 10)
# 方法3:直接输入RGB或HEX值
hex_input = page.ele('input[data-format="hex"]')
hex_input.input('#3366FF')
复杂拖拽上传
处理需要拖拽文件到特定区域的上传功能:
from DrissionPage import ChromiumPage
import os
page = ChromiumPage()
page.get('https://example.com/upload')
# 方法1:如果有隐藏的文件输入框,直接使用它
try:
file_input = page.ele('input[type=file]', timeout=1)
file_input.upload('C:/path/to/file.jpg')
print("使用常规上传方法成功")
except:
print("找不到标准文件输入框,尝试模拟拖拽...")
# 方法2:使用JavaScript模拟拖拽事件
file_path = os.path.abspath('C:/path/to/file.jpg')
# 获取拖拽区域
drop_zone = page.ele('#drop-zone')
# 执行JavaScript模拟文件拖拽
page.run_js('''
function simulateFileDrop(dropTarget, filePath) {
// 创建DataTransfer对象
const dt = new DataTransfer();
// 创建File对象
const file = new File(['file content'], 'filename.jpg', {type: 'image/jpeg'});
dt.items.add(file);
// 创建拖拽事件
const dragEvent = new DragEvent('drop', {
bubbles: true,
cancelable: true,
dataTransfer: dt
});
// 分发事件
dropTarget.dispatchEvent(dragEvent);
}
// 获取目标元素并执行拖拽
const dropZone = document.querySelector('#drop-zone');
simulateFileDrop(dropZone, arguments[0]);
''', file_path)
执行连续操作
有时需要执行一系列连续操作,模拟真实用户行为:
from DrissionPage import ChromiumPage
import time
import random
def simulate_human_behavior(page):
"""模拟人类用户浏览页面的行为"""
# 随机滚动
scroll_distance = random.randint(300, 800)
page.scroll.down(scroll_distance, smooth=True)
time.sleep(random.uniform(1, 3))
# 随机点击一个链接
links = page.eles('tag:a')
if links:
random_link = random.choice(links)
if random_link.is_displayed():
random_link.scroll.to_see()
time.sleep(random.uniform(0.5, 1.5))
random_link.click()
page.wait.load_complete()
# 随机移动鼠标到某个元素上
elements = page.eles('.hoverable')
if elements:
random_element = random.choice(elements)
if random_element.is_displayed():
random_element.scroll.to_see()
random_element.hover()
time.sleep(random.uniform(1, 2))
# 随机查看图片
images = page.eles('tag:img')
if images:
random_image = random.choice(images)
if random_image.is_displayed():
random_image.scroll.to_see()
time.sleep(random.uniform(1, 3))
# 回到顶部
page.scroll.to_top(smooth=True)
time.sleep(random.uniform(1, 2))
# 创建页面并执行模拟行为
page = ChromiumPage()
page.get('https://example.com')
# 执行多次模拟行为
for _ in range(3):
simulate_human_behavior(page)
处理验证码
自动化过程中,经常会遇到验证码挑战:
处理文本验证码
from DrissionPage import ChromiumPage
import pytesseract
from PIL import Image
import io
import time
page = ChromiumPage()
page.get('https://example.com/login')
# 填写登录信息
page.ele('#username').input('user123')
page.ele('#password').input('password123')
# 处理文本验证码
captcha_img = page.ele('#captcha-img')
# 方法1:手动处理
# captcha_img.save('captcha.png')
# code = input("请查看captcha.png并输入验证码: ")
# 方法2:使用OCR自动识别
img_bytes = captcha_img.get_screenshot_as_bytes()
img = Image.open(io.BytesIO(img_bytes))
# 使用pytesseract进行OCR识别
captcha_text = pytesseract.image_to_string(img, config='--psm 7')
captcha_text = ''.join(c for c in captcha_text if c.isalnum()) # 清理文本
print(f"识别到的验证码: {captcha_text}")
# 输入验证码
page.ele('#captcha-input').input(captcha_text)
# 提交表单
page.ele('#login-button').click()
# 检查登录结果
time.sleep(2)
if '验证码错误' in page.html:
print("验证码识别错误,尝试刷新验证码重试")
# 刷新验证码
page.ele('#refresh-captcha').click()
# ... 重试逻辑 ...
else:
print("登录成功!")
处理滑块验证码
from DrissionPage import ChromiumPage
import time
import random
page = ChromiumPage()
page.get('https://example.com/with-slider-captcha')
# 通用滑块验证码破解方法
def solve_slider_captcha():
# 获取滑块和轨道
slider = page.ele('.slider-button')
track = page.ele('.slider-track')
# 获取轨道宽度
track_width = track.rect.width
slider_width = slider.rect.width
# 计算需要滑动的距离(通常是轨道宽度减去滑块宽度)
distance = track_width - slider_width
# 生成人类般的移动轨迹
# 先加速,后减速,加点随机性
tracks = []
current = 0
mid = distance * 4 / 5 # 前4/5加速,后1/5减速
t = 0.2 # 时间因子
v = 0 # 初速度
while current < distance:
if current < mid:
a = random.uniform(2, 5) # 加速度
else:
a = random.uniform(-3, -1) # 减速度
v0 = v # 初速度
v = v0 + a * t # 当前速度
move = v0 * t + 1/2 * a * t * t # 移动距离
current += move
# 确保不超过总距离
if current > distance:
current = distance
tracks.append(round(current))
# 模拟人类拖动过程
slider.hover()
time.sleep(random.uniform(0.1, 0.3))
# 按下鼠标
page.action.mouse.move_to(slider)
page.action.mouse.button_down()
time.sleep(random.uniform(0.1, 0.3))
# 拖动滑块
for track in tracks:
x = slider.rect.x + track
y = slider.rect.y
page.action.mouse.move_to((x, y))
time.sleep(random.uniform(0.01, 0.03))
# 小幅回退,模拟人类行为
page.action.mouse.move_to((slider.rect.x + distance - random.randint(1, 3), slider.rect.y))
time.sleep(random.uniform(0.2, 0.3))
# 释放鼠标
page.action.mouse.button_up()
time.sleep(1)
# 检查是否成功
if page.ele('.captcha-success', timeout=3):
return True
return False
# 尝试解决验证码,最多尝试3次
for attempt in range(3):
print(f"尝试解决滑块验证码 (第{attempt+1}次)")
if solve_slider_captcha():
print("验证码解决成功!")
break
else:
print("验证码解决失败,重试...")
# 刷新或重置验证码
page.refresh()
time.sleep(2)
总结
DrissionPage 提供了丰富的高级交互操作功能,使您能够精确模拟各种复杂的用户行为:
- 鼠标操作:悬停、拖拽、精确点击、多次点击等
- 键盘操作:按键、组合键、特殊字符输入等
- 高级表单操作:文件上传、富文本编辑器、复杂下拉菜单等
- 滚动操作:页面滚动、元素滚动、滚动并等待加载等
- 复杂交互场景:拖拽排序、滑块控件、颜色选择器、连续操作等
- 验证码处理:处理文本验证码和滑块验证码等
通过合理组合这些高级交互功能,您可以构建出更真实、更健壮的自动化脚本,处理各种复杂的网页操作场景。同时,适当添加随机延时和轨迹,可以让自动化操作更加自然,有效避免被网站识别为机器人。