如何使用 Playwright 和 Scrapeless 抓取 Iframe
Senior Web Scraping Engineer
TL;DR:
- iframe包含一个独立的文档,父页面选择器无法搜索。 在定位或提取子框架元素之前,请选择子框架。
- Playwright通过
page.frames提供每个浏览上下文。 根据稳定的名称、URL模式或拥有的iframe元素来识别所需的子框架,而不是依赖数组位置。 Frame和FrameLocator满足不同的提取需求。 使用Frame进行元数据和生命周期控制;使用FrameLocator进行简洁的元素操作。- Playwright可以通过浏览器协议自动化跨源iframe内容。 页面内的JavaScript仍受到浏览器的同源策略的限制。
- 相同的iframe提取逻辑可在本地和通过Scrapeless使用。 转到Scrapeless Scraping Browser更改浏览器创建,而不是框架选择或定位器代码。
- 免费开始。 新的Scrapeless账户包括免费的Scraping Browser运行时—请在app.scrapeless.com注册。
引言:从正确的文档开始
iframe是一个独立的文档,具有自己的URL、生命周期和DOM。本指南使用Playwright列举真实页面上的浏览上下文,通过名称和URL识别预期的子框架,并提取父页面选择器无法看到的表单。同样的框架逻辑在将Playwright连接到Scrapeless Scraping Browser后运行。
为什么 iframe 需要不同的提取路径
<iframe> 在父页面内嵌入了另一个浏览上下文。该元素属于父DOM;加载的文档属于子框架。针对父页面评估的选择器不会搜索该子文档。
HTML iframe参考描述了嵌套上下文及其自身的会话历史。Playwright将每个上下文暴露为 Frame ,而 FrameLocator 为您提供了一种通过 <iframe> 元素定位元素的简洁方法。
为什么使用Playwright与Scrapeless
Playwright可以检查框架元数据并与子文档进行交互,即使iframe是跨源的。该浏览器级别的访问与在父页面内运行的JavaScript有所不同,后者受到同源策略的限制。
Scrapeless Scraping Browser将Chromium移动到一个托管的反检测环境中,同时保留Playwright的框架API。连接使用connect_over_cdp;浏览器创建后的每条提取行均保持标准Playwright。
先决条件
- Python 3.10 或更高版本。
playwright1.59.0 或当前兼容版本。- 本地Chrome/Chromium用于完整的可执行回退。
- 一个有资金的Scrapeless账户和用于云浏览器连接的
SCRAPELESS_API_KEY。在验证账户上启动的新会话返回代码14500,因此云连接是一个标记的先决条件缺口。
安装
bash
python -m pip install "playwright==1.59.0"
可运行的示例使用主机的 /usr/bin/google-chrome。如果您的项目是通过Playwright管理浏览器,请使用Playwright管理的Chromium。
连接到Scrapeless Scraping Browser
注意:此连接需要有资金的Scraping Browser余额。最终的验证账户返回
余额不足,请先充值。请使用您自己的资金密钥;下面的框架枚举和提取完全在本地Chrome中运行。
python
import os
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
params = urlencode({
"token": os.environ["SCRAPELESS_API_KEY"],
"sessionTTL": 120,
"proxyCountry": "US",
})
cdp_url = f"wss://browser.scrapeless.com/api/v2/browser?{params}"
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(cdp_url)
page = browser.contexts[0].pages[0]
# 继续以下框架步骤。
browser.close()
请使用Scrapeless开发者文档获取当前连接参数。
第一步 — 打开具有已知子框架的页面
Selenium维持一个小型公共测试夹具。其父页面有一个名为 iframe1-name 的iframe,其 src 加载 formPage.html:
python
TARGET_URL = "https://www.selenium.dev/selenium/web/iframes.html"
page.goto(TARGET_URL, wait_until="domcontentloaded")
page.wait_for_selector("#iframe1")
page.wait_for_timeout(500)
print("parent title:", page.title())
print("parent email matches:", page.locator("#email").count())
text
parent title: 该页面包含ifram
parent email matches: 0
电子邮件输入存在,但它属于嵌入的表单文档,而不是父文档。
第2步 — 在选择一个之前枚举框架
page.frames 包含主框架以及每个附加的子框架和后代:
python
print("框架数量:", len(page.frames))
for index, frame in enumerate(page.frames):
print(index, repr(frame.name), frame.url, frame.title())
text
框架数量: 2
0 '' https://www.selenium.dev/selenium/web/iframes.html 该页面包含iframe
1 'iframe1-name' https://www.selenium.dev/selenium/web/formPage.html 我们从这里离开
索引零是主文档。不要将索引一变成永久合同:另一个广告、同意小部件或分析框架可以更改顺序。名称和URL是更好的证据。
第3步 — 通过名称或URL选择框架
该固定装置为您提供了两个稳定的信号:
python
named_frame = page.frame(name="iframe1-name")
url_frame = next(
frame for frame in page.frames
if frame.url.endswith("/formPage.html")
)
assert named_frame is url_frame
print("已选择框架:", url_frame.name)
print("已选择标题:", url_frame.title())
如果名称是生成的,匹配一个有限的URL模式或找到拥有的iframe元素并调用content_frame()。当多个候选者匹配时失败;默默选择第一个重复项将产生来自错误小部件的可信数据。
第4步 — 从子文档中提取数据
一旦您持有Frame,其定位器API表现得像页面的:
python
email = url_frame.locator("#email")
selected = url_frame.locator("#multi option:checked").evaluate_all(
"options => options.map(option => option.value)"
)
record = {
"frame_name": url_frame.name,
"frame_url": url_frame.url,
"frame_title": url_frame.title(),
"email_inputs": email.count(),
"selected_values": selected,
}
print("电子邮件输入:", record["email_inputs"])
print("选择的值:", record["selected_values"])
text
电子邮件输入: 1
选择的值: ['鸡蛋', '香肠']
子页面有一个多选字段,其中选中了这两个选项。这比仅仅读取标题要强:这证明Playwright已经到达表单文档并评估了其中的状态。
第5步 — 使用FrameLocator进行直接操作
当您不需要框架元数据时,FrameLocator更简短:
python
frame_ui = page.frame_locator("#iframe1")
print("通过FrameLocator的电子邮件:", frame_ui.locator("#email").count())
官方Playwright框架指南显示了同样的两个层级:通过iframe元素定位内容,或通过URL/名称获取Frame对象。
准备在托管浏览器中运行这些定位器吗?创建一个免费的Scrapeless账户,并仅替换浏览器创建。
完整本地验证脚本
python
from playwright.sync_api import sync_playwright
TARGET_URL = "https://www.selenium.dev/selenium/web/iframes.html"
with sync_playwright() as p:
browser = p.chromium.launch(
executable_path="/usr/bin/google-chrome",
headless=True,
)
page = browser.new_page()
page.goto(TARGET_URL, wait_until="domcontentloaded")
page.wait_for_selector("#iframe1")
page.wait_for_timeout(500)
child = next(
frame for frame in page.frames
if frame.url.endswith("/formPage.html")
)
selected = child.locator("#multi option:checked").evaluate_all(
"options => options.map(option => option.value)"
)
frame_locator_count = page.frame_locator("#iframe1").locator("#email").count()
assert page.locator("#email").count() == 0
assert child.name == "iframe1-name"
assert child.locator("#email").count() == 1
assert selected == ["鸡蛋", "香肠"]
assert frame_locator_count == 1
print("父标题:", page.title())
print("父电子邮件匹配:", page.locator("#email").count())
print("框架数量:", len(page.frames))
print("子名称:", child.name)
print("子URL:", child.url)
print("子标题:", child.title())
print("子电子邮件匹配:", child.locator("#email").count())
print("选择的值:", selected)
print("FrameLocator电子邮件匹配:", frame_locator_count)
browser.close()
完整运行返回两个框架,零个父电子邮件匹配,一个子电子邮件匹配,预期的框架名称/标题/URL,以及来自子表单的['鸡蛋', '香肠']。
您得到的结果
验证的工作流程返回提取的表单状态和足够的框架元数据以识别其来源:
json
{
"parent_email_matches": 0,
"frame_count": 2,
"frame_name": "iframe1-name",
"frame_url": "https://www.selenium.dev/selenium/web/formPage.html",
"frame_title": "我们从这里离开",
"child_email_matches": 1,
json
{
"selected_values": ["鸡蛋", "香肠"]
}
输出建立了四个事实:父文档无法看到电子邮件字段,意图子文档可以通过名称和URL进行识别,子定位器找到一个匹配的输入,而选择的表单值来自该框架内部。当一个页面包含多个嵌入文档时,保留每个提取记录的框架名称和URL。
跨源iframe和同源策略
当父级和子级的源不同,浏览器JavaScript(如document.querySelector("iframe").contentDocument)可能会被阻止。Playwright通过浏览器自动化协议与浏览器进行通信,并将子文档显示为一个框架,因此它可以定位和与跨源文档中的元素进行交互。
这并不会消除每个边界。沙箱属性可能会限制脚本,浏览可以在您读取时替换一个框架,而浏览器扩展或PDF查看器可能不会暴露普通的HTML文档。在编写提取合同之前,请检查实际的框架树。
动态和嵌套框架
等待附加和导航
在其子文档到达最终URL之前,iframe元素可能会出现。等待子定位器或检查框架导航事件;有用的准备标记是数据承载元素,而不仅仅是父级<iframe>标签。
期望脱离
单页应用程序有时会移除并重新创建iframe。保存的框架对象可能会变得脱离。UI转换后,从其稳定名称、URL或拥有元素再次解析框架,而不是假设旧对象仍然存在。
刻意遍历嵌套框架
对于嵌套上下文,检查frame.child_frames或链式FrameLocators。在输出来源中保持父子路径,以便两个同名小部件不会混淆。
结论
iframe提取从选择正确的文档开始。枚举框架树,通过稳定名称或URL选择子框架,等待其中的元素,并在提取记录中保持框架元数据。使用FrameLocator进行简洁的操作,当您需要身份、导航或嵌套框架控制时使用Frame对象。将工作流迁移到Scrapeless会更改浏览器创建,而不是框架逻辑。
准备好从Iframe提取数据了吗?
加入正在Scrapeless社区中构建基于浏览器的提取工作流的开发人员:Discord · Telegram。
开始使用Scrapeless,查看Scrapeless定价,并阅读CDP暴露的内容,然后将相同的框架工作流迁移到托管浏览器。
常见问题
问:为什么父页面无法找到iframe内部的元素?
iframe拥有一个单独的文档。父页面定位器搜索父浏览上下文;首先选择子框架或使用FrameLocator。
问:我应该通过数组索引选择框架吗?
不应该。当页面添加小部件或广告时,框架的顺序会发生变化。优选稳定名称、URL模式或拥有的iframe元素,对模糊匹配则失败。
问:Frame和FrameLocator有什么区别?
一个Frame暴露文档身份、URL、名称、生命周期和子框架。FrameLocator通过特定的iframe元素提供简洁的定位操作。
问:Playwright可以访问跨源iframe吗?
是的,Playwright可以通过浏览器协议自动化跨源子框架,即使页面内JavaScript由于同源政策无法读取contentDocument。
问:我如何等待动态加载的iframe?
等待框架内部的数据承载定位器或已知的最终框架URL。仅仅存在iframe元素并不能证明其子文档已经完成导航。
问:如果框架被脱离,会发生什么?
保存的框架不再代表一个实时文档。在页面转换后,从稳定证据重新解析替换框架。
问:iframe提取是否合法?
嵌入并不会改变目标的数据权利或条款。检查网站和嵌入提供商的政策,在适用时遵循robots指令,保持数量有限,并为敏感或商业收集寻求法律建议。
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。



