用 Microsoft.mshtml.dll 和 WebClient 自己实现网页保存为 MHT 文件
相信大家经常用IE保存网页功能保存有价值的网页,但是IE的网页保存功能做的不是太好,经常会有些页面保存失败。我也深受其烦,好在本人是程序员,程序员最大的好处是会自己编软件。正好我自己开发了个多页签浏览的IE,于是便在其中增加了网页保存为MHT文件的功能。
网页保存为MHT功能主要包含以下几个关键问题:
1)修改所有相对链接为绝对链接;
2)把网页内容连同包含的 JS、图片、CSS等文件下载到本地并打包到一起,形成单一的MHT文件;
3)要能处理Frameset和IFrame;
为了避免解析HTML带来的不必要的麻烦,本人利用Microsoft.mshtml.dll 实现HTML文本的解析,并用WebClient类实现JS、图片、CSS等文件的下载。下面逐个解析其中的原理和注意事项。
1)修改所有相对链接为绝对链接:
首先要获得待保存的网页的document对象,如果你用.NET2.0的WebBrowser控件,可以从它的 WebBrowser.Document.DomDocument as mshtml.IHTMLDocument2 获得document对象。然后可以遍历所有包含相对链接的HTML标签,以<script>为例:
mshtml.IHTMLDocument2 doc2 = WebBrowser.Document.DomDocument as mshtml.IHTMLDocument2;
string baseUrl = GetUrlBase(doc2);
Uri baseUri = new Uri(baseUrl);
Uri uri;
mshtml.IHTMLElementCollection scripts = doc2.all.tags("script") as mshtml.IHTMLElementCollection;
foreach (mshtml.IHTMLScriptElement e in scripts)
{
// 把script的src属性转为绝对URL:
if (string.IsNullOrEmpty(e.src) == false && Uri.TryCreate(baseUri, e.src, out uri))
{
e.src = uri.AbsoluteUri;
_toBeDownloaded.Add(uri.AbsoluteUri);//顺便记下将需要下载的URL
}
}
其中GetBaseUrl是获得document的baseUrl:
private string GetUrlBase(mshtml.IHTMLDocument2 doc2)
{
// 例子:
// <base href="http://www.somedomain.com/directory/" />
mshtml.IHTMLBaseElement baseElem = doc2.all.item("base", 0) as mshtml.IHTMLBaseElement;
if (baseElem != null)
{
return baseElem.href;
}
return doc2.location.href;
}
于此相似,要处理 <img>、<frame>、<iframe>标签的 src 属性,和 <link>、<a> 的 href 属性。
2)下载文件
WebClient wc = new WebClient();
try
{
wc.Headers["Referer"] = _url; //当前页面的url,克服某些网站的防盗链措施
wc.CachePolicy = new RequestCachePolicy(RequestCacheLevel.CacheIfAvailable);//优先从cache获取
byte[] data = wc.DownloadData(location);
string contentType = wc.ResponseHeaders["content-type"];
Encoding enc = wc.Encoding;
if (location.ToLower().EndsWith(".css") || contentType.ToLower().EndsWith("css"))
{
string content = enc.GetString(data);
content = ProcessCssUrls(content, new Uri(location));//替换Css文件里的 url() 里的图片URL(从相对路径改为绝对路径,原理同上,这里自己解析CSS语法也不太难)
data = enc.GetBytes(content);//获得数据
}
// 用一个 ContentPart 结构保存每个部分的数据
ContentPart part = new ContentPart();
part.ContentType = contentType;
part.Data = data;
part.Encoding = enc;
}
catch (Exception err)
{
Logger.Write(err.ToString());
}
3)打包:
打包比较简单,我参考IE的打包文件实现,所有内容均用 Base64 编码。
文件格式如下,聪明人一看就明白:
(1)文件头:
Content-Type: multipart/related;
type="text/html";
boundary="----=_NextPart_000_0000_01C899B4.5364D820"
<空一行>
(2)每个Part:
------=_NextPart_000_0000_01C899B4.5364D820
Content-Type: text/html; charset=gbk;
charset="gb2312"
Content-Transfer-Encoding: base64
Content-Location: http://bbs.spforum.net/viewthread.php?tid=185549&extra=page%3D4
PEhUTUwgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiPjxIRUFEPjxUS...
<空一行>
(3)文件尾
------=_NextPart_000_0000_01C899B4.5364D820--
4)处理多 frame、iframe:
其实每个frame都有自己的document,取出来,递归处理下就可以了。
---------------
最后自己评论下:
优点:(1)成功率高,几乎不会出现保存网页失败的情况。(2)因为WebClient设置了优先从IE缓存下载图片、CSS等文件,所以保存速度快。
缺点:(1)MHT文件内容偏大,因为所有Part都用Base64编码,而IE对于一些纯文本的文件如css、js、html等,是用quot-printable格式保存,编码开销要小一些,最终的mht文件也要小一些。
有需要源代码的,请发邮件给AppFramework@hotmail.com,一份只要500元。
用 Microsoft.mshtml.dll 和 WebClient 自己实现网页保存为 MHT 文件相关推荐
- Microsoft.mshtml到底是什么?
MSHTML到底是什么,现在越来越糊涂了. Lostinet在结合 MSHTML 与 WebBrowser 生成美观实用的 WinForm 应用程序一文给出的例子中引用的是MSHTML(COM组件) ...
- 模块mshtml.dll已加载,但找不到入口点DllRegisterServer(随手笔记)
模块mshtml.dll已加载,但找不到入口点DllRegisterServer 通常在运行中注册动态库文件:Microsoft.Office.Interop.Word.dll (按回车键执行命令)都 ...
- vmware tools安装程序无法继续,Microsoft Runtime DLL安装程序未能完成安装。的解决方法
vmware tools安装程序无法继续,Microsoft Runtime DLL安装程序未能完成安装.的解决方法 参考文章: (1)vmware tools安装程序无法继续,Microsoft R ...
- 安装VMware,出现Microsoft Runtime DLL 安装程序未能完成安装,解决方法
安装VMware,出现Microsoft Runtime DLL 安装程序未能完成安装,解决方法 参考文章: (1)安装VMware,出现Microsoft Runtime DLL 安装程序未能完成安 ...
- 找不到编译动态表达式所需的一种或多种类型。是否缺少对 Microsoft.CSharp.dll 和 System.Core.dll 的引用?...
提示"找不到编译动态表达式所需的一种或多种类型.是否缺少对 Microsoft.CSharp.dll 和 System.Core.dll 的引用? "错误 解决方法: 将引入的 ...
- Microsoft.CSharp.dll程序集的作用
<C#与.NET 4高级程序设计:第5版>第18章动态类型和动态语言运行时,本章,我们将学习dynamic关键字的方方面面,理解如何使用DLR (Dynamic Language Runt ...
- vmware安装问题:Microsoft Runtime DLL安装程序未能完成安装
文章目录 一.vmware安装问题:Microsoft Runtime DLL安装程序未能完成安装 1.1 在输入%temp% 1.2. 找到{ADC3121A-3EBA-4016-AF64-00B8 ...
- 检测到 LoaderLock Message Microsoft.DirectX.dll”正试图在 OS 加载程序锁内执行托管代码。...
今天在群里有朋友问了一个"检测到 LoaderLock Message Microsoft.DirectX.dll"正试图在 OS 加载程序锁内执行托管代码."的问题,自 ...
- 未能加载文件或程序集“Microsoft.mshtml”或它的某一个依赖项。未能验证强名称签名。此程序集可能已被篡改,或者已被延迟签名,但没有用正确的私钥进行完全签名。 (异常来自 HRESULT:0
未能加载文件或程序集"Microsoft.mshtml"或它的某一个依赖项.未能验证强名称签名.此程序集可能已被篡改,或者已被延迟签名,但没有用正确的私钥进行完全签名. (异常来自 ...
最新文章
- 同步与异步,阻塞与非阻塞的区别
- PowerShell 2.0管理事件日志(一)查看和读取事件日志
- libcudart.so.10.0: cannot open shared object file: No such file or directory
- 使用VS2005调试ASP程序
- loader调用过程
- volatile和final
- 河源市计算机组装竞赛,计算机组装大赛决赛圆满结束
- matlab文件目录表示,Matlab - 文件目录路径操作_读取不同路径下的相同文件名表格...
- linux 编写完程序吗,linux下编写C++程序
- 【Python 标准库学习】系统相关的参数和函数库 — sys
- 【Matlab】模式识别——聚类算法集锦
- Nodejs Promise对象
- JavaScript常用开发框架总结
- 基于PHPCMS的SQL注入(Havij)
- 【LOJ119】单源最短路 模板
- 标准表达式中数据类型不匹配_三观不同的人在一起有多累?三观一致的标准,不进行三观测试真不知道!人生观测试,价值观测试,世界观测试题推荐!超准三观匹配度测试!...
- ip地址和域名的关系是什么?
- 图片去水印的原理_图片去水印方法 图片如何去掉水印
- 如何查看两个word文档的不同之处
- three.js 入门指南(敷衍一下)