Go语言使用Playwright自动化测试、录屏、执行js

news2025/7/15 15:42:29

Go版本的Playwright支持Chromium、Firefox和WebKit的Web自动化测试,兼容Windows、Linux和MacOS,默认支持headless无头模式,安装方便、绿色高效、兼容性强、运行速度快。
支持的主要功能有:
跨多个页面、域名和iframe的场景;
在执行操作(如单击、填充)之前自动等待元素就绪;
拦截网络活动以拦截和模拟网络请求;
模拟移动设备、地理位置和权限;
通过阴影穿透选择器支持web组件;
鼠标和键盘的原生事件;
上传和下载文件;

安装

使用go get安装playwright-go。

go get github.com/playwright-community/playwright-go

另外还需要安装驱动和浏览器依赖:

go run github.com/playwright-community/playwright-go/cmd/playwright install --with-deps
# Or
go install github.com/playwright-community/playwright-go/cmd/playwright
playwright install --with-deps

1

自动化测试

package main

import (
	"fmt"
	"log"

	"github.com/playwright-community/playwright-go"
)

func main() {
	pw, err := playwright.Run()
	if err != nil {
		log.Fatalf("could not start playwright: %v", err)
	}
	browser, err := pw.Chromium.Launch()
	if err != nil {
		log.Fatalf("could not launch browser: %v", err)
	}
	page, err := browser.NewPage()
	if err != nil {
		log.Fatalf("could not create page: %v", err)
	}
	if _, err = page.Goto("http://www.dzwww.com/xinwen/"); err != nil {
		log.Fatalf("could not goto: %v", err)
	}
	entries, err := page.QuerySelectorAll("ul.list")
	if err != nil {
		log.Fatalf("could not get entries: %v", err)
	}
	for i, entry := range entries {
		titleElement, err := entry.QuerySelector("h3 > a")
		if err != nil {
			log.Fatalf("could not get title element: %v", err)
		}
		title, err := titleElement.TextContent()
		if err != nil {
			log.Fatalf("could not get text content: %v", err)
		}
		fmt.Printf("%d: %s\n", i+1, title)
	}
	if err = browser.Close(); err != nil {
		log.Fatalf("could not close browser: %v", err)
	}
	if err = pw.Stop(); err != nil {
		log.Fatalf("could not stop Playwright: %v", err)
	}
}

playwright.Run()创建playwright对象。
pw.Chromium.Launch()创建Chromium浏览器对象。
browser.NewPage()创业一个新页面。
page.Goto打开一个url页面。
page.QuerySelectorAll使用css选择器查找所有元素。
entry.QuerySelector查找第一个元素。
titleElement.TextContent()获取inner_text文本内容。
browser.Close()关闭浏览器。
pw.Stop()关闭playwright对象。

录屏视频

package main

import (
	"fmt"
	"log"

	"github.com/playwright-community/playwright-go"
)

func main() {
	pw, err := playwright.Run()
	if err != nil {
		log.Fatalf("could not launch playwright: %v", err)
	}
	browser, err := pw.Chromium.Launch()
	if err != nil {
		log.Fatalf("could not launch Chromium: %v", err)
	}
	page, err := browser.NewPage(playwright.BrowserNewContextOptions{
		RecordVideo: &playwright.BrowserNewContextOptionsRecordVideo{
			Dir: playwright.String("videos/"),
		},
	})
	if err != nil {
		log.Fatalf("could not create page: %v", err)
	}
	gotoPage := func(url string) {
		fmt.Printf("Visiting %s\n", url)
		if _, err = page.Goto(url); err != nil {
			log.Fatalf("could not goto: %v", err)
		}
		fmt.Printf("Visited %s\n", url)
	}
	gotoPage("http://whatsmyuseragent.org")
	gotoPage("https://github.com")
	gotoPage("https://microsoft.com")
	if err := page.Close(); err != nil {
		log.Fatalf("failed to close page: %v", err)
	}
	path, err := page.Video().Path()
	if err != nil {
		log.Fatalf("failed to get video path: %v", err)
	}
	fmt.Printf("Saved to %s\n", path)
	if err = browser.Close(); err != nil {
		log.Fatalf("could not close browser: %v", err)
	}
	if err = pw.Stop(); err != nil {
		log.Fatalf("could not stop Playwright: %v", err)
	}
}

这里用到了playwright的浏览器上下文配置:playwright.BrowserNewContextOptions

执行js

package main

import (
	"fmt"
	"log"

	"github.com/playwright-community/playwright-go"
)

func main() {
	pw, err := playwright.Run()
	if err != nil {
		log.Fatal(err)
	}
	browser, err := pw.Chromium.Launch()
	if err != nil {
		log.Fatalf("could not launch browser: %v\n", err)
	}
	page, err := browser.NewPage()
	if err != nil {
		log.Fatalf("could not create page: %v\n", err)
	}
	if _, err = page.Goto("https://en.wikipedia.org/wiki/JavaScript"); err != nil {
		log.Fatalf("could not goto: %v\n", err)
	}
	// mw.config.values is the JS object where Wikipedia stores wiki metadata
	handle, err := page.EvaluateHandle("mw.config.values", struct{}{})
	if err != nil {
		log.Fatalf("could not acquire JSHandle: %v\n", err)
	}
	// mw.config.values.wgPageName is the name of the current page
	pageName, err := handle.(playwright.JSHandle).GetProperty("wgPageName")
	if err != nil {
		log.Fatalf("could not get Wikipedia page name: %v\n", err)
	}

	fmt.Printf("Lots of type casting, brought to you by %s\n", pageName)

	if err := browser.Close(); err != nil {
		log.Fatalf("could not close browser: %v\n", err)
	}
	if err := pw.Stop(); err != nil {
		log.Fatalf("could not stop Playwright: %v\n", err)
	}

}

page.EvaluateHandle可以传递js代码字符串来执行js。

并行抓取

package main

import (
	"archive/zip"
	"bytes"
	"context"
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/playwright-community/playwright-go"
)

func assertErrorToNilf(message string, err error) {
	if err != nil {
		log.Fatalf(message, err)
	}
}

func worker(id int, jobs chan Job, results chan<- Job, browser playwright.Browser) {
	for job := range jobs {
		fmt.Printf("starting (try: %d): %s\n", job.Try, job.URL)
		if job.Try >= 3 {
			job.Success = false
			job.err = fmt.Errorf("Stopped with domain %s (%w)", job.URL, job.err)
			results <- job
			continue
		}
		jobCtx, cancel := context.WithTimeout(context.Background(), time.Second*12)
		internalJobError := make(chan error, 1)
		go func() {
			internalJobError <- processJob(browser, job, jobCtx)
			cancel()
		}()
		select {
		case <-jobCtx.Done():
			job.err = fmt.Errorf("timeout (try: %d)", job.Try+1)
			job.Success = false
			job.Try++
			jobs <- job
		case err := <-internalJobError:
			if err != nil {
				job.err = err
				job.Success = false
				job.Try++
				jobs <- job
				cancel()
			} else {
				job.Success = true
				job.err = nil
				results <- job
			}
		}
	}
}

func processJob(browser playwright.Browser, job Job, ctx context.Context) error {
	context, err := browser.NewContext(playwright.BrowserNewContextOptions{
		UserAgent: playwright.String("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36"),
	})
	if err != nil {
		return fmt.Errorf("could not create context: %w", err)
	}
	defer context.Close()
	go func() {
		<-ctx.Done()
		context.Close()
	}()

	page, err := context.NewPage()
	if err != nil {
		return fmt.Errorf("could not create page: %w", err)
	}

	_, err = page.Goto("http://"+job.URL, playwright.PageGotoOptions{
		WaitUntil: playwright.WaitUntilStateNetworkidle,
	})
	if err != nil {
		return fmt.Errorf("could not goto: %s: %v", job.URL, err)
	}
	cwd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("could not get cwd %w", err)
	}
	_, err = page.Screenshot(playwright.PageScreenshotOptions{
		Path: playwright.String(filepath.Join(cwd, "out", strings.Replace(job.URL, ".", "-", -1)+".png")),
	})
	if err != nil {
		return fmt.Errorf("could not screenshot: %w", err)
	}
	return nil
}

type Job struct {
	URL     string
	Try     int
	err     error
	Success bool
}

func main() {
	log.Println("Downloading Alexa top domains")
	topDomains, err := getAlexaTopDomains()
	assertErrorToNilf("could not get alexa top domains: %w", err)
	log.Println("Downloaded Alexa top domains successfully")
	cwd, err := os.Getwd()
	if err != nil {
		assertErrorToNilf("could not get cwd %w", err)
	}
	if err := os.Mkdir(filepath.Join(cwd, "out"), 0777); err != nil && !os.IsExist(err) {
		assertErrorToNilf("could not create output directory %w", err)
	}

	pw, err := playwright.Run()
	assertErrorToNilf("could not launch playwright: %w", err)
	browser, err := pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{
		Headless: playwright.Bool(false),
	})
	assertErrorToNilf("could not launch Chromium: %w", err)

	numberOfJobs := int(math.Min(30, float64(len(topDomains))))

	jobs := make(chan Job, numberOfJobs)
	results := make(chan Job, numberOfJobs)

	for w := 1; w <= 3; w++ {
		go worker(w, jobs, results, browser)
	}

	for _, url := range topDomains[:numberOfJobs] {
		jobs <- Job{
			URL: url,
		}
	}

	for a := 0; a < numberOfJobs; a++ {
		job := <-results
		if job.Success {
			fmt.Println("success:", job.URL)
		} else {
			fmt.Println("error:", job.URL, job.err)
		}
	}

	close(jobs)
	close(results)

	assertErrorToNilf("could not close browser: %w", browser.Close())
	assertErrorToNilf("could not stop Playwright: %w", pw.Stop())
}

func getAlexaTopDomains() ([]string, error) {
	resp, err := http.Get("http://s3.amazonaws.com/alexa-static/top-1m.csv.zip")
	if err != nil {
		return nil, fmt.Errorf("could not get: %w", err)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("could not read body: %w", err)
	}
	defer resp.Body.Close()
	zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body)))
	if err != nil {
		return nil, fmt.Errorf("could not create zip reader: %w", err)
	}
	alexaFile, err := zipReader.File[0].Open()
	if err != nil {
		return nil, fmt.Errorf("could not read alexa file: %w", err)
	}
	defer alexaFile.Close()
	reader := csv.NewReader(alexaFile)
	out := make([]string, 0)
	for {
		record, err := reader.Read()
		if err == io.EOF {
			return out, nil
		}
		if err != nil {
			return nil, fmt.Errorf("could not read csv: %w", err)
		}
		out = append(out, record[1])
	}
}

参考

https://playwright-community.github.io/playwright-go/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/395902.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么led灯性价比高?家用led灯选哪种最好

人造灯具经过多年的发展&#xff0c;现在最为成熟的工艺就是led灯了&#xff0c;不仅有荧光灯高效节能、省电高亮的特点&#xff0c;而且光线可控性非常高&#xff0c;对于调光、提高光线柔和度等方面非常方便&#xff0c;保护眼睛更有效。 那么什么led灯性价比高呢&#xff0c…

JavaScript 高级5 :ES6

JavaScript 高级5 &#xff1a;ES6 Date: January 19, 2023 Sum&#xff1a;ES6新增语法、ES6内置对象扩展&#xff08;Array、String扩展方法、set数据结构&#xff09; 目标 能够说出使用let关键字声明变量的特点 能够使用解构赋值从数组中提取值 能够说出箭头函数拥有的…

基于SpringBoot的学生会管理系统 源码

StudentUnionManagementSystem 基于SpringBoot的学生会管理系统 源码 链接 目录StudentUnionManagementSystem介绍软件架构使用说明1.页面登录2.首页3.成员信息管理4.角色信息管理5.权限管理6.活动管理7.文件管理8.活动展示介绍 学生会管理系统 SpringBoot Mybatis-plus shir…

CentOS 7 怎样设置自动连接网络

作为 Linux 中的重要一员&#xff0c;CentOS 7 始终把安全放在第一位&#xff0c;开机以后并不会自动启动网络。而且&#xff0c;相比 Windows 下只需点击几下鼠标便能搞定配置&#xff0c;在 CentOS 7 中要想设置开机自动连接网络&#xff0c;简直可以说是需要具有“专业级水准…

手动实现 数组和链表

数组和链表示意图 目的 不用java标准库 自己时间 一个 arraylist 和 linked list 区别 数组 数组 支持 随机访问 可以立即访问 根据 第一个元素的位置&#xff0c; 推算出 第三个地方的位置 如下图所示&#xff0c;假设我们知道 头的位置是X 数组是 int类型 每个位置就是 …

Qt音视频开发21-mpv内核万能属性机制

一、前言 搞过vlc内核后又顺带搞了搞mpv内核&#xff0c;mpv相比vlc&#xff0c;在文件数量、sdk开发便捷性方面绝对占优势的&#xff0c;单文件&#xff08;可能是静态编译&#xff09;&#xff0c;不像vlc带了一堆插件&#xff0c;通过各种属性来set和get值&#xff0c;后面…

第十四届蓝桥杯三月真题刷题训练——第 5 天

目录 题目1&#xff1a;数的分解 题目描述 运行限制 代码&#xff1a; 题目2&#xff1a;猜生日 题目描述 运行限制 代码&#xff1a; 题目3&#xff1a;成绩分析 题目描述 输入描述 输出描述 输入输出样例 运行限制 代码&#xff1a; 题目4&#xff1a;最大和…

Vue3做出B站【bilibili】 Vue3+TypeScript【快速入门一篇文章精通系列(一)前端项目案例】

本项目分为二部分 1、后台管理系统&#xff08;用户管理&#xff0c;角色管理&#xff0c;视频管理等&#xff09; 2、客户端&#xff08;登录注册、发布视频&#xff09; Vue3做出B站【bilibili】 Vue3TypeScript【快速入门一篇文章精通系列&#xff08;一&#xff09;前端项目…

vue3 拖拽 穿梭框

文章目录期望结果当前技术栈实现方法安装 sortablejs导入 sortablejs视图 通过 id 绑定 sortablejs 数据通过 data-xxx 自定义属性 挂载ts中 通过id获取dom 实现拖拽 getNewArr函数通过自定义属性对数据做处理以下是全部代码官网链接下面是文档UsageOptionsgroup optionsort op…

代码随想录之哈希表(力扣题号)

242. 有效的字母异位词 直接用数组模拟哈希表 只有小写字母&#xff0c;开26的数组就可以了 class Solution {public boolean isAnagram(String s, String t) {//24-28int[] hash new int[26];Arrays.fill(hash,0);for(int i0;i<s.length();i){hash[s.charAt(i)-a];}for(i…

2023年3月全国数据治理工程师认证DAMA-CDGA/CDGP火热报名中...

弘博创新是DAMA中国授权的数据治理人才培养基地&#xff0c;贴合市场需求定制教学体系&#xff0c;采用行业资深名师授课&#xff0c;理论与实践案例相结合&#xff0c;快速全面提升个人/企业数据治理专业知识与实践经验&#xff0c;通过考试还能获得数据专业领域证书。 DAMA认…

【致敬女神】HTMLReport应用之Unittest+Python+Selenium+HTMLReport项目自动化测试实战

HTMLReport应用之UnittestPythonSeleniumHTMLReport项目自动化测试实战1 测试框架结构2 技术栈3 实现思路3.1 使用HtmlTestRunner3.2 使用HTMLReport4 TestRunner参数说明4.1 源码4.2 参数说明5 框架代码5.1 common/reportOut.py5.2 common/sendMain.py5.3 report5.3.1 xxx.htm…

ARM架构下使用Docker安装Nacos

大家好&#xff0c;我是中国码农摘星人。 欢迎分享/收藏/赞/在看&#xff01; 注意&#xff1a;以下内容仅适用于 ARM 架构&#xff0c;X86 及 AMD 架构理论类似&#xff0c;只需要修改配置即可。 构建 MySQL 8.x 镜像 MySQL 5.x 版本没有 ARM 架构的镜像 FROM mysql:8.0.32 A…

Java 8 排序

今天分享 Java 8 进行排序的 10 个姿势&#xff0c;其实就是把 Java 8 中的 Lambda、Stream、方法引用等知识点串起来 传统排序 现在有一个 List 集合&#xff1a; public static List<User> LIST new ArrayList() {{add(new User("Lisa", 23));add(new Us…

三维人脸实践:基于Face3D的渲染、生成与重构

face3d: Python tools for processing 3D face git code: https://github.com/yfeng95/face3d paper list: PaperWithCode 该方法广泛用于基于三维人脸关键点的人脸生成、属性检测&#xff08;如位姿、深度、PNCC等&#xff09;&#xff0c;能够快速实现人脸建模与渲染。推荐…

如何判断多账号是同一个人?用图技术搞定 ID Mapping

原文出处&#xff1a;https://discuss.nebula-graph.com.cn/t/topic/11873 本文是一个基于图数据库 NebulaGraph 上的图算法、图数据库、图神经网络的 ID-Mapping 方法综述&#xff0c;除了基本方法思想的介绍之外&#xff0c;我还给大家弄了可以跑的 Playground。 基于图数据…

浏览器:浏览器指纹

一、引子 场景一、绑定用户与浏览器&#xff08;设备&#xff09;&#xff0c;比如某一个网站的账号给到用户&#xff0c;用户只能在自己的电脑的某浏览器使用。 场景二、精准推送广告。 场景三、公司做营销活动&#xff0c;防止活动奖品被程序薅羊毛。 等等场景我们有什么…

Qt配置VS的编译环境(以MSVC2015 64bit为例)

目录 一、原因 二、VS2015安装 三、配置套件&#xff08;Kits&#xff09; 一、原因 很多时候&#xff0c;由于VS版本切换&#xff0c;需要从高版本切换到低版本&#xff0c;或者从低版本升级到高版本&#xff0c;例如VS2019到VS2015&#xff0c;或者VS2010到VS2015。 以VS2…

辽宁千圣文化:抖音店铺怎么做二次优化?

抖音商品卡订单是指永华在抖音、抖音极速版&#xff0c;通过直播的方式出现短视频页面商品卡之后&#xff0c;直接成交商品详情页直接成交后的订单&#xff0c;那么跟着辽宁千圣文化小编来一起看看吧&#xff01;一.与政策有关1.什么是「商品卡订单」&#xff1f;用户通过抖音、…

FCN网络(Fully Convolutional Networks)

首个端到端的针对像素级预测的全卷积网络 原理&#xff1a;将图片进行多次卷积下采样得到chanel为21的特征层&#xff0c;再经过上采样得到和原图一样大的图片&#xff0c;最后经过softmax得到类别概率值 将全连接层全部变成卷积层&#xff1a;通常的图像分类网络最后几层是全…