玩转浏览器自动化(2)Playwright 基本概念

在 Playwright 中,我们可以清晰地映射出浏览器的各个部分,例如浏览器、页面等等。这些都是 Playwright 的基本概念,我们需要深入了解这些概念,才能更加高效地使用 Playwright。

Browser(浏览器)

在 Playwright 中,Browser 代表着一个浏览器实例。为了创建浏览器实例,我们可以使用 playwright.[BrowserType],其中 BrowserType 可以是 Chromium、Firefox 或者 Webkit。比如下面的代码:.

var chromium = playwright.Chromium;  
var browser = await chromium.LaunchAsync();  

不过需要注意的是,这里使用的浏览器类型是 Chromium,而不是我们熟知的 Chrome。在 Playwright 中,浏览器类型指的是浏览器的内核,而不是浏览器本身。比如 Google Chrome、Microsoft Edge、Opera 都是基于 Chromium 内核的浏览器,所以他们的浏览器类型都是 Chromium。而 Safari 则是基于 Webkit 内核的浏览器。

当然,Playwright 也可以在机器上已有的浏览器基础上运行:

var chromium = playwright.Chromium;  
var browser = await chromium.LaunchAsync(new BrowserTypeLaunchOptions { Channel = "chrome" });  

需要注意的是,无论你使用哪种浏览器类型,它们都支持无头浏览器模式。

Playwright 支持无头浏览器和有界面浏览器。无头浏览器指的是没有界面的浏览器,只有后台进程。相比有界面浏览器,无头浏览器启动速度更快,可以在无界面服务器上运行,不需要图形界面,也不会占用本地输入资源,避免影响用户体验。

Playwright默认使用无头浏览器,但是也支持有界面浏览器。如果需要使用有界面浏览器,可以通过设置Headless属性为false来实现。

var browser = await chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
    Headless = false
});

在开发过程中,使用有界面浏览器方便调试;在生产环境中,使用无头浏览器可以提高效率。

连接现有浏览器

除了创建新的浏览器实例,Playwright还支持连接到一个已经存在的浏览器实例,这在许多浏览器自动化应用场景中非常有用。例如,可以在已经登录到需要双因素身份验证的帐户或者已经完成了一些手动操作的浏览器中继续自动化程序,或者将控制权交还给手动操作。

让我们来看一下如何连接已经存在的 Chrome 浏览器。首先,我们需要打开 Chrome 浏览器的调试开关,这可以通过传递--remote-debugging-port参数来实现。在 Windows 上,你可以在命令提示符下运行以下命令:"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222

接着,在打开的浏览器的地址栏中输入:http://127.0.0.1:9222/json/version。这将显示一些关于该浏览器的元数据,包括webSocketDebuggerUrl属性。这个属性的值是我们连接到现有浏览器所需的地址。

玩转浏览器自动化(2)Playwright 基本概念

现在,我们可以通过 chromium.ConnectAsync 来连接这个浏览器实例进行后续自动化操作。例如,我们可以使用以下代码获取已打开页面的标题:

var browser = await chromium.ConnectOverCDPAsync("ws://127.0.0.1:9222/devtools/browser/762eb309-53e4-44cf-b353-497a98e8da83");  
  
var page = browser.Contexts.First().Pages.First();  
  
var title = await page.InnerTextAsync("title");  

在这里,我们使用了 ConnectOverCDPAsync 方法,该方法使用 Chrome DevTools 协议将 Playwright 附加到现有浏览器实例。

Chrome DevTools 协议

Chrome DevTools 协议是一套由 Chrome 浏览器提供的远程调试协议,可以通过 WebSocket 连接来控制浏览器。它可以帮助开发者获取浏览器的各种信息,例如页面的标题、URL、DOM 树、CSS 样式、网络请求等等。同时,它也可以控制浏览器,例如点击页面上的某个元素、输入文本、截图等等。这个开放的协议可以被任何人用于与 Chrome 进行交互,而 Playwright 就是基于这个协议来实现的。

使用 Chrome DevTools 协议的方式非常简单,只需要在浏览器中按下 F12,就可以打开开发者工具,这个工具正是使用了 Chrome DevTools 协议。

玩转浏览器自动化(2)Playwright 基本概念

BrowserContext(浏览器上下文)

浏览器上下文(Browser Context)是指浏览器实例中的一个独立的隔离环境,每个上下文都有自己的Cookie、缓存、会话等数据。不同的上下文之间相互隔离,每个上下文都有自己的用户数据目录,因此你可以在同一个浏览器实例中运行多个上下文,每个上下文都有自己的数据。

每个浏览器实例都可以有多个浏览器上下文,你可以通过 browser.Contexts 属性获得所有打开的浏览器上下文。如果你想要创建一个新的上下文,可以使用 browser.NewContextAsync() 方法。

需要注意的是,浏览器上下文和普通的浏览器窗口的概念不同,不同的浏览器上下文会在同一个浏览器实例上创建隔离的状态,你可以类比成 Chrome 的无痕式窗口或者 Edge 的 InPrivate 窗口;而如果使用 window.open 方法打开一个新的浏览器窗口,这个窗口使用的还是和弹出它的父页面相同的浏览器上下文。

如果我们使用 Chrome 浏览器打开一个普通窗口和一个无痕式窗口,通过开发者工具,可以看到它们使用的是不同的Cookie。下面我们用代码来验证一下:

var browserContext1 = await browser.NewContextAsync();

var cookie1 = await GetCookie(browserContext1);

Console.WriteLine(cookie1);

var browserContext2 = await browser.NewContextAsync();

var cookie2 = await GetCookie(browserContext2);

Console.WriteLine(cookie2);

static async Task<string> GetCookie(IBrowserContext browserContext)
{
    var page = await browserContext.NewPageAsync();
    await page.GotoAsync("https://www.baidu.com");
    var cookies = await browserContext.CookiesAsync();
    return cookies.First(p => p.Name == "BAIDUID").Value;
}

通过 browser.NewContextAsync 方法创建了两个浏览器上下文,并通过自定义的 GetCookie 方法获取每个浏览器上下文中的 Cookie。运行结果显示它们的Cookie是不一样的,说明两个上下文是相互隔离的。

玩转浏览器自动化(2)Playwright 基本概念

Page(页面)

Page 是指浏览器上下文中的单个标签页或弹出窗口。一个浏览器上下文可以拥有多个页面,可以通过 browserContext.Pages 属性获取当前上下文中所有打开的页面。如果需要创建一个新页面,可以使用 browserContext.NewPageAsync 方法,也可以使用 browser.NewPageAsync 方法在新的浏览器上下文中创建一个新页面。

下面我们通过代码来验证一下:

var browserContext = await browser.NewContextAsync();  
  
var page1 = await browserContext.NewPageAsync();  
  
var page2 = await browserContext.NewPageAsync();  
  
var page3 = await browser.NewPageAsync();  
  
Console.WriteLine(browserContext.Pages.Count());  

以上代码通过 browserContext.NewPageAsync 方法创建了 2 个页面,通过 browser.NewPageAsync 方法创建了 1 个页面。运行结果如下图所示:

玩转浏览器自动化(2)Playwright 基本概念

可以看到,通过 browserContext.NewPageAsync 方法创建的页面被统计到了 browserContext.Pages,而通过 browser.NewPageAsync 方法创建的页面并没有统计到 browserContext 中。

导航页面

在 Playwright 中,Page 是非常重要的概念,可以通过它来操作浏览器页面的各种元素。其中最常用的方法之一是导航页面。

在 Playwright 中,通过调用page.GotoAsync方法来导航页面,该方法接收一个URL参数,用于指定要导航的页面。例如,以下代码将导航到百度首页:

await page.GotoAsync("https://www.baidu.com");  

除了必须的URL参数,page.GotoAsync还接收可选的options参数,用于指定导航的行为。options参数包含了以下属性:

  • Timeout 可以设置页面导航的超时时间。默认值是 30 秒,如果页面导航时间超过这个时间,就会抛出超时异常。如果需要设置不同的超时时间,可以在调用 page.GotoAsync() 方法时传入一个 PageGotoOptions 对象,并设置 Timeout 属性的值。但是更好的做法是使用 Page.SetDefaultNavigationTimeout(timeout) 或 Page.SetDefaultTimeout(timeout) 方法来设置全局的超时时间,这样就不用每次都传入 options 参数了。

  • WaitUntil 用来指定页面导航完成的条件。它的默认值是 Load,也就是当整个页面和依赖资源都加载完成时,认为页面导航完成。除此之外,还可以选择 DOMContentLoaded、NetworkIdle 和 Commit。

  • Referer 用来指定请求的来源页面。如果不指定,它的默认值是当前页面的 URL。Referer 是一个 HTTP 头,浏览器将它发送给服务器,告诉它是哪个页面正在请求该资源。有些网站可能会把它作为一种验证机制,以确保请求是从网站允许的页面发出的,而不是从其他地方(比如脚本)发出的,以便防止恶意攻击。

Frame(框架)

Frame代表页面中的一个框架,它是指浏览器页面中的一个嵌套的独立渲染区域,比如页面中的<iframe>元素。每个页面都可以有多个框架,可以通过page.Frames属性获得当前页面中所有的框架。每个页面都有一个主框架(MainFrame),如未显式指定框架,所有页面交互(如点击按钮)假定都在主框架中操作。可以通过frame.ChildFrames属性获得当前框架下的子框架。

下面我们用代码来验证一下:

var page = await browser.NewPageAsync();
await page.GotoAsync("a.html");

foreach (var frame in page.Frames)
    Console.WriteLine($"{frame.Url.Split('/').Last()}");

Console.WriteLine("");
Console.WriteLine("显示 Frame 嵌套树:");
DumpFrameTree(page.MainFrame, string.Empty);

static void DumpFrameTree(IFrame frame, string indent)
{
    Console.WriteLine($"{indent}{frame.Url.Split('/').Last()}");
    foreach (var child in frame.ChildFrames)
        DumpFrameTree(child, indent + "    ");
}

在示例代码中,我们打开了一个包含两个<iframe>元素的HTML文件。

玩转浏览器自动化(2)Playwright 基本概念

并使用page.Frames属性获取了当前页面中所有的框架。我们还使用page.MainFrame属性获取了当前页面的主框架,使用frame.ChildFrames属性获取了当前框架下的子框架。最后,我们展示了Frame嵌套树的示例。

玩转浏览器自动化(2)Playwright 基本概念

总结

本章我们介绍了 Playwright 中的 Browser、BrowserContext、Page、Frame 等概念,以及它们之间的关系。

在下一章中,我们将介绍 Playwright 中的元素定位。