Playwright 基础教程:元素拖拽、坐标获取与文本提取实战
前言
Playwright 是微软开源的自动化测试工具,支持多种浏览器和语言。在 Web 自动化测试及爬虫开发中,我们经常需要处理复杂的用户交互,如拖拽操作,或者需要获取元素的精确位置信息以及页面内的文本数据。本文将详细介绍如何使用 Playwright 实现元素拖拽、计算元素坐标、获取网页源码以及提取元素内文本,并提供完整的代码示例。
环境准备与连接方式
在使用 Playwright 之前,确保已安装 Python 环境并安装了 playwright 库。可以通过以下命令安装依赖:
pip install playwright
playwright install
Playwright 提供了两种主要的浏览器连接模式:连接到已打开的浏览器或启动新的浏览器实例。
1. 连接已打开的浏览器
这种方式适用于调试场景,可以查看浏览器实际运行状态。通过 CDP (Chrome DevTools Protocol) 端口连接。
from playwright.sync_api import sync_playwright
class BrowserConnection:
def __init__(self):
self.playwright = sync_playwright().start()
# 连接本地已启动的 Chrome 浏览器,默认端口为 9223
browser = self.playwright.chromium.connect_over_cdp("http://127.0.0.1:9223")
self.default_context = browser.contexts[0]
self.page = self.default_context.pages[0]
2. 启动新浏览器实例
这是最常用的方式,适合自动化脚本执行。可以配置无头模式(headless)或可视化模式。
class NewBrowserSession:
def __init__(self, url):
playwright = sync_playwright().start()
# headless=False 表示显示浏览器窗口,便于观察
browser = playwright.chromium.launch(headless=False)
context = browser.new_context()
self.page = context.new_page()
self.page.goto(url)
元素拖拽操作 (Drag and Drop)
拖拽是常见的用户交互行为,例如文件上传、排序列表等。Playwright 提供了 drag_and_drop 方法简化此过程。
官方文档说明
该方法将指定元素拖拽到目标元素。内部逻辑会先移动到源元素执行 mousedown,再移动到目标元素执行 mouseup。
基本用法
# 简单拖拽:从源选择器拖到目标选择器
page.drag_and_drop("#source", "#target")
# 精确控制位置:指定相对于元素左上角的偏移量
page.drag_and_drop(
"#source",
"#target",
source_position={"x": 34, "y": 7},
target_position={"x": 10, "y": 20}
)
测试案例
为了验证拖拽功能,我们可以创建一个简单的 HTML 页面进行测试。该页面包含一个可拖动的红色方块和一个绿色的放置区域。
HTML 结构示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Drag and Drop Test</title>
<style>
#dragElement {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
top: 0;
left: 0;
cursor: move;
}
#dropElement {
width: 200px;
height: 200px;
background-color: green;
margin: auto;
}
</style>
</head>
<body>
<div id="bigdiv">
<div id="dragElement"></div>
<div id="dropElement"></div>
</div>
<script>
const dragElement = document.getElementById('dragElement');
let isDragging = false;
let startOffset = {x: 0, y: 0};
function startDrag(e) {
isDragging = true;
startOffset.x = e.clientX - dragElement.offsetLeft;
startOffset.y = e.clientY - dragElement.offsetTop;
}
function endDrag() {
isDragging = false;
}
function drag(e) {
if (!isDragging) return;
dragElement.style.left = e.clientX - startOffset.x + 'px';
dragElement.style.top = e.clientY - startOffset.y + 'px';
}
dragElement.addEventListener('mousedown', startDrag);
document.addEventListener('mouseup', endDrag);
document.addEventListener('mousemove', drag);
</script>
</body>
</html>
拖拽代码实现
假设上述 HTML 文件保存在本地路径,以下是使用 Playwright 执行拖拽的完整类定义。
from playwright.sync_api import sync_playwright
import os
class DragDropDemo:
def __init__(self, file_path):
self.playwright = sync_playwright().start()
browser = self.playwright.chromium.launch(headless=False)
context = browser.new_context()
self.page = context.new_page()
# 加载本地 HTML 文件
self.page.goto(f"file://{os.path.abspath(file_path)}")
def drag_and_drop_operate(self):
# 将红色框拖入绿色框
self.page.drag_and_drop(source="#dragElement", target="#dropElement")
print("拖拽操作完成")
if __name__ == '__main__':
# 请替换为实际的本地文件路径
demo = DragDropDemo(r"D:/L_Learning/MyLearningCode/draganddrop.html")
demo.drag_and_drop_operate()
demo.playwright.stop()
注意事项:
在执行拖拽之前,请确保要拖动的元素和目标元素都已经加载完成。如果页面加载较慢,建议使用 wait_for_selector 等待元素出现。
元素坐标获取 (Bounding Box)
在某些自动化场景中,我们需要知道元素在页面上的精确位置,例如点击元素的中心点,或者根据坐标进行截图分析。
bounding_box 方法
bounding_box() 返回一个字典,包含元素的 x, y 坐标以及宽度和高度。
def get_bounding_box(page):
locator = page.locator("#dragElement")
# 等待元素可见
locator.wait_for(state="visible")
box = locator.bounding_box()
print(box) # 输出示例:{'x': 0, 'y': 0, 'width': 100, 'height': 100}
return box
返回值说明:
x: 元素左上角距离视口左侧的距离。y: 元素左上角距离视口顶部的距离。width: 元素宽度。height: 元素高度。
计算中心点
如果需要点击元素的中心,可以通过以下公式计算:
def calculate_center(box):
center_x = box["x"] + box["width"] / 2
center_y = box["y"] + box["height"] / 2
return center_x, center_y
# 使用示例
center = calculate_center(get_bounding_box(page))
page.click(f"#{center[0]} #{center[1]}") # 注意:click 通常接受选择器,这里仅为坐标计算演示
在实际使用中,更推荐直接使用 locator.click(),它会自动定位到元素中心。但在某些复杂交互或自定义事件模拟中,手动计算坐标非常有用。
获取网页源码
Playwright 获取网页源代码的方式与 Selenium 类似,但 API 更加现代化。
对比 Selenium
- Selenium:
driver.page_source - Playwright:
page.content()
代码示例
# 获取当前页面的完整 HTML 源码
page_source = page.content()
# 打印前 500 个字符以检查内容
print(page_source[:500])
应用场景: 对于爬虫开发者来说,获取源码后可以使用正则表达式或 BeautifulSoup 等库提取特定数据。这对于处理动态渲染较少的页面非常高效。
获取元素内文本
在表格数据抓取或表单验证中,提取元素内的文本是核心需求。Playwright 的 Locator 对象提供了多种方法来提取文本,不同的方法适用于不同的场景。
常用方法列表
locator.all_inner_texts(): 返回列表,包含所有匹配元素的内部文本。locator.all_text_contents(): 返回列表,包含所有匹配元素的全文本内容(含子元素)。locator.inner_text(): 返回字符串,第一个匹配元素的内部文本。locator.text_content(): 返回字符串,第一个匹配元素及其所有子元素的全文本内容。locator.inner_html(): 返回字符串,第一个匹配元素的内部 HTML 代码。
详细区别解析
-
inner_text vs text_content:
inner_text()类似于浏览器的innerText,它会考虑 CSS 样式(例如隐藏的元素不会返回文本),并且去除多余的空白。text_content()类似于浏览器的textContent,它会返回 DOM 树中的所有文本节点,包括隐藏的文本,且保留原始格式。
-
all_inner_texts vs all_text_contents:
- 前者针对多个元素返回数组,后者同理。主要区别在于是否包含子元素文本的处理逻辑差异。
测试代码
使用之前的 HTML 结构进行测试:
def extract_element_text(page):
locator = page.locator("#bigdiv")
# 获取所有内部文本
texts1 = locator.all_inner_texts()
print(f"all_inner_texts type: {type(texts1)}, value: {texts1}")
# 获取所有文本内容
texts2 = locator.all_text_contents()
print(f"all_text_contents type: {type(texts2)}, value: {texts2}")
# 获取单个内部文本
text3 = locator.inner_text()
print(f"inner_text type: {type(text3)}, value: {text3}")
# 获取单个文本内容
text4 = locator.text_content()
print(f"text_content type: {type(text4)}, value: {text4}")
# 获取内部 HTML
html5 = locator.inner_html()
print(f"inner_html type: {type(html5)}, value: {html5[:100]}...")
最佳实践建议
- 等待策略:在提取文本前,务必确保元素已加载。推荐使用
locator.wait_for(state="visible")。 - 容错处理:如果元素可能不存在,应捕获异常或使用
locator.count()判断。 - 性能优化:避免一次性提取过多无关文本,尽量缩小选择器范围。
常见问题与解决方案
问题 1:拖拽失败
原因:元素未加载完成或被遮挡。
解决:增加显式等待,使用 page.wait_for_load_state("networkidle")。
问题 2:文本提取为空
原因:使用了 text_content() 但元素被 CSS 隐藏。
解决:尝试改用 inner_text(),它会根据可视性过滤文本。
问题 3:坐标不准
原因:页面滚动导致视口变化。
解决:确保在固定视口下操作,或使用 scroll_into_view_if_needed() 方法。
总结
本文系统讲解了 Playwright 在 Web 自动化中的几个关键功能:
- 元素拖拽:利用
drag_and_drop轻松模拟鼠标交互,支持自定义偏移量。 - 坐标获取:通过
bounding_box获取元素几何信息,辅助精确定位。 - 源码获取:使用
page.content()快速抓取页面 HTML,配合正则进行数据清洗。 - 文本提取:深入对比了
inner_text、text_content等方法的差异,帮助开发者根据场景选择合适方案。
掌握这些技能将大大提升自动化测试脚本的稳定性和爬虫数据的获取效率。建议在实际项目中结合具体的业务逻辑,灵活运用这些 API 构建健壮的自动化流程。

