自定义 Google 以显示之前的搜索






4.89/5 (34投票s)
该应用程序在 Google 页面上添加了一个自定义按钮和一些锚元素,这些元素链接到 Google 上之前搜索过的文本。
引言
通常,当我搜索某个内容时,几天后我很有可能会再次搜索相同的内容,以便回忆起上次学到的东西。所以,如果我觉得再次点击搜索按钮用与上次相同的关键词来获得我想要的结果,我就会打开 Internet Explorer 并查看历史记录。这很好,但有一天我只是想,为什么 Google 不在其页面上显示最近搜索的文本呢?如果这样做了,它将为 Googlers 提供极大的便利。当然,既然我不能向 Google 提出这个想法,我决定为我的机器自定义 Google,以便它可以显示我过去几次搜索的内容。
如何做到
这可以通过修改 Google 的 HTML 来实现,每次它在任何浏览器窗口中加载时,都会添加一些 HTML 元素。一个带有文本 “我感觉真的很好”
的按钮,与标准的 Google 页面按钮(“Google Search”
和 “I'm Feeling Lucky”
)一起添加到了 Google 页面的 HTML 中。点击此按钮后,过去搜索过的所有文本都会作为 HTML 渲染在 Google 页面的 Span
元素中(上图中的红色文本)。当执行新的搜索时,搜索查询将从地址栏中的 Google URL 中检索,并存储为历史记录(IE 历史记录在此处不起作用)。
使用的组件
此应用程序使用以下组件
- SHDocVw.dll - 该组件直接由 Internet Explorer 托管,并提供与导航、就地链接、收藏夹和历史记录管理相关的函数。SHDocVw 还公开了与其宿主接口,允许它作为 ActiveX 控件单独托管。SHDocVw.dll 组件也称为 WebBrowser 控件。它还托管 MSHTML.dll 组件。
- MSHTML.dll - MSHTML.dll 组件负责浏览器(Internet Explorer 4.0 或更高版本)中的 HTML 解析和渲染,并通过 DOM 提供对加载的 HTML 文档的访问。它还托管脚本引擎、ActiveX 控件、插件以及 HTML 文档可以引用的所有其他对象。
有关这些组件的详细阅读以及如何选择其中一个组件,请阅读此内容。
理解 Google HTML 结构
要编辑和修改网站的 HTML,首先应该理解它。理解涉及知道页面上的 HTML 元素,其中将添加自定义元素。
任何页面的源代码都可以在 Web 浏览器中查看。许多浏览器(如 Internet Explorer 9.0)提供更好的方法来导航到 Web 页面上可见元素的 HTML。在这里,我将使用 Internet Explorer 9.0 开发人员工具来解释 Google 的 HTML 结构。
首先,让我们看一下“Google Search”和“I'm Feeling Lucky”按钮的父元素。 这些按钮的名称分别为“btnK”和“btnI”。自定义按钮将作为这些按钮元素的父级的最后一个子级添加。为了使自定义按钮看起来像 Google 的按钮,只需确保按钮的 type 属性为 submit,因为 Google 的 CSS 应用于所有 type 属性为 "submit"
的输入元素。
之前的搜索文本将显示为链接元素,链接到具有与上次相同的查询的 Google。这些链接元素需要在 Google 页面中的某个元素中显示。为了在 Google 页面底部显示链接元素,实际上是一个大面积的白色空白区域将被使用。
Div
元素具有 "footer"
作为其 ID 属性。要在该页面内显示链接元素,将添加一个 span
元素作为该元素的子项,然后将链接元素添加到此 span
元素中。不需要为 anchor
元素添加样式,因为所有样式都由 Google 的 CSS 处理,该 CSS 应用于 footer div
的所有 anchor
元素的子项。
一切如何运作
为了开始理解代码并对它的工作方式有一个清晰的认识,首先值得看一下下面显示的应用程序流程。请特别注意 Window 的 DocumentComplete 事件和自定义按钮的 OnClick 事件。
注册所有窗口
当应用程序在 Main 方法中启动时,将创建一个 SHDocVw.ShellWindows 类的新实例。ShellWindows 是属于 shell 的所有打开窗口的集合。它包括 Internet Explorer 和 Windows Explorer 窗口。ShellWindows 类公开两个事件:WindowRegistered 事件(每当创建一个新窗口实例时触发)和 WindowRevoked 事件(每当一个窗口实例被销毁时触发)。对于我们的目的,我们需要为 WindowRegistered 事件添加 EventHandler,以便捕获所有新的 Internet Explorer 实例。
//This will be used to detect a new window.
ExplorerWindow = new SHDocVw.ShellWindows();
ExplorerWindow.WindowRegistered +=
new DShellWindowsEvents_WindowRegisteredEventHandler(ExplorerWindow_WindowRegistered);
如果创建了 Internet Explorer 的新实例,则会触发 WindowsRegistered 事件。在该事件的 EventHandler 中,将枚举 ShellWindows 集合中的所有窗口。集合中的所有窗口都将转换为 SHDocVw.dll 的 InternetExplorer 类。为尚未设置 "IsRegistered"
属性的窗口设置该属性,并将为窗口的 DocumentComplete 事件添加新的 EventHandler。使用 "IsRegistered"
属性是为了确保 EventHandler 只被添加到窗口的 DocumentComplete 事件一次。
SHDocVw.ShellWindows AllWindows = new ShellWindows();
//Check if a window is already registered if not then register it
//and wait for the document complete event to get fired
foreach (object Window in AllWindows)
{
SHDocVw.InternetExplorer IE = (InternetExplorer)Window;
if (IE.GetProperty("IsRegistered") == null)
{
IE.PutProperty("IsRegistered", true);
IE.DocumentComplete += new DWebBrowserEvents2_DocumentCompleteEventHandler(IE_DocumentComplete);
}
}
当窗口的 readystate 属性更改为 READYSTATE_COMPLETE
时,将触发 DocumentComplete 事件。当浏览器完成网页下载时,会发生这种情况。一旦 Web 页面加载完毕,下一步就是确保打开的页面是 Google。完成此操作的最佳方法是查看 InternetExplorer 类的 LocationURL
属性。LocationURL
属性保存当前显示的页面的位置。没有什么比正则表达式更适合搜索字符串了。
一旦知道加载的页面是 Google,下一步就是创建元素并将它们添加到页面中。另外,页面可能是 Google 的搜索页面而不是主页。如果是这样,则应将搜索查询保存在 XML 中。下面是 DocumentComplete EventHandler 中的代码片段。
//if the loaded document is Google then create required HTML Elements
//on page, save queries in XML and load results from XML to Google page
if (Doc != null && System.Text.RegularExpressions.Regex.Match(
IE.LocationURL, @"www\.google\..*").Success)
{
CreateElements(Doc);
SaveResults(URL.ToString());
}
创建元素
Web 页面中加载的文档通过 MSHTML.HTMLDocument 类的实例进行访问。在 MSHTML.HTMLDocument
类中,存储了对浏览器窗口中加载的文档的引用。Web 页面脚本可用的所有 JavaScript 方法也可通过 MSHTML 获得。MSHTML 提供对 Web 页面 DOM 的访问。
要创建新的输入元素,将使用 MSHTML.HTMLDocument
类的 createElement
方法。对这个新的输入元素的引用保存在 MSHTML.HTMLInputElement
类中。新按钮的 type 属性设置为 "Submit"
,以便被 Google CSS 转换,因为 Google CSS 应用于所有 type 属性为 "Submit"
的元素。还将一个子 span 元素添加到我们的自定义按钮中,其 innerText
属性设置为 "我感觉真的很好"
(将在我们的按钮上显示的文本)。
要将自定义按钮(“我感觉真的很好”
)添加到页面,首先需要找到现有 Google 按钮(“I'm Feeling Lucky”
)的引用,然后将我们的按钮添加到其父项中。如上文(在“理解 Google HTML 结构”部分)所示,“I'm Feeling Lucky”按钮对应的 input
元素的 HTML Name 属性是 "btnI"
。要获取所有名称为 "btnI"
的 HTML 元素,将使用 MSHTML.HTMLDocument 类的 getElementsByName 方法。此方法返回 MSHTML.IHTMLElementCollection
接口的对象。从该集合中,可以将“I'm Feeling Lucky”按钮的引用提取到 MSHTML.HTMLInputElement
类中。
将按钮添加到页面后,需要在页面 HTML 中添加一个 Span
元素,该元素将以 anchor
元素的形式显示之前的搜索查询。Span
元素需要添加到 ID 为 "footer"
的 Div
元素中。MSHTML.HTMLDivElement
和 MSHTML.HTMLSpanElement
类用于保存对 HTML Div
和 Span
元素的引用。Span
元素的 display 属性设置为 none
,ID 属性设置为 SpanSearchResults
。当点击自定义按钮时,Span 将显示。
//if a button doesn't exists already then create one and add
//an event handler to its onclick event
if (Doc.getElementById(ButtonId) == null)
{
mshtml.IHTMLElementCollection LuckyButtonCollection =
(mshtml.IHTMLElementCollection)Doc.getElementsByName("btnI");
if (LuckyButtonCollection.length <= 0) return;
mshtml.HTMLButtonElement LuckyButton =
(mshtml.HTMLButtonElement)LuckyButtonCollection.item(name: Type.Missing, index: 0);
mshtml.HTMLButtonElement CustomButton =
(mshtml.HTMLButtonElement)Doc.createElement("button");
CustomButton.id = ButtonId;
CustomButton.className = "gbqfba";
//build a span element which will hold the button name
mshtml.HTMLSpanElement CustomButtonText = (mshtml.HTMLSpanElement)Doc.createElement("span");
CustomButtonText.className = "gbqfsb";
CustomButtonText.innerText = "I'm Feeling Really Good";
CustomButton.appendChild((mshtml.IHTMLDOMNode)CustomButtonText);
LuckyButton.parentNode.appendChild((mshtml.IHTMLDOMNode)CustomButton);
mshtml.HTMLButtonElementClass CustomButtonClass = (mshtml.HTMLButtonElementClass)CustomButton;
CustomButtonClass.HTMLButtonElementEvents2_Event_onclick +=
new HTMLButtonElementEvents2_onclickEventHandler(
CustomButtonClass_HTMLInputTextElementEvents2_Event_onclick);
}
//create the Span element if one doesn't exists
mshtml.HTMLSpanElement Ele = (mshtml.HTMLSpanElement)Doc.getElementById(SpanSearchResults);
if (Ele == null)
{
mshtml.HTMLDivElement ParentDiv = (mshtml.HTMLDivElement)Doc.getElementById("footer");
mshtml.HTMLSpanElement TargetSpan = (mshtml.HTMLSpanElement)Doc.createElement("span");
TargetSpan.id = SpanSearchResults;
if (ParentDiv == null) return;
ParentDiv.insertBefore((mshtml.IHTMLDOMNode)TargetSpan, ParentDiv.firstChild);
TargetSpan.style.display = "none";
}
mshtml.HTMLDivElement LogoDiv = (mshtml.HTMLDivElement)Doc.getElementById("hplogo");
mshtml.HTMLDivElement LogoChildDiv = (mshtml.HTMLDivElement)LogoDiv.firstChild;
LogoChildDiv.innerText = LogoChildDiv.innerText + ". Customized By Hitesh";
按钮点击事件处理程序
现在,由于元素已添加到 Google 网页中,下一步就是为按钮点击添加一些操作。当点击 “我感觉真的很好”
按钮时,它将使 Span
元素(‘SpanSearchResults
’)可见,并将 anchor
元素添加到其中以显示之前的搜索查询。
//onclick of our custom button clear the span which holds any previous searchs and
//reload the searchs in Google page from the saved XML.
static bool CustomButtonClass_HTMLInputTextElementEvents2_Event_onclick(IHTMLEventObj pEvtObj)
{
MSHTML.HTMLDocument doc = (MSHTML.HTMLDocument)pEvtObj.srcElement.document;
MSHTML.HTMLSpanElement SearchResults = (MSHTML.HTMLSpanElement)doc.getElementById("SpanSearchResults");
SearchResults.style.display = "";
if (!LoadResultsInGoogle(doc))
NoResultsMessage(doc);
return true;
}
要将 anchor
元素添加到页面,首先从 XML 文件中读取查询,然后为每个搜索查询创建一个 anchor
元素。每个 anchor 元素将显示搜索查询,点击时将打开一个新窗口,其中包含 Google 针对同一查询的搜索结果。anchor 元素的工具提示将设置为查询搜索并保存在应用程序 XML 中的日期和时间。
将存储搜索查询的 XML 的结构如下。
<searches>
<search text="" URL="" datetime=""/>
<searches>
每个搜索元素对应一个搜索查询,其 text 属性保存搜索的文本,URL 属性保存将同一查询发送到 Google 的 URL,通常是“http://www.google.com/search?q=query”,datetime 属性将存储查询搜索的日期和时间戳。
在 Google 中加载先前的搜索查询
要在网页中显示之前的搜索查询,首先需要从 XML 文件中读取所有“search
”节点。使用 XPath 查询可以从 XML 文件中读取所有“search 节点。
XmlNodeList Nodes = XMLDoc.SelectNodes("//search");
对于找到的每个节点,将创建一个 anchor 元素并添加到我们的自定义 Span 元素中。anchor 元素的 href 属性将是 XML“search
”节点的 URL 属性,anchor 元素的 innerText 将设置为节点的 text 属性,title 属性将设置为字符串“Searched On
”+ 节点的 datetime 属性。target 属性将设置为 _blank
,以确保所有查询都在新窗口中打开。然后,这些 anchor 元素中的每一个都将被添加到我们 ID 属性设置为 'SpanSearchResults
' 的自定义 span 中。
for (int i = 0; i < Nodes.Count; i++)
{
XmlNode CurrNode = Nodes[i];
if (CurrNode.Attributes["text"].Value == "")
continue;
string URL = CurrNode.Attributes["URL"].Value;
MSHTML.HTMLAnchorElement anch = (MSHTML.HTMLAnchorElement)Doc.createElement("a");
if (TargetSpan.firstChild != null)
TargetSpan.insertBefore((MSHTML.IHTMLDOMNode)anch, TargetSpan.firstChild);
else
TargetSpan.appendChild((MSHTML.IHTMLDOMNode)anch);
anch.setAttribute("href", URL);
anch.setAttribute("name", "searchanchors");
anch.target = "_blank";
anch.innerText = CurrNode.Attributes["text"].Value;
anch.style.color = "#aa0000";
anch.title = "Searched On " + CurrNode.Attributes["datetime"].Value;
}
将新查询保存到 XML
现在你已经打开了 Google 并搜索了一些内容,因此你的搜索查询应该作为历史记录保存在 XML 文件中,以便下次再次打开 Google 时可以显示。在 Google 的 URL 中,搜索查询存在于参数“q
”中,格式为 "q=queried+text
"。所以只需要从 URL 中分离出“q
”后面的 =
符号和 &
(表示新参数的开始)之间的部分。这可以通过代码中的正则表达式来实现。
System.Text.RegularExpressions.Regex reg =
new System.Text.RegularExpressions.Regex(@"q=(?<query>[^&]+)");
System.Text.RegularExpressions.Match mt = reg.Match(URL);
string query = "";
string text = "";
if (mt.Success)
{
query = mt.Result("${query}");
}
通常,当在 Google 中搜索任何文本时,URL 中的查询会将所有空格替换为“+
”号。所以所有的“+
”都应该被替换为 <space>
字符,所有 URL 编码的字符都应该被替换为它们的 ASCII 表示。
text = query.Replace("+", " ");
//find the HTML Encoded characters in Query text and replace them with their
//Character
System.Text.RegularExpressions.Regex HexReg =
new System.Text.RegularExpressions.Regex(@"(?<val>%(?<hex>[A-Z0-9]{2}))");
System.Text.RegularExpressions.Match HexMatch = HexReg.Match(text);
while (HexMatch.Success)
{
text = text.Replace(HexMatch.Result("${val}"),
char.ConvertFromUtf32(int.Parse(HexMatch.Result("${hex}"),
System.Globalization.NumberStyles.HexNumber)));
HexMatch = HexMatch.NextMatch();
}
现在,已从 URL 中检索到搜索文本,因此应检查该文本是否已保存在 XML 文件中,如果没有,则应在 XML 的根节点下创建一个新的“Search
”节点并保存。
XmlDocument xmlDoc = LoadXML();
XmlNodeList ExistingNodes =
xmlDoc.SelectNodes("//search[@text='" + text + "']");
//prepare the query and save it in XML
if (ExistingNodes.Count > 0) return false;
XmlElement searchEle = xmlDoc.CreateElement("search");
searchEle.SetAttribute("text", text);
searchEle.SetAttribute("URL", "http://www.google.com/search?q=" + query);
searchEle.SetAttribute("datetime", DateTime.Now.ToString());
XmlNode root = xmlDoc.DocumentElement;
if (root != null)
root.AppendChild(searchEle);
else
xmlDoc.AppendChild(searchEle);
SaveXML(xmlDoc);
下次打开 Google 时,这个新保存的查询将在 Google 页面上显示,并且可以通过单击代表它的 anchor 元素再次导航。
如何使用
要使用该应用程序,请将可执行文件的快捷方式放在 Windows 的 startup 文件夹中。每次 Windows 启动时,可执行文件将无 UI 运行,并将修改每个 Google 实例。
在构建过程中运行应用程序,然后可以通过打开新的 IE 窗口并在此其中打开 Google 来进行测试。
关注点
相同的解决方法可以应用于实时定制许多其他网站。唯一的要求是稍微理解目标网站的 HTML,并知道如何使用 SHDocVw.dll 和 MSHTML.dll。
历史
- 2012年2月16日 - 更新了附加代码,该代码已根据更改的 Google 标记进行了修改。
- 2012年5月6日 - 根据更新的 Google 标记更新了代码和图像。
- 2012年5月8日 - 在新版本中图像未显示。问题已解决