玩转浏览器自动化(6)Playwright 使用 JavaScript

在之前的章节中,我们已经掌握了 Playwright 的基本功能,包括创建浏览器和页面,定位元素并与之交互等。这些功能已经足够满足大部分的浏览器自动化需求。

然而,在实际应用中,我们可能需要更复杂的操作,这时候 Playwright 的 API 就不足以满足我们的需求了。这时候,我们就需要使用更底层的方式来进行操作,即直接运行 JavaScript。.

为了实现这些高级功能,我们需要了解 Playwright 中如何使用 JavaScript,并学习一些基本的 JavaScript 语法和编程技巧。这将有助于我们更好地理解 Playwright 的运行原理,并更加灵活地应对各种场景的需求。

JavaScript 基础

JavaScript 是一种高级编程语言,被广泛应用于 Web 应用程序中,它可以用于操作 DOM、获取页面数据、发送网络请求等等。

变量

JavaScript 的语法和基本数据类型与其他编程语言类似,其中变量是保存数据的基本单元,函数则用于封装可重复使用的代码。在 JavaScript 中,变量可以是数字、字符串、布尔值、对象或数组等类型。变量声明使用 var 关键字,如下所示:

var a = 1;

var b = 'hello';

var c = false;

var d = {name: 'Tom', age: 20};

var e = [1, 2, 3];

这里我们声明了五个变量,分别保存了不同类型的数据。

函数

函数的声明使用 function 关键字,可以接受多个参数,并且在函数中参数是局部变量,如下所示:

function add(a, b) {
var result = a + b;
return result;
}

在上面的代码中,我们声明了一个名为 add 的函数,它接收两个参数 a 和 b,并将它们的和返回。

Lambda 表达式

Lambda 表达式是 JavaScript 中的匿名函数,通常用于将行为作为值传递给另一个函数。Lambda 表达式的声明使用箭头符号 =>,如下所示:

var add = (a, b) => a + b;

add(1, 2);

在上面的代码中,我们使用 Lambda 表达式声明了一个函数并将其保存在变量 add 中,然后调用它。

为了更好地理解这些基础知识,下面是一个在浏览器开发者工具中运行上述代码的结果截图:

玩转浏览器自动化(6)Playwright 使用 JavaScript

EvaluateAsync 方法

在 Playwright 中,我们可以使用 page.EvaluateAsync 方法来运行 JavaScript 代码。该方法可以在浏览器的上下文中运行 JavaScript 函数,并将结果带回到 Playwright 的环境中。例如,我们可以使用以下代码在浏览器中执行一个简单的加法运算:

var sum = await page.EvaluateAsync<int>("()=>1 + 2");  

这个 Lambda 表达式返回了 1 + 2 的结果,然后我们将其赋值给变量 sum。需要注意的是,EvaluateAsync 方法的参数是一个 Lambda 表达式,可以直接在其中使用浏览器的全局变量,比如 window 和 document。

除了在页面对象上使用,EvaluateAsync 方法还可以在元素上使用。例如,我们可以使用以下代码获取页面当前的 URL:

var href = await page.EvaluateAsync<string>("document.location.href");  

另外,EvaluateAsync 方法还可以接受一个可选参数,用于将 Playwright 环境中的值传递给浏览器。该参数的类型可以是任何基元类型,例如:

Console.WriteLine(await page.EvaluateAsync<int>("a => a", 1));  
Console.WriteLine(await page.EvaluateAsync<string>("a => a", "hello"));  
Console.WriteLine(await page.EvaluateAsync<bool>("a => a", false));  

参数值的类型也可以是数组,例如:

Console.WriteLine(await page.EvaluateAsync<int>("array => array.length", new int[] { 1, 2, 3 }));  

如果想同时传入多个参数,可以使用一个对象来表示,然后通过 变量名.属性名的方式来访问,例如:

Console.WriteLine(await page.EvaluateAsync<int>("o => o.a + o.b", new { a = 1, b = 2 }));  

JSHandle/ElementHandle

使用 Playwright 中的 JSHandle/ElementHandle 对象可以更方便地操作页面元素。JSHandle 表示页面内的 JavaScript 对象,可以通过 page.EvaluateHandleAsync 方法创建;而 ElementHandle 则表示页面内的 DOM 元素,可以通过 page.QuerySelectorAsync 方法创建。虽然它们在本质上是相同的,但是 ElementHandle 可以访问 DOM 元素的属性和方法,而 JSHandle 对象则不一定。

当需要定位一个无法用普通方式定位到的元素时,可以使用 JSHandle/ElementHandle 来实现。比如某些 DOM 元素是通过 JavaScript 动态生成的,这时候,就可以通过 JSHandle/ElementHandle 来定位元素。例如,可以使用以下代码来创建一个动态生成的按钮元素:

var jsHandle = await page.EvaluateHandleAsync("() => document.createElement('button')");  
  
var elementHandle = jsHandle.AsElement();  

一旦获得了 JSHandle/ElementHandle 对象,就可以在其上调用 EvaluateAsync 方法来运行 JavaScript 代码了。与 page.EvaluateAsync 方法不同的是,Lambda 表达式的第一个参数是 JSHandle/ElementHandle 本身。例如,可以使用以下代码来点击一个按钮:

await jsHandle.EvaluateAsync("handle => handle.click()");  

当然,你也可以为 EvaluateHandleAsync 方法传入参数,它将作为第二个参数传入 Lambda 表达式。例如,可以使用以下代码来给一个输入框赋值:

var jsHandle = await page.EvaluateHandleAsync("() => document.getElementById('kw')");  
  
await jsHandle.EvaluateAsync("(handle,str) => handle.value = str", "Playwright");  

此外,JSHandle 对象也可以作为参数传递给 page.EvaluateAsync 方法。因此,以下代码与上面的代码是等价的:

var  jsHandle = await page.EvaluateHandleAsync("() => document.getElementById('kw')");  
  
await page.EvaluateAsync("o => o.handle.value = o.str", new { handle =  jsHandle, str = "Playwright" });  

Locator vs ElementHandle

Locator 和 ElementHandle 是 Playwright 中用于定位 DOM 元素的两个重要概念。虽然它们都可以找到页面中的元素,但它们之间有一些区别。

ElementHandle 是一个指向特定 DOM 元素的引用。一旦获取了 ElementHandle,它将指向该元素,即使在操作期间该元素被渲染成完全不同的组件,ElementHandle 仍然会指向原来的元素,这可能会导致一些意想不到的行为。

相比之下,Locator 包含如何检索元素的逻辑,并且每次调用时都会重新获取 DOM 元素。这意味着如果元素在操作期间被更改或替换,Locator 将返回新元素,而不是原始元素。因此,在大多数情况下,使用 Locator 更为可靠。

JavaScript互操作

Playwright 不仅允许我们从C#调用JavaScript函数,还可以让JavaScript调用C#函数。这种相互调用的方式可以大大提高我们的代码灵活性和可读性。

为了注册我们的C#方法以供JavaScript使用,我们可以使用page.ExposeFunctionAsync方法。该方法包含两个参数:name和callback。其中name是在JavaScript中调用该方法时使用的名称,而callback是在Playwright上下文中回调的C#方法。

以下是一个示例,我们在C#中注册一个名为sha256的方法,并在JavaScript中调用它:

using var playwright = await Playwright.CreateAsync();    
await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = false });  
  
var page = await browser.NewPageAsync();  
  
await page.ExposeFunctionAsync("sha256", (string input) =>  
{  
    return Convert.ToBase64String(  
        SHA256.Create().ComputeHash(System.Text.Encoding.UTF8.GetBytes(input)));  
});  
  
await page.SetContentAsync(@"<script>  
    async function onClick() {  
    document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');  
    }  
</script>  
<button onclick='onClick()'>Click me</button>  
<div></div>");  
  
await page.ClickAsync("button");  

玩转浏览器自动化(6)Playwright 使用 JavaScript

在这段代码中,我们使用ExposeFunctionAsync声明了一个回调函数,它的名称是sha256。该回调函数只有一个参数input,它的功能是计算传入字符串的SHA256哈希值。由于JavaScript本身不支持这种哈希算法,所以我们需要使用C#来实现。然后,在JavaScript中,我们使用window.sha256回调该函数,并获得C#方法的返回值。

总结

本章介绍了如何使用 Playwright 的 JavaScript 互操作特性,包括通过 EvaluateAsync 方法调用 JavaScript 函数和通过 ExposeFunctionAsync 方法注册 JavaScript 函数回调。通过这些技术,您可以更加轻松地实现自动化任务。

在下一章中,我们将学习 Playwright 的另一个重要主题,自动化测试。