Go语言调用Chrome浏览器去进行截图的操作,对电脑的性能要求比较高,所以速度比较有限,但是目前来看这种方式可以最佳的去获取网页加载后的结果。

main.go

package mainimport ("context""errors""flag""fmt""io/ioutil""log""net/url""os""path/filepath""strings""sync""sync/atomic""time""github.com/chromedp/chromedp"
)// 任务结构
type Task struct {URL      stringFilename string
}// 域名黑名单,包含关键字的域名将被跳过
var blacklist = []string{"edu.cn", "gov.cn"}var (totalTasks    int64 // 总任务数finishedTasks int64 // 已完成任务数
)func main() {start := time.Now()defer func() {if r := recover(); r != nil {log.Printf("程序异常退出: %v", r)}}()// 定义命令行参数,增加初始等待时间参数urlFile := flag.String("urls", "urls.txt", "包含URL列表的文件路径")outputDir := flag.String("output", "screenshots", "截图保存的目录")workers := flag.Int("workers", 50, "并发工作线程数(建议1~3)")width := flag.Int("width", 1280, "浏览器窗口宽度")height := flag.Int("height", 800, "浏览器窗口高度")fullPage := flag.Bool("full", false, "是否截取整个页面")timeout := flag.Int("timeout", 20, "每个任务的超时时间(秒,建议大于页面加载等待时间,默认120)")retry := flag.Int("retry", 3, "失败重试次数")initialWait := flag.Int("initialWait", 1, "初始等待时间(秒),用于分散任务启动")flag.Parse()// 确保timeout参数合理if *timeout <= 30 {log.Printf("警告:timeout参数过小,已自动调整为60秒以避免context canceled错误!")*timeout = 60}// 创建输出目录if _, err := os.Stat(*outputDir); os.IsNotExist(err) {if err := os.MkdirAll(*outputDir, 0755); err != nil {log.Fatalf("创建输出目录失败: %v", err)}}// 读取URL列表urls, err := readURLs(*urlFile)if err != nil {log.Fatalf("读取URL文件失败: %v", err)}if len(urls) == 0 {log.Fatal("URL列表为空")}// 统计总任务数totalTasks = int64(len(urls))// 创建任务通道,增加缓冲大小taskCh := make(chan Task, len(urls))// 填充任务通道go func() {for _, url := range urls {// 生成文件名filename := generateFilename(url, *outputDir)taskCh <- Task{URL: url, Filename: filename}}close(taskCh)}()// 创建等待组var wg sync.WaitGroup// 启动进度监控协程go func() {startTime := time.Now()for {done := atomic.LoadInt64(&finishedTasks)total := totalTaskselapsed := time.Since(startTime).Seconds()var speed float64 = 0if elapsed > 0 {speed = float64(done) / elapsed}remain := 0.0if speed > 0 {remain = float64(total-done) / speed}percent := float64(done) / float64(total) * 100fmt.Printf("\r进度: %d/%d (%.2f%%) | 速度: %.2f/秒 | 已用: %.0fs | 预计剩余: %.0fs",done, total, percent, speed, elapsed, remain)if done >= total {fmt.Println()break}time.Sleep(1 * time.Second)}}()// 启动工作线程,增加启动间隔log.Printf("开始处理 %d 个URL,使用 %d 个工作线程\n", len(urls), *workers)for i := 0; i < *workers; i++ {// 增加启动间隔,避免同时启动过多线程time.Sleep(time.Duration(i*(*initialWait)) * time.Second)wg.Add(1)go func(workerID int) {defer wg.Done()processTasks(workerID, taskCh, *width, *height, *fullPage, *timeout, *retry)}(i)}// 等待所有工作线程完成wg.Wait()elapsed := time.Since(start)log.Printf("所有任务完成,耗时: %s\n", elapsed)
}// 读取URL文件
func readURLs(filePath string) ([]string, error) {data, err := ioutil.ReadFile(filePath)if err != nil {return nil, err}// 按行分割URLvar urls []stringlines := strings.Split(string(data), "\n")for _, line := range lines {if url := strings.TrimSpace(line); url != "" {if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {url = "https://" + url}// 检查黑名单blacklisted := falsefor _, keyword := range blacklist {if strings.Contains(url, keyword) {blacklisted = truebreak}}if blacklisted {continue}urls = append(urls, url)}}return urls, nil
}// 生成文件名
func generateFilename(urlStr, outputDir string) string {// 移除URL中的协议部分u, err := url.Parse(urlStr)if err != nil {// 如果解析失败,使用时间戳作为文件名return filepath.Join(outputDir, fmt.Sprintf("unknown_%d.png", time.Now().UnixNano()))}// 使用主机名和路径生成文件名filename := strings.ReplaceAll(u.Host+u.Path, "/", "_")if len(filename) > 100 {filename = filename[:100]}return filepath.Join(outputDir, filename+".png")
}// 处理任务
func processTasks(workerID int, taskCh <-chan Task, width, height int, fullPage bool, timeout, retry int) {// 优化Chrome选项,增加更多反检测设置opts := append(chromedp.DefaultExecAllocatorOptions[:],chromedp.Flag("headless", false),chromedp.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"),chromedp.Flag("disable-blink-features", "AutomationControlled"),chromedp.Flag("disable-web-security", true),           // 禁用Web安全策略chromedp.Flag("allow-running-insecure-content", true), // 允许运行不安全内容chromedp.Flag("ignore-certificate-errors", true),      // 忽略SSL证书错误chromedp.WindowSize(width, height),chromedp.Flag("no-sandbox", true),             // 禁用沙盒模式,在某些环境需要chromedp.Flag("disable-setuid-sandbox", true), // 禁用setuid沙盒)// 每个worker只启动一个Chrome实例allocCtx, allocCancel := chromedp.NewExecAllocator(context.Background(), opts...)defer allocCancel()parentCtx, parentCancel := chromedp.NewContext(allocCtx)defer parentCancel()for task := range taskCh {var success boolvar attempt intfor attempt = 1; attempt <= retry; attempt++ {log.Printf("工作线程 %d 正在处理 %s (尝试 %d/%d)\n", workerID, task.URL, attempt, retry)if attempt > 1 {time.Sleep(time.Duration(attempt*2) * time.Second)}// 每个任务新建tabctx, cancel := chromedp.NewContext(parentCtx)err := captureScreenshot(ctx, task.URL, fullPage, timeout, task.Filename)cancel()if err == nil {log.Printf("工作线程 %d 成功保存截图: %s\n", workerID, task.Filename)success = truebreak}log.Printf("工作线程 %d 处理 %s 失败: %v (尝试 %d/%d)\n", workerID, task.URL, err, attempt, retry)if err != nil && (strings.Contains(err.Error(), "ERR_NAME_NOT_RESOLVED") ||strings.Contains(err.Error(), "context canceled")) {log.Printf("域名未被解析,停止对此URL的重试: %s", task.URL)break}}// 每个任务完成后,finishedTasks++atomic.AddInt64(&finishedTasks, 1)if !success {log.Printf("工作线程 %d 处理 %s 失败,已达到最大重试次数\n", workerID, task.URL)f, err := os.OpenFile("failed_urls.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)if err == nil {f.WriteString(task.URL + "\n")f.Close()}}}
}// 截图函数,优化等待策略
func captureScreenshot(ctx context.Context, url string, fullPage bool, timeout int, outputPath string) error {// 设置更长的超时ctx, cancel := context.WithTimeout(ctx, time.Duration(timeout)*time.Second)defer cancel()var buf []byteerr := chromedp.Run(ctx, chromedp.Tasks{chromedp.Navigate(url),chromedp.ActionFunc(func(ctx context.Context) error {var readyState stringstart := time.Now()for {err := chromedp.Evaluate(`document.readyState`, &readyState).Do(ctx)if err != nil {return err}if readyState == "complete" {time.Sleep(2 * time.Second) // 页面加载完成后再等2秒return nil}if time.Since(start) > time.Duration(timeout)*time.Second {return errors.New("等待页面加载超时")}time.Sleep(500 * time.Millisecond)}}),chromedp.FullScreenshot(&buf, 95), // 提高截图质量})if err != nil {if errors.Is(err, context.DeadlineExceeded) {log.Printf("截图超时(context deadline exceeded):%s", url)} else if errors.Is(err, context.Canceled) {log.Printf("截图被取消(context canceled):%s", url)} else {log.Printf("截图失败: %s, 错误: %+v", url, err)}return err}return ioutil.WriteFile(outputPath, buf, 0644)
}

go.mod

module screenshot-toolgo 1.24.4require (github.com/chromedp/cdproto v0.0.0-20250403032234-65de8f5d025b // indirectgithub.com/chromedp/chromedp v0.13.7 // indirectgithub.com/chromedp/sysutil v1.1.0 // indirectgithub.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535 // indirectgithub.com/gobwas/httphead v0.1.0 // indirectgithub.com/gobwas/pool v0.2.1 // indirectgithub.com/gobwas/ws v1.4.0 // indirectgolang.org/x/sys v0.29.0 // indirect
)

运行命令:

go run main.go

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华曦达港股IPO递表,AI Home生态构建智能生活新蓝图

在智能家居逐渐普及的当下&#xff0c;华曦达打造的AI Home生态为用户提供了更智能、便捷的生活解决方案&#xff0c;在行业中展现出独特优势。 华曦达AI Home生态由AI Home系统平台、AI Home基础设施、AI Home设备以及可连接外部设备的开放式设备矩阵构成&#xff0c;是一个开…

java+vue+SpringBoo智慧农业专家远程指导系统(程序+数据库+报告+部署教程+答辩指导)

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿ppt部署教程代码讲解代码时间修改工具 技术实现 开发语言&#xff1a;后端&#xff1a;Java 前端&#xff1a;vue框架&#xff1a;springboot数据库&#xff1a;mysql 开发工具 JDK版本&#xff1a;JDK1.…

免费AI助手工具深度测评:Claude4本地化部署与实战应用指南

免费AI助手工具深度测评&#xff1a;Claude4本地化部署与实战应用指南 AI无限对话免费Rovo工具Claude4碾压cursor和augment 前言 在AI工具日益普及的今天&#xff0c;大多数高质量的AI助手都需要付费订阅或有使用限制。然而&#xff0c;最近发现了一款基于Claude 4的免费AI助手…

MCP浏览器工具:playwright、chrome-mcp

参考&#xff1a; https://github.com/microsoft/playwright-mcp https://github.com/hangwin/mcp-chrome chrome-mcp安装需要额外安装成浏览器插件 用cherrystudio v1.4.5测试 mcp配置&#xff1a; "chrome-mcp-server": {"name": "chrome-mcp-serve…

水利水电安全员考试不同等级的考试内容有哪些区别?

水利水电安全员考试一般分为企业主要负责人&#xff08;A 类&#xff09;、项目负责人&#xff08;B 类&#xff09;和专职安全生产管理人员&#xff08;C 类&#xff09;三个等级。不同等级的考试内容都包括安全生产知识和管理能力两部分&#xff0c;但具体的侧重点有所不同。…

关于USB模式的一些内容(附USB接口颜色释义图)

今天在处理工作中的事情的时候,突然有个产品的小伙伴来问关于USB的事情,顺便给她简单说了下。USB接口模式主要包括以下几种:Host(主机模式)、Device(设备模式)、OTG(On-The-Go),以及较少使用的Accessory模式。以下是对这些模式的详细说明、区别差异及应用场景: 1. H…

React中的ErrorBoundary

文章目录 前言✅ 一、使用类组件实现 ErrorBoundary&#xff08;官方推荐方式&#xff09;用法示例&#xff1a; ✅ 二、用函数组件实现 ErrorBoundary&#xff08;借助 Hook react-error-boundary 库&#xff09;1. 安装 react-error-boundary2. 使用 ErrorBoundary 组件&…

历年西北工业大学计算机保研上机真题

西北工业大学计算机保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/problem 海伦公式求面积 题目描述 给定三角形的三条边长 a a a, b b b, c c c&#xff0c;先判断这三条边是否能构成一个三角形。 如果不能构成三角形&#xff0c;输出 N a N NaN NaN&#…

扫地机产品认证--黑名单制裁公司能否拿到美国产品准入许可(FCC认证)

扫地机产品认证–黑名单制裁公司能否拿到美国产品准入许可(FCC认证) 文章目录 扫地机产品认证--黑名单制裁公司能否拿到美国产品准入许可(FCC认证)⚠️ **一、核心限制规则**📋 **二、企业需满足的额外条件**🛡️ **三、黑名单企业的应对可能性**💎 **四、总结**产品认证…

数据结构复习2

第二章 线性表 2.1线性表的定义和基本操作 线性表&#xff1a;一种逻辑结构&#xff0c;表示数据元素之间的一对一线性关系&#xff08;如数组、链表、栈、队列等&#xff09;。 2.1.1线性表的定义 线性表是具有相同数据类型的n(n>0)个数据元素的有限序列。 (其中n为表长…

空间转录组benchmark 相关 读完scGPT spatial 和 空间单细胞基因乳房细胞数据集文章之后

文章目录 ✅ 空间转录组测序方式总体划分&#x1f9ec; 成像型空间转录组&#xff08;Imaging-based ST&#xff09;原理&#xff1a;技术代表 & 特点&#xff1a;优点&#xff1a;局限&#xff1a; &#x1f9ec; 测序型空间转录组&#xff08;Sequencing-based ST&#x…

清理华为云服务器内存使用率

这里写自定义目录标题 一、正确终止进程&#xff1a;不要带尖括号二、看清楚谁“真吃”了内存三、临时清掉缓存&#xff08;谨慎用&#xff09;四、长期优化1. 给系统加个 Swap2. 调整 MySQL 内存配置3. 水平&#xff0f;垂直扩容4. 告警 总结与下一步 华为云的“内存使用率”默…

Go 语言中的 package 和 go modules

1、package 的定义和导入 在任何大型软件项目中&#xff0c;代码的组织和管理都是至关重要的。Go 语言通过 包&#xff08;Package&#xff09; 的概念来解决这个问题&#xff0c;它不仅是代码组织的基础&#xff0c;也是代码复用的关键。本文将深入探讨 Go 语言中包的定义、规…

C#语言入门-task4 :C#语言的高级应用

C# 作为一门现代化、面向对象的编程语言&#xff0c;在企业级应用、游戏开发、移动应用、云计算等领域有着广泛的应用。以下是 C# 语言的一些高级应用场景和技术方向&#xff1a; 一、高级语言特性与编程范式 1. 异步编程&#xff08;Async/Await&#xff09; 应用场景&…

FastAPI vs Flask vs Django:Python Web框架全面对比

Python 作为最受欢迎的编程语言之一&#xff0c;其 Web 开发生态极为丰富。FastAPI、Flask 和 Django 是当前主流的三大 Python Web 框架&#xff0c;各有千秋。本文将从架构设计、开发效率、性能表现、生态支持、适用场景等方面&#xff0c;全面对比这三大框架&#xff0c;帮助…

如何从零开始掌握Pandas的DataFrame使用

视频演示 如何通过实例学习Pandas DataFrame的创建与数据访问 &#x1f9e9; 理解 Pandas DataFrame&#xff1a;数据分析的核心结构 Pandas 是 Python 中用于数据分析与处理的主力库&#xff0c;而 DataFrame 是 Pandas 最常用的二维表格数据结构。我们可以将其想象成一个 Ex…

LaTeX下载与实践入门指南

LaTeX下载与实践入门指南 简单来说&#xff0c;LaTeX 是一种以代码驱动的排版系统。和 Word 那种所见即所得&#xff08;WYSIWYG&#xff09;的编辑方式不同&#xff0c;LaTeX 更像是你写代码、它帮你生成精美排版。你写的不是文字排版&#xff0c;而是一种“结构化内容&#…

Java--数组

目录 1.1 介绍&#xff1a;数据可以存放多个同一类型的数据。 1.2 排序&#xff1a; 冒泡排序法&#xff1a; 1.3 查找 1. 顺序查找 2. 二分查找 二维数组&#xff1a; 杨辉三角&#xff1a; 1.1 介绍&#xff1a;数据可以存放多个同一类型的数据。 数组的引用&#xf…

地址簇与数据序列

深入理解IP地址与端口号&#xff1a;网络通信的基础 IP地址&#xff1a;互联网的门牌号 IP地址&#xff08;Internet Protocol Address&#xff09;是分配给网络中每台设备的唯一标识符&#xff0c;就像现实世界中的门牌号一样。在计算机上&#xff0c;一个网卡对应一个IP地址…

中学数集相等概念凸显无穷集不可~其真子集——初数一直将不是N的真子集误为⊂N

中学数集相等概念凸显无穷集不可&#xff5e;其真子集——初数一直将不是N的真子集误为⊂N 黄小宁 [摘要]证明了初等数学应有几何起码常识&#xff1a;当且仅当平移的距离0时才能使平移前、后的点集&#xff08;元点不少于两个&#xff09;重合。从而表明初中的直线公理使中学…