相信大家经常用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 文件相关推荐

  1. Microsoft.mshtml到底是什么?

    MSHTML到底是什么,现在越来越糊涂了. Lostinet在结合 MSHTML 与 WebBrowser 生成美观实用的 WinForm 应用程序一文给出的例子中引用的是MSHTML(COM组件) ...

  2. 模块mshtml.dll已加载,但找不到入口点DllRegisterServer(随手笔记)

    模块mshtml.dll已加载,但找不到入口点DllRegisterServer 通常在运行中注册动态库文件:Microsoft.Office.Interop.Word.dll (按回车键执行命令)都 ...

  3. vmware tools安装程序无法继续,Microsoft Runtime DLL安装程序未能完成安装。的解决方法

    vmware tools安装程序无法继续,Microsoft Runtime DLL安装程序未能完成安装.的解决方法 参考文章: (1)vmware tools安装程序无法继续,Microsoft R ...

  4. 安装VMware,出现Microsoft Runtime DLL 安装程序未能完成安装,解决方法

    安装VMware,出现Microsoft Runtime DLL 安装程序未能完成安装,解决方法 参考文章: (1)安装VMware,出现Microsoft Runtime DLL 安装程序未能完成安 ...

  5. 找不到编译动态表达式所需的一种或多种类型。是否缺少对 Microsoft.CSharp.dll 和 System.Core.dll 的引用?...

    提示"找不到编译动态表达式所需的一种或多种类型.是否缺少对 Microsoft.CSharp.dll 和 System.Core.dll 的引用? "错误 解决方法:   将引入的 ...

  6. Microsoft.CSharp.dll程序集的作用

    <C#与.NET 4高级程序设计:第5版>第18章动态类型和动态语言运行时,本章,我们将学习dynamic关键字的方方面面,理解如何使用DLR (Dynamic Language Runt ...

  7. vmware安装问题:Microsoft Runtime DLL安装程序未能完成安装

    文章目录 一.vmware安装问题:Microsoft Runtime DLL安装程序未能完成安装 1.1 在输入%temp% 1.2. 找到{ADC3121A-3EBA-4016-AF64-00B8 ...

  8. 检测到 LoaderLock Message Microsoft.DirectX.dll”正试图在 OS 加载程序锁内执行托管代码。...

    今天在群里有朋友问了一个"检测到 LoaderLock Message Microsoft.DirectX.dll"正试图在 OS 加载程序锁内执行托管代码."的问题,自 ...

  9. 未能加载文件或程序集“Microsoft.mshtml”或它的某一个依赖项。未能验证强名称签名。此程序集可能已被篡改,或者已被延迟签名,但没有用正确的私钥进行完全签名。 (异常来自 HRESULT:0

    未能加载文件或程序集"Microsoft.mshtml"或它的某一个依赖项.未能验证强名称签名.此程序集可能已被篡改,或者已被延迟签名,但没有用正确的私钥进行完全签名. (异常来自 ...

最新文章

  1. 同步与异步,阻塞与非阻塞的区别
  2. PowerShell 2.0管理事件日志(一)查看和读取事件日志
  3. libcudart.so.10.0: cannot open shared object file: No such file or directory
  4. 使用VS2005调试ASP程序
  5. loader调用过程
  6. volatile和final
  7. 河源市计算机组装竞赛,计算机组装大赛决赛圆满结束
  8. matlab文件目录表示,Matlab - 文件目录路径操作_读取不同路径下的相同文件名表格...
  9. linux 编写完程序吗,linux下编写C++程序
  10. 【Python 标准库学习】系统相关的参数和函数库 — sys
  11. 【Matlab】模式识别——聚类算法集锦
  12. Nodejs Promise对象
  13. JavaScript常用开发框架总结
  14. 基于PHPCMS的SQL注入(Havij)
  15. 【LOJ119】单源最短路 模板
  16. 标准表达式中数据类型不匹配_三观不同的人在一起有多累?三观一致的标准,不进行三观测试真不知道!人生观测试,价值观测试,世界观测试题推荐!超准三观匹配度测试!...
  17. ip地址和域名的关系是什么?
  18. 图片去水印的原理_图片去水印方法 图片如何去掉水印
  19. 如何查看两个word文档的不同之处
  20. three.js 入门指南(敷衍一下)

热门文章

  1. swishmax(文字特效工具),闪客快斧(破解flash工具)
  2. ZoneAlarm Security Suite 2009注册机及注册方法
  3. 使用bat文件做定时任务
  4. 使用ScanCode扫描开源项目的license
  5. 动态规划DP 之 抢劫得到最多的财务(只是针对题,别无他意)
  6. 甲方项目管理培训课程大纲
  7. Android开发英语听力软件,基于Android平台的大学英语听力学习系统的设计与实现...
  8. 企业 Apple 设备管理概述
  9. PHILIPS 无线键鼠配对说明
  10. Hi3798日志分析-海思