当前位置: 首页 > news >正文

南山区网站建设公司/优化外包哪里好

南山区网站建设公司,优化外包哪里好,网站建设phpcms,宁波外贸公司排名2022截取网页截图看似是一项简单的任务,但当你真正动手去做的时候,就会发现事情远没有那么容易。我在尝试截取一篇很长的 Reddit 帖子时就深有体会。一开始我以为只要调用 browser.TakeImage() 就万事大吉,结果却陷入了浏览器视口、动态内容加载、…

截取网页截图看似是一项简单的任务,但当你真正动手去做的时候,就会发现事情远没有那么容易。我在尝试截取一篇很长的 Reddit 帖子时就深有体会。一开始我以为只要调用 browser.TakeImage() 就万事大吉,结果却陷入了浏览器视口、动态内容加载、内存占用等一系列问题。

在这篇文章中,我将分享从一开始的“想当然”到最后找到高效方案的全过程。我们将以 r/dotnet 子版块为案例,探索常见的陷阱以及如何避免这些问题。看完这篇文章后,你将掌握如何稳定、完整地截取包含动态内容与无限滚动的网页截图的方法。

基础截图方式

我们先从最基础的方法开始:不进行任何特殊处理,直接截取截图。在 DotNetBrowser 中,这非常简单:

var image = browser.TakeImage();
var bitmap = ToBitmap(image);
bitmap.Save("screenshot.png", ImageFormat.Png);

由于 DotNetBrowser 返回的是原始位图,我们需要一个工具方法将其转换为 System.Drawing.Bitmap ,以便进行标准的 .NET 操作:

public static Bitmap ToBitmap(DotNetBrowser.Ui.Bitmap bitmap)
{var width = (int)bitmap.Size.Width;var height = (int)bitmap.Size.Height;var data = bitmap.Pixels.ToArray();var bmp = new Bitmap(width, height, PixelFormat.Format32bppRgb);var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),ImageLockMode.WriteOnly, bmp.PixelFormat);Marshal.Copy(data, 0, bmpData.Scan0, data.Length);bmp.UnlockBits(bmpData);return bmp;
}

运行这段代码后,我们得到了第一张截图:

使用 DotNetBrowser 制作的简单屏幕截图

使用 [DotNetBrowser](https://teamdev.cn/dotnetbrowser/?utm_source=csdn&utm_medium=article&utm_campaign=taking-screenshots-of-web-pages) 制作的简单屏幕截图

但问题显而易见:我们只截取了页面的一小部分。

这是因为 TakeImage() 方法仅截取浏览器视口内可见的内容,而默认视口大小并不足以显示整个页面。

截取整页截图

我们需要先获取网页的尺寸,并将浏览器调整为相应大小,然后重新尝试截图。可以通过 JavaScript 获取页面的尺寸,方法是取 documentdocument.documentElement 中较大的那个值:

var widthScript = @"Math.max(document.body.scrollWidth,document.documentElement.scrollWidth)";var heightScript = @"Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)";

然后调整浏览器大小以匹配这些尺寸:

var frame = browser.MainFrame;
var width = frame.ExecuteJavaScript<double>(widthScript).Result;
var height = frame.ExecuteJavaScript<double>(heightScript).Result;browser.Size = new Size((uint)width, (uint)height);
var image = browser.TakeImage();

以下是使用这种改进方法得到的结果:

截图区域变大了,但内容尚未加载完成

截图区域变大了,但内容尚未加载完成。

查看完整图片

紧接着出现了另一个问题:页面并未完全加载。这是因为我们在调整浏览器大小后立即进行了截图,没有给浏览器足够的时间来加载和渲染所有内容。从页面底部的加载指示器可以看出这一点。

让我们填加一个暂停等待时间:

browser.Size = new Size((uint)width, (uint)height);
// 通过反复试验得出的任意数字。
Thread.Sleep(2000);
var image = browser.TakeImage();

再试一次:

包含所有内容的完整截图

包含所有内容的完整截图

查看完整图片

终于,我们得到了一张像样的截图!但位图对象变得相当大,而且 Chromium 需要大量资源来渲染大视口,这在实际应用中并不实用。我们可以做得更好。

截取分段截图

使用之前的方法,浏览器会一次性渲染整个页面,并将结果位图传递给 .NET 进程内存。在处理长页面时,这会迅速耗尽系统资源,尤其是 RAM。

与其尝试一次性截取整个页面,不如我们将其分解为更小、更易管理的部分:

  1. 对页面的各个小段进行多次截图。
  2. 在每次截取之间滚动屏幕。
  3. 将各个部分拼接成最终图像。

以下是实现这种方法的代码:

// Reddit 页面是无限滚动的,因此我们需要设置一个固定的截图次数。
var numberOfShots = 15;
var viewportHeight = 1000;
browser.Size = new Size((uint)Width, (uint)viewportHeight);var capturedHeight = 0;
for (var count = 0; count < numberOfShots; count++)
{capturedHeight += viewportHeight;frame.ExecuteJavaScript($"window.scrollTo(0, {capturedHeight})").Wait();// 为了等待内容加载而设定的任意暂停时间。Thread.Sleep(500);var image = browser.TakeImage();var bitmap = ToBitmap(image);bitmap.Save($"screenshot-{count:D3}.png", ImageFormat.Png);
}

在实际应用中,我们会在另一台服务器上的异步任务中拼接这些片段。但为了简单起见,我们选择直接使用这个辅助函数:

public static Bitmap MergeBitmapsVertically(List<Bitmap> bitmaps)var files = Directory.GetFiles("/path/to/directory", "*.png").OrderBy(Path.GetFileName).ToArray();var images = files.Select(f => Image.FromFile(f)).ToArray();int width = images.Max(img => img.Width);int totalHeight = images.Sum(img => img.Height);using var merged = new Bitmap(width, totalHeight);using (var g = Graphics.FromImage(final)){int y = 0;foreach (var img in images){g.DrawImage(img, 0, y);y += img.Height;}}merged.Save("merged.png");
}

让我们看看结果如何:

合并后的截图片段包含重复元素

合并后的截图片段包含重复元素

查看完整图片

又一个新问题出现了:页眉和导航侧边栏在每张分段截图里重复出现,这显然不是截图应有的样子!

处理固定元素

当我们滚动页面时,像页眉和侧边栏这样的固定元素会保持在原位不动。但在进行分段截图时,我们希望这些固定元素只出现在第一段截图中。

让我们通过 CSS 的 position 属性来找出这些元素,并将它们隐藏起来:

var removeFixedElements = @"(() => {document.querySelectorAll('*').forEach(el => {const pos = getComputedStyle(el).position;if (pos === 'fixed' || pos === 'sticky') {el.style.display = 'none';}});})()";for (var count = 0; count < numberOfShots; count++)
{// 仅从第二张截图开始移除固定元素,// 这样可以保留第一张截图中的页眉和导航栏。if (count == 1) {frame.ExecuteJavaScript(removeFixedElements).Wait();}// 继续进行截图操作。
}

另一个常见的固定元素是 Cookie 同意弹窗。由于每个网站的实现方式都不一样,我们需要逐个页面单独处理。

以 Reddit 为例,我们可以使用以下代码识别并隐藏 Cookie 弹窗:

var removeCookieScreen =@"(() => {const dialog = document.querySelector('reddit-cookie-banner');if (dialog) {dialog.style.display = 'none';}})()";
frame.ExecuteJavaScript(removeCookieScreen).Wait();

最终,我们以节省内存的方式截取了一张合适的网页截图。

一张干净、完整的网页截图

一张干净、完整的网页截图

查看完整图片

总结

最初只是一个简单的 browser.TakeImage() 调用,最后演变成一个需要应对无数细节的复杂解决方案。我们解决了以下挑战:

  • 一次性截取整页内容;
  • 将长页面分段截取并拼接;
  • 处理固定元素和 Cookie 弹窗;
  • 优化内存使用和实用性。

需要说明的是,本文介绍的方法并非放之四海而皆准。每个网页都有其独特之处,而优秀的截图工具之所以强大,正是因为它能处理各种边缘场景。尽管如此,本文所展示的方法已经涵盖了使用 DotNetBrowser 进行网页截图所需掌握的所有核心知识。

http://www.whsansanxincailiao.cn/news/30289080.html

相关文章:

  • 做网站必须知道的问题/合肥seo整站优化
  • 安康网站开发公司/营销方案怎么写?
  • 南京网站开发联系南京乐识/如何做百度搜索推广
  • 宠物网站建设报告/搜索引擎优化的定义
  • 广州市增城区建设局网站是什么/百度快照收录
  • 广州微网站建设/做网站用什么编程软件
  • 专业建站公司建站系统/百度首页排名怎么做到
  • 如何选择建设网站类型/爱站网关键词搜索工具
  • 无货源电商怎么找货源/关键词seo报价
  • 百度网站介绍显示图片/重庆森林经典台词梁朝伟
  • wordpress建立手机网站/百度一下首页百度一下
  • 手机网站加速器/百度搜索风云榜明星
  • 为什么要建设营销型网站/品牌运营策划方案
  • 破解织梦做的网站/百度搜索风云榜排名
  • wordpress搬家后变慢/网站关键词优化报价
  • 做游戏赚钱的网站/音乐接单推广app平台
  • 网站建设系统平台/百度网址是什么
  • 计算机系毕设代做网站/关键词挖掘站长工具
  • 国外服务器ip大全/seo自学教程
  • 商城网站建设资讯/网站如何建立
  • 微信导航网站模板/重庆人力资源和社会保障网官网
  • 个人网站怎么做的模板/百度一下app下载安装
  • 基于jsp的网站建设论文/集客营销软件
  • 开个人网站如何赚钱/郑州网络推广代理顾问
  • 柳州网站建设公司/网络营销的重要性与意义
  • 上海地区做旧物回收的网站/最新新闻头条
  • 二级域名查询ip/新乡seo网络推广费用
  • 游戏ui设计最好的培训机构/百度刷排名优化软件
  • 建网站空间可以不买/网络运营是什么专业
  • 余姚有专业做网站的吗/新乡网站推广