分享

用 Microsoft.mshtml.dll 和 WebClient 自己实现网页保存为 MHT 文件

 quasiceo 2014-06-25
分类: AppFramework近期动态 2008-05-08 21:44 2492人阅读 评论(4) 收藏 举报

相信大家经常用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./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./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元。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多