如何使用 Selenium Webdriver 自动将网页保存为单个 .MHTML 文件





0/5 (0投票)
使用 Selenium Webdriver 将网页保存为单个自包含文件
引言
EdgeSinglePageDownloader 是一个简单的控制台应用程序,它使用 Edge Selenium Webdriver 下载一组网页并将它们保存为指定文件夹中的单个 .MHTML 文件(它也应该适用于 ChromeDriver)。这是一个简单的概念验证,展示了如何使用 Selenium Webdriver 实现此功能,并允许批量下载和保存一组网页。
背景
Edge Selenium Webdriver 是一个 NuGet 包,允许您通过模拟用户交互来自动化 Microsoft Edge。通过向 Edge 发送 CTRL + S 键来保存浏览的网页,以弹出“另存为”对话框,然后指定文件名,选择文件格式(网页,单个文件 .mhtml)并单击“保存”按钮。不幸的是,Selenium Webdriver 提供的 SendKeys
方法不起作用(至少我在 Windows 上无法使其工作),因此在多次尝试后,我切换到使用 VBScript SendKeys 方法,该方法可以完美运行,但有一个缺点,即需要 Windows 操作系统。
Using the Code
使用代码非常简单,您只需要
- 调整
DefaultSaveFolder
常量以指定保存 mhtml 文件的本地文件夹,默认值为 C:\tempconst string DefaultSaveFolder = "c:\\temp";
- 调整
urlsToSave
变量的初始化,其中包含您想要保存的 url,默认提供 The Verge 和 Wired 的 urlvar urlsToSave = new List<string> { "https://www.theverge.com", "https://www.wired.com/" };
之后,只需运行代码,Edge 将启动,所有 urlsToSave
将按顺序浏览,并保存在 DefaultSaveFolder 中,文件名为 Page_1.mhtml、Page_2.mhtml、...、Page_n.mhtml。
代码非常简单,全部包含在 Main
函数和 SaveAsSingleFile
辅助函数中。
Main
函数执行以下步骤
- 循环遍历
urlsToSave
变量中的所有 url,并为每个 url- 实例化一个
EdgeDriver
类(由 Selenium Webdriver 提供),该类启动一个新的 Edge 浏览器 - 通过调用
EdgeDriver.Navigate().GoToUrl
使浏览器导航到 url - 通过调用辅助函数
SaveAsSingleFile
将网页保存为单个 .mhtml 文件
- 实例化一个
static void Main(string[] args)
{
var options = new EdgeOptions();
var service = EdgeDriverService.CreateDefaultService();
service.EnableVerboseLogging = true;
WshShell = new WshShellClass();
var urlsToSave = new List<string>
{ "https://www.theverge.com", "https://www.wired.com/" };
var i = 1;
foreach (var url in urlsToSave)
{
Driver = new EdgeDriver(service, options);
Driver.Navigate().GoToUrl(url);
SaveAsSingleFile(Path.Combine(DefaultSaveFolder,
$"Page_{i++}.mhtml"),url);
Driver.Close();
}
}
SaveAsSingleFile
辅助函数执行以下步骤
- 检查输出目录是否存在,如果不存在则创建它
- 检查输出文件是否存在且格式为 .mhtml,如果存在,则直接退出而不重新保存,否则删除现有文件
- 使用
WshShell.SendKeys
向 Edge 浏览器发送 CTRL(^ 字符)+ S 键,这将弹出“另存为”对话框(下图)。请注意,“文件名
”和“保存类型
”文本框的标签分别有一个带下划线的字符 'n
' 和 't
',您可以通过按 ALT + 这些字符之一来聚焦它们的控件。请注意,这些快捷方式取决于本地化(我使用的是英文本地化 Windows),在您的 Windows 安装中,它们可能会有所不同,因此请根据需要进行调整。 - 发送 ALT(% 字符)+ 'n' 和传递给函数的文件名
- 发送 ALT(% 字符)+ 't',向下箭头以打开所有“保存类型”的可能格式,向上箭头以选择“网页,单个文件 (*.mhtml)”并按两次 ENTER(~ 字符)以确认文件格式并按“保存”按钮。
- 之后,它会等待 1 分钟(由
MaxWaitForSaveMSec
常量指定)来检查已创建的已保存文件是否为 MHTML 格式(它会检查文件是否包含string
"Snapshot-Content-Location: {url}
"),如果不是,它会返回函数开头重新执行所有操作。
static void SaveAsSingleFile(string filename, string url)
{
again:
if (!Directory.Exists(Path.GetDirectoryName(filename)))
Directory.CreateDirectory(Path.GetDirectoryName(filename));
if (System.IO.File.Exists(filename))
{
// simple check that the existing file format is mhtml,
// otherwise delete and re-save it
if (!System.IO.File.ReadAllText(filename).Contains
($"Snapshot-Content-Location: {url}"))
System.IO.File.Delete(filename);
else
return;
}
WshShell.SendKeys("^s");
Thread.Sleep(1000);
// send alt+n, enter filename
WshShell.SendKeys($"%n{filename}");
Thread.Sleep(20);
// send alt+t, down arrow, up arrow (to select single mhtml), press enter twice
WshShell.SendKeys($"%t");
Thread.Sleep(20);
WshShell.SendKeys($"{{DOWN}}");
Thread.Sleep(20);
WshShell.SendKeys($"{{UP}}");
Thread.Sleep(20);
WshShell.SendKeys($"~~");
// waits up to MaxWaitForSaveMSec to check that the file is saved correctly
var endtime = DateTime.Now.AddMilliseconds(MaxWaitForSaveMSec);
while (DateTime.Now < endtime)
{
Thread.Sleep(1000);
// simple check that the file is present and its format is mhtml,
// otherwise retry again to save
if (System.IO.File.Exists(filename))
{
if (!System.IO.File.ReadAllText(filename).Contains
($"Snapshot-Content-Location: {url}"))
goto again;
else
break;
}
}
}
关注点
要使用 VBScript SendKeys 方法,您必须创建一个 WScript.Shell COM 对象的实例。最简单的方法是直接引用其 ActiveX 控件文件,方法是右键单击项目文件 -> 添加 -> COM 引用 -> 浏览 -> 选择 C:\Windows\SysWOW64\wshom.ocx。
您应该会在 Visual Studio 的 Dependencies/COM 节点中看到 Interop.IWshRuntimeLibrary
。单击它并将“嵌入互操作类型”从“是”更改为“否”(如果您使用 Net Core)。
完成此操作后,您可以通过以下方式实例化 WScript.Shell COM
对象:
var wshShell = new WshShellClass();
历史
- V1.0(2023 年 9 月 22 日):初始版本