制作离线浏览器 - 使用 Visual C++

在网络世界中,经常需要保存网页以便在没有网络的情况下查看。本文将介绍如何使用Visual C++创建一个离线浏览器,它可以下载网页及其所有资源,并将它们保存到本地硬盘上。将使用WinInet、URL Moniker和MSHTML等API来实现这一功能。

概述

制作离线浏览器的动机源于一个需要记录用户与网页交互的模块。为了不使用浏览器的"另存为"选项,决定自己开发一个工具来保存网页到本地硬盘。以下是实现这一功能的简要算法描述:

1. 下载网页的HTML内容,例如www.google.com,并将其保存到指定的文件夹中。

2. 遍历HTML文档,查找每个标签的src属性,src属性的值是资源的URL。如果资源的URL是绝对的,例如www.google.com/images/logo.gif,那么没有问题。但如果URL是相对的,例如images/logo.gif,则需要使用主机名将其转换为绝对路径。

3. 更新src属性以反映资源URL的任何变化。相对URL将保持不变,但对于绝对地址,src属性现在将更改为相对路径。

4. 将原始src属性的值保存到srcdump中,仅供将来参考,以便原始src仍然可用。

在开发记录用户与网页交互的模块时,需要将网页保存到本地硬盘上。寻找了一些能够实现这一功能的代码,但没有找到有用的资料,因此决定自己开发。在这里分享这段代码,希望它能帮助其他从事相关工作的人,并希望得到一些反馈,指出可能犯的错误。没有使用MFC,以确保它与Win32应用程序以及MFC兼容。

使用代码

以下是使用代码的示例:

LoadHtml()函数根据bDownload参数的值工作在两种模式下:

如果bDownload为true,则假定HTML已经使用SetHtml()函数加载,并且不会执行以下代码片段,只是从URL中填充主机名和端口字段。

如果bDownload为false,则首先从指定的URL下载HTML,然后填充主机名和端口字段。

HINTERNET hNet = InternetOpen("Offline Browser", INTERNET_OPEN_TYPE_PROXY, NULL, NULL, 0); if (hNet == NULL) return; HINTERNET hFile = InternetOpenUrl(hNet, sUrl.c_str(), NULL, 0, 0, 0); if (hFile == NULL) return; while (true) { const int MAX_BUFFER_SIZE = 65536; unsigned long nSize = 0; char szBuffer[MAX_BUFFER_SIZE+1]; BOOL bRet = InternetReadFile(hFile, szBuffer, MAX_BUFFER_SIZE, &nSize); if (!bRet || nSize <= 0) break; szBuffer[nSize] = '\0'; m_sHtml += szBuffer; }

BrowseOffline()假设HTML已经加载。首先,它通过将HTML加载到MSHTML DOMDocument接口来构建HTML DOM树:

SAFEARRAY* psa = SafeArrayCreateVector(VT_VARIANT, 0, 1); VARIANT *param; bstr_t bsData = (LPCTSTR)m_sHtml.c_str(); hr = SafeArrayAccessData(psa, (LPVOID*)&m); param->vt = VT_BSTR; param->bstrVal = (BSTR)bsData; hr = pDoc->write(psa); hr = pDoc->close(); SafeArrayDestroy(psa);

一旦构建了DOM树,就可以遍历它并寻找需要下载的资源。目前,只寻找所有元素中的src属性,一旦找到src属性,就下载并保存到本地文件夹。

MSHTML::IHTMLElementCollectionPtr pCollection = pDoc->all; for (long a = 0; a < pCollection->length; a++) { std::string sValue; IHTMLElementPtr pElem = pCollection->item(a); if (GetAttribute(pElem, L"src", sValue)) { if (!IsAbsolute(sValue)) { if (sValue[0] == '/') sValue = sValue.substr(1, sValue.length()-1); CreateDirectories(sValue, m_sDir); if (!DownloadResource(sValue, sValue)) { std::string sTemp = m_sScheme + m_sHost; sTemp += sValue; if (sTemp[0] == '/') sTemp = sTemp.substr(1, sTemp.length()-1); SetAttribute(pElem, L"src", sTemp); SetAttribute(pElem, L"srcdump", sValue); } else { SetAttribute(pElem, L"srcdump", sValue); } } else { std::string sTemp; sTemp = TrimHostName(sValue); CreateDirectories(sTemp, m_sDir); if (DownloadResource(sTemp, sTemp)) { if (sTemp[0] == '/') sTemp = sTemp.substr(1, sTemp.length()-1); SetAttribute(pElem, L"src", sTemp); SetAttribute(pElem, L"srcdump", sValue); } } } }

由于src和srcdump属性的值发生了变化,原始HTML被更新并保存为[GUID].html,其中GUID是使用CoCreateGuid()生成的全局唯一标识符。

MSHTML::IHTMLDocument3Ptr pDoc3 = pDoc; MSHTML::IHTMLElementPtr pDocElem; pDoc3->get_documentElement(&pDocElem); BSTR bstrHtml; pDocElem->get_outerHTML(&bstrHtml); std::string sNewHtml((const char*)OLE2T(bstrHtml)); SaveHtml(sNewHtml);

一旦有了资源的绝对URL,就可以直接下载并保存到适当的本地文件夹中。

if (URLDownloadToFile(NULL, sTemp.c_str(), sTemp2.c_str(), 0, NULL) == S_OK) return true; else return false;

网站目录结构

COfflineBrowser obj; char szUrl[1024]; printf("Enter URL: "); gets(szUrl); obj.SetDir("c:\\MyTemp\\"); obj.LoadHtml(szUrl, true); obj.BrowseOffline();
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485