Playwright入门 附实战项目
什么是 Playwright?
Playwright 是一个开源工具,用于自动化浏览器测试。它主要用于测试网站在不同浏览器和平台之间的功能兼容性。 Playwright 可以很好地模拟用户的操作,如点击按钮,输入文本等。它还可以模拟输入事件,如键盘和鼠标事件,以及浏览器的特定事件,如浏览器窗口的大小改变等。
Playwright 的优势
和 Selenium 的对比
Playwright VS Selenium VS Puppeteer VS Cypress - 虫师 - 博客园
在使用过程中,个人感觉 Playwright 更符合我们.Neter 的使用习惯,提供的 API 更简洁方便。学习成本不高,只需要了解基本的前端知识就可以使用。目前官方提供了一套比较详细的文档 https://playwright.dev/dotnet/docs/input 可供查看。缺点方面:作为一款新兴的自动化框架,在资源方面肯定是和 selenium 没法比较的。
Playwright 如何使用?
第一个简单示例
访问 baidu.com官网并截图
using Microsoft.Playwright;
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(new() { Channel = "msedge"});
var page = await browser.NewPageAsync();
await page.GotoAsync("https://www.baidu.com");
var screenshotAsync = await page.ScreenshotAsync(new PageScreenshotOptions { Path = "screenshot.png" });
代码首先创建了一个 Playwright 实例,然后使用 edge 启动了一个浏览器。接着,新建了一个页面,并使用 GotoAsync 方法跳转到了 "https://www.baidu.com" 然后 ScreenshotAsync 方法将页面截图保存到了 "screenshot.png" 文件中。
几个重要类型
在上 Playwright 示例 中,有几种重要的类型,如下:
playwright
: 主要的 Playwright 类型,用于启动和管理浏览器实例。browser
: 一个 Browser 是一个 Chromium, Firefox 或 WebKit(plarywright 支持的三种浏览器)的实例 plarywright 脚本通常以启动浏览器实例开始,以关闭浏览器结束。浏览器实例可以在 headless 或 head 模式下启动。context
: 一个 BrowserContex 就像是一个独立的匿名模式会话(session),非常轻量,但是又完全隔离。每个 browser 实例可有多个 BrowserContex,且完全隔离。比如可以在两个 BrowserContext 中登录两个不同的账号,也可以在两个 context 中使用不同的代理。page
: 表示浏览器页面的类型,相当于一个 tab
入门
无头与有头
有头和无头你可以理解为是否让程序静默执行,还是显示他的模拟操作(即显示他的模拟浏览器操作,可视化)(即有程序浏览器 UI)
上面的程序没有设置有头或无头,所以默认是无头的。
我们想要看看程序是如何模拟的,可以开启有头模式。
await using var browser = await playwright.LaunchAsync(new() { Channel = "msedge", Headless = false, SlowMo = 100 });
模拟移动浏览器https://playwright.dev/dotnet/docs/emulation
有些网页在手机上显示才是正常的,有时候我们需要特定的手机环境自动化操作,而 Playwright 也提供了相关了驱动直接设置浏览器打开方式。还内置了常用的手机 DPI 设置。
vvar iphone12 = playwright.Devices=["iPhone 12"];
await using var context = await browser.NewContextAsync(iphone12);
获取网页内容
获取百度热榜内容
如果我们按照控制台获取元素的方式(用 jquery 的方式),就是:
$(".title-content-title").text()
用 playwright 可以用以下方式来实现
var topics = await page.QuerySelectorAllAsync(".title-content-title");
foreach (var topic in topics)
{
Console.WriteLine(await topic.InnerTextAsync());
}
input 输入和按钮点击
await page.Locator("#kw").FillAsync("什么是playwright");
await page.Locator("#s_tab > div > a.s-tab-item.s-tab-video").ClickAsync();//await page.ClickAsync("#s_tab > div > a.s-tab-item.s-tab-video");
进阶
访问指定网页
访问网页和跳转网页都可以使用 page.GotoAsync()。
按照官网文档,调用 page.GotoAsync(url) 后页面加载过程:
- 设定 url
- 通过网络加载解析页面
- 触发 page.on("domcontentloaded") 事件
- 执行页面的 js 脚本,加载静态资源
- 触发 page.on("load") 事件
- 页面执行动态加载的脚本
- 当 500ms 都没有新的网络请求的时候,触发 networkidle 事件
page.GotoAsync(url) 会跳转到一个新的链接。默认情况下 Playwright 会等待到 load 状态。如果我们不关心加载的 CSS 图片等信息,可以改为等待到 domcontentloaded 状态,如果页面是 ajax 加载,那么我们需要等待到 networkidle 状态。如果 networkidle 也不合适的话,可以采用 page.WaitForSelectorAsync()等待某个元素出现。
更多的选择器表达式
在上面的代码中,我们使用了 CSS 表达式(比如#button)来选取元素。实际上,Playwright 还支持 XPath 和自己定义的两种简单表达式,并且是自动识别的。
获取更多元素属性的方式
我们可以直接从整个 page 中获取某个元素属性或子元素属性,也可以通过元素获取子元素属性或者本身的属性。
await page.TitleAsync();
await page.QuerySelectorAsync("");
await page.ContentAsync();
await page.InnerTextAsync("");
await page.InnerHTMLAsync("");
await page.GetAttributeAsync("","");
page.EvaluateAsync<int>(@"()=>{
var top = document.querySelector('.renderTargetContainer').offsetTop;
var height = document.querySelector('.renderTargetContainer').offsetHeight;
return top + height;
}");
必要的等待才能有更多操作
Playwright 会自动等待元素处于可操作的稳定状态。但是有时候我们会发现这个并不称心如意,所以我们可以手动设置一些等待操作。我们可以用 page.waitfor* 函数来手工等待:
//等待某个请求
await page.WaitForRequestAsync(request => request.Url.Contains("example.com"));
//等待某个事件
await page.WaitForEventAsync("eventName");
//等待某个URL
await page.WaitForUrlAsync("<u>https://www.example.com</u>");
//等待1000ms的时间
await page.WaitForTimeoutAsync(1000);
//等待某个元素加载
await page.WaitForSelectorAsync("#selector");
//等待某个回应
await page.WaitForResponseAsync(response => response.Url.Contains("example.com"));
//等待页面重定向并加载完毕
await page.WaitForNavigationAsync();
//等待某个状态被激发
await page.WaitForLoadStateAsync(LifecycleEvent.DOMContentLoaded);
//等待某个函数被执行完毕
await page.WaitForFunctionAsync("document.querySelector('#selector') !== null");
无头?速速显形!
当我们使用无头模式的时候,我们看不到浏览器背着我们干了什么勾当。这时候就需要我们委托 playwright 来监控浏览器的一举一动。它提供了一个 video 录制功能。具体的使用方法和使用例子直接看下面:
复用 Cookies 等认证信息 Authentication | Playwright .NET
playwright 支持 Cookies、Local storage、Session storage 多种保存认证状态的方式。这个是 Playwright 特别方便的一点,他可以直接导出 Cookies 和 LocalStorage, 然后在新的 Context 中使用。下面是一个登录信息保存与复用的例子。如果保存了之前登录的 cookie,我们访问网页的时候他就不会跳转页面,再要求我们登录。
var context = await browser.NewContextAsync(new BrowserNewContextOptions() { StorageStatePath = authPath });
await context.StorageStateAsync(new BrowserContextStorageStateOptions() { Path = authPath });
监听网络事件Network | Playwright .NET
通过 page.on(event, fn) 可以来注册对应事件的处理函数,其中比较重要的就是 request 和 response 两个事件。
可以通过 page.on("request") 和 page.on("response") 来监听请求和响应事件。
page.Request += (_, request) => Console.WriteLine(">> " + request.Method + " " + request.Url);
page.Response += (_, response) => Console.WriteLine("<< " + response.Status + " " + response.Url);
拦截网络事件
通过 context.route, 还可以伪造修改拦截请求等。比如说,拦截所有的图片请求以减少带宽占用:
await page.RouteAsync("**/*.{png,jpg,jpeg}", route => route.AbortAsync());
// Abort based on the request type
await page.RouteAsync("**/*", async route => {
if ("image".Equals(route.Request.ResourceType))
await route.AbortAsync();
else
await route.ContinueAsync();
});
甚至可以玩一些骚操作(什么请求都不给他发出去,还给他一个 404
await page.RouteAsync("**/*", async route => {
await route.FulfillAsync(new() { Status = 404, Body = "error" });
});
灵活设置代理Network | Playwright .NET
Playwright 还可以很方便地设置代理。Puppeteer 在打开浏览器之后就无法在更改代理了,对于爬虫类应用非常不友好,而 Playwright 可以通过 Context 设置代理,这样就非常轻量,不用为了切换代理而重启浏览器。
var proxy = new Proxy { Server = "per-context" };
await using var browser = await BrowserType.LaunchAsync(new()
{
// Browser proxy option is required for Chromium on Windows.
Proxy = proxy
});
using var context = await Browser.NewContextAsync(new()
{
Proxy = new Proxy { Server = "http://myproxy.com:3128" })
});