图像和位置搜索(aroo)
索引您的网站(包括图像和地理数据),然后在 Google Earth 中搜索和显示结果。

背景
本文是继前五篇 Searcharoo 示例之后的延续
Searcharoo 1 是一个简单的搜索引擎,它爬取文件系统。非常粗糙。
Searcharoo 2 添加了一个“爬虫”来索引网页链接,然后搜索多个单词。
Searcharoo 3 将目录保存起来以便按需加载;爬取了 FRAMESETs 并添加了停止词、开始词和词干提取。
Searcharoo 4 添加了非文本文件类型(例如 Word、PDF 和 PowerPoint),改进了 robots.txt 支持,并添加了一个远程索引控制台应用程序。
Searcharoo 5 在 Medium Trust 环境下运行,并将 FilterDocument
重构为 DownloadDocument
及其子类,用于索引 Office 2007 文件。
版本 6 简介
已进行以下添加
- 扩展在 v5 中引入的
DownloadDocument
对象层次结构,以索引 Jpeg 图像(即照片等图像文件中的元数据)以及新的 Microsoft XPS(XML 纸张规范)格式(如果您在 .NET 3.5 中编译)。 - 将纬度/经度(GPS)位置数据添加到基类,并索引嵌入在 Jpeg 图像中的地理位置数据以及 HTML 文档的 META 标签。在搜索结果中包含位置信息,并提供一个链接以在 Google Maps 和 Google Earth 上显示该位置。
- 允许 Searcharoo 在 ASP.NET 应用程序被限制为“无”信任的网站上运行。v4 中的远程索引控制台应用程序以及v5 中的更改旨在解决此问题 - 但我遇到了一种情况,即不允许任何文件或 Web 访问。现在代码允许将目录(远程)编译到程序集中,并在任何信任场景下上传。
- 次要改进:在 Indexer.EXE 控制台应用程序中添加了颜色,允许设置多个“起始页”(因此您可以索引您的网站、您的博客、您托管的论坛等)。
- 错误修复,包括:<TITLE> 标签解析、正确识别外部网站链接以及转义的&amp; 符号。
注意:此版本的 Searcharoo 仅显示位置(纬度、经度)数据 - 它不“按位置搜索”。要了解如何使用位置数据进行“附近”搜索,请参阅商店定位器:帮助客户找到您(使用 Google Maps)文章。
图像索引(读取 Jpeg 元数据)
在Searcharoo 5 中,添加了 DownloadDocument
类来处理 IFilter 和 Office 2007 文件 - 这些文件需要先下载到磁盘,然后才能运行其他代码来提取和索引其文本内容。
添加对索引 Jpeg 图像元数据的支持遵循相同的简单步骤 - 子类化 DownloadDocument
并结合处理 IPTC/EXIF 和 XMP 的代码,以读取嵌入在 Jpeg 文件中的任何信息。

对象模型的一些简单更改如上所示 - 实际的 JpegDocument
类是从 DocxDocument 克隆而来的,其解析代码来自 Asim Goheer 的 EXIFextractor 和 Omar Shahine 的使用 C# 读取 JPEG 的 XMP 元数据。
代码的两个关键部分如下所示 - 第一个使用 System.Drawing.Imaging
提取 PropertyItems,然后将它们与一组已知的、硬编码的十六进制值进行解析;第二个从 Jpeg 二进制数据中提取一个 Xml“岛”。这两种方法返回的元数据键/值对有所不同(有时会重叠) - 我们只使用了其中的一小部分(标题、描述、关键字、GPS 位置、相机品牌和型号)。还有几十个值,包括焦距、闪光灯设置 - 相机支持的任何内容 - 但如果您需要,可以自行解析其他字段。
System.Drawing.Imaging 和 EXIFextractor
EXIF 数据以“二进制”格式存储 - 在 Notepad2 中打开一个已标记的图像会显示可识别的数据和二进制标记。

二进制结构被 .NET 的 System.Drawing.Imaging
代码(如下)识别,但您必须知道要提取的每条数据的十六进制代码和数据类型。
using System.Collections; // DictionaryEntry
using System.Drawing.Imaging;
// ...
public static PropertyItem[] GetExifProperties(string fileName)
{
using (FileStream stream =
new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
using (System.Drawing.Image image =
System.Drawing.Image.FromStream(stream,
/* useEmbeddedColorManagement = */ true,
/* validateImageData = */ false))
{
return image.PropertyItems;
}
}
}
Searcharoo 使用 EXIFextractor 代码,以便于查看和更改 - 还可以通过将 Exiv2.dll 库集成到您的代码中来访问 EXIF 数据的替代方法,但留给您自行处理。
通过 XML 岛进行 XMP
与 EXIF 数据不同,XMP 在 JPG 文件中基本上是“人类可读的”,您可以在下面看到 - 在二进制图像数据中有一个纯 XML 的“岛”。重要的是,XMP 是获取标题和描述信息的唯一途径,这对于搜索非常有用。

public static string GetXmpXmlDocFromImage(string filename)
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(filename))
{
contents = sr.ReadToEnd();
sr.Close();
}
beginPos = contents.IndexOf("<rdf:RDF", 0);
endPos = contents.IndexOf("</rdf:RDF>", 0);
// ... then get title
xmlNode = doc.SelectSingleNode(
"/rdf:RDF/rdf:Description/dc:title/rdf:Alt", NamespaceManager);
// ... then get description
xmlNode = doc.SelectSingleNode(
"/rdf:RDF/rdf:Description/dc:description/rdf:Alt", NamespaceManager);
// ... etc.
值得注意的是,如果您使用 .NET 3.0 或更高版本,提取 XMP 元数据比上述示例更“科学” - 使用 WIC - Windows Imaging Component 来访问照片元数据。为了使此版本的 Searcharoo 与 .NET 2.0 兼容,已经(暂时)避免使用这些较新的功能。
如果您想为图像添加元数据,请尝试 iTag:照片标记软件(推荐)。iTag 用于标记测试期间使用的许多照片。
索引地理位置(纬度/经度)
图像元数据和 HTML Meta 标签
只有两种文档类型有“标准”的地理标记方法:HTML 文档通过 <META >
标签,Jpeg 图像通过其元数据。图像的位置与其余元数据一起提取(如上所述),但对于 HtmlDocument
类,我们需要添加代码来解析以下两个“标准”地理标记中的一个(只需要一个标记,而不是两个)
<meta name="ICBM" content="50.167958, -97.133185"> <meta name="geo.position" content="50.167958;-97.133185">
代码目前使用以下正则表达式/for
循环解析出描述、关键字和机器人 META 标签。

添加对 ICBM 和 geo.position 的支持,只需添加几个额外的 case
子句即可。

一旦我们获得 HTML 或 Jpeg 的经纬度,它就会被设置在基类 Document
的属性中;然后可以将其复制到 Catalog File
对象中(这些对象将被持久化以供以后搜索)。
附加元数据:文件类型和关键字(标签)
由于我们需要为基类 Document
添加位置属性,因此开始存储关键字和文件类型以提高搜索准确性和用户体验似乎是合乎逻辑的。在这两种情况下,都在基类 Document
中添加了一个新属性,并更新了相关的子类(能够“读取”该数据的子类)以填充该属性。
目前只有 Html 和 Jpeg 类支持关键字解析,但几乎所有子类都正确设置了指示文件类型的Extension 属性。
这些额外信息将为用户在查看结果时提供更多反馈,并且将来可用于:(a)备选搜索结果导航(例如,标签云)和/或(b)在匹配关键字时更改排名算法。
“无”信任 Catalog 访问
这个问题延续了 Searcharoo 5 中讨论的“中等”信任问题,因此再次阅读该文章可能是有益的。
基本上,新问题是连 WebClient 权限都不允许,因此 Search.aspx
代码无法从任何文件(无论是通过本地文件访问还是 URL)加载 Catalog
。Searcharoo 需要一种方法在 Web 服务器上将 Catalog
加载到内存中,而无需任何特殊权限……最简单的方法似乎是将 Catalog 编译到代码中!
所需的步骤是
- 运行 Searcharoo.Indexer.EXE 并使用正确的配置远程索引您的网站
- 将生成的 z_searcharoo.xml 目录文件(或您在 .config 中为其指定的任何名称)复制到特殊的 WebAppCatalogResource 项目
- 确保 Xml 文件 **Build Action: Embedded Resource**
- 确保该项目只有这一个资源
- 编译解决方案 -
WebAppCatalogResource.DLL
将被复制到WebApplication
\bin\ 目录 - 将
WebAppCatalogResource.DLL
部署到您的服务器
System.Reflection.Assembly a = System.Reflection.Assembly.Load("WebAppCatalogResource"); string[] resNames = a.GetManifestResourceNames(); Catalog c2 = Kelvin<Catalog>.FromResource(a, resNames[0]);
最后一点:而不是删除在“Full”和“Medium”信任下运行的 Binary 或 Xml 序列化功能,所有方法仍然可用。Binary 或 Xml 由(您的网站和 Indexer 控制台应用程序的)web.config
/app.config
设置控制。
<appSettings> <add key="Searcharoo_InMediumTrust" value="True" /> </appSettings>如果设置为
True
,Catalog 将保存为 *.XML 文件,如果设置为 False
,则保存为 *.DAT。只有当代码无法加载这两个文件中的任何一个时,才会使用资源 DLL(一个简单的强制方法是删除所有 .DAT 和 .XML Catalog 文件)。
呈现新索引的数据
上述更改主要集中在添加索引功能:查找新数据(纬度、经度、关键字、文件类型)、对其进行目录化以及允许访问 Catalog。呈现包含这些附加数据的搜索结果需要对 File
和 ResultFile
类进行一些更改,这些类在执行搜索并显示结果时由 Search.aspx
页面使用。

添加到 Document
的新属性(如上)在 File
中也有镜像,并在需要时由显示友好的只读属性“包装”在 ResultFile
中。ResultFile
类绑定到 asp:Repeater
以生成以下输出(请注意某些结果旁边的坐标,它们链接到 maps.google.com 上的特定位置)

SearchKml.aspx
存在位置数据(纬度和经度坐标)不仅允许我们“链接”到单个位置 - 它还允许以全新的方式查看搜索结果!当发现一个或多个结果链接具有位置数据时,将显示一个新的 Google Earth 视图链接。它链接到 SearchKml.aspx
页面(该页面继承自 Search.aspx
的相同“代码隐藏”),但它不显示 HTML,而是以 KML(Keyhole 标记语言,Google Earth 使用)格式显示结果!
KML 输出与来自 Search.aspx 的 HTML 非常不同

但是,指向 SearchKml.aspx 的链接的格式更像一个“文件引用”:例如 searcharoo.net/SearchKml/newyork.kml 会带您到下面的屏幕截图。


您可能会想,链接 searcharoo.net/SearchKml/newyork.kml 是如何(以及为什么)使用 SearchKml.aspx
页面呢?
URL 格式(使用 .kml 扩展名并嵌入搜索词)的原因是,它允许浏览器根据文件扩展名打开 Google Earth(当安装了 Google Earth 时,它会将 .KML 和 .KMZ 注册为已知文件类型)。因为链接“看起来”是指向一个 newyork.kml 文件(而不是一个 ASPX 页面),所以大多数浏览器/操作系统会自动在 Google Earth(或其他为该文件类型注册的程序)中打开它。
链接语法是使用 **404 自定义错误处理程序**实现的(这也必须在 web.config 或 IIS 自定义错误选项卡中设置)。

注意:使用自定义 HttpHandler 可以实现相同的“行为”,但它需要将 .KML 扩展名“映射”到 IIS 中的 .NET Framework - 这在廉价托管提供商上并不总是可行的(他们通常仍然允许您设置自定义 404 URL)。使用 .NET 3.5 SP1 中引入的新 URL Routing Framework 会更容易。目前,Searcharoo 使用最简单的方法 - 404.aspx。
次要增强
颜色编码的 Indexer.EXE
这纯粹是为了使 Searcharoo.Indexer.EXE 更易于使用而进行的化妆品更改;它使用了 Philip Fitzsimons 的文章在控制台上使用颜色文本。每种不同的“日志记录级别”(或‘详细程度’)都以不同的颜色输出,以便于阅读。
“详细程度”在 Searcharoo.Indexer.EXE.config 文件(Visual Studio 中的 app.config)中设置。
运行时外观如下

多个起始 URL
Searcharoo 的早期版本只允许一个“起始 URL”,并且任何离开该 URL 的链接都会被忽略。版本 6 现在允许指定多个“起始 URL” - 它们都将被索引并添加到同一个 Catalog
中进行搜索。在 .config 文件中用逗号或分号分隔多个子域(例如,也许您有 forums.* 和 www.* 域名;或者您希望同时索引您的博客和您的照片共享网站)。

警告:索引需要时间并占用网络带宽 - **不要**在不了解需要多长时间的情况下索引大量站点。如果您在索引过程中途停止,Catalog
不会保存!
识别“完全限定”的本地链接
一些人报告(但未解决)的一个“bug”已经得到解决 - 如果您的网站有“完全限定”的链接(例如,我的博客 conceptdev.blogspot.com 的所有锚标记都指定为“http://conceptdev.blogspot.com/2007/08/latlong-to-pixel-conversion-for.html”),这些链接会被标记为“外部”且未被爬取(HtmlDocument
类,大约在第 360 行)。
Spider.cs
已更新,将这些链接添加到 LocalLinks
ArrayList 中。

注意:代码进行“开头匹配”比较,因此如果您为“起始 URL”指定了子目录(例如 http://searcharoo.net/SearcharooV1/),那么指向另一个子目录的完全限定链接仍不会被索引(例如,lt;a href="http://searcharoo.net/SearcharooV2/SearcharooSpider_alpha.html"> 将不会被跟踪)。
错误修复(光荣榜)
非常感谢以下 CodeProject 的读者/贡献者
<TITLE> 标签解析
Erick Brown [work] 识别出 <TITLE> 标签中的 CRLF 导致其未被索引的问题……并提供了一个新的 Regex 来修复它。
正确识别外部网站链接
mike-j-g(后来 hitman17)正确地指出链接匹配(在 HtmlDocument
中)是区分大小写的,并提供了一个简单的修复。
处理查询字符串中的转义的&amp; 符号
再次感谢 Erick Brown [work] 指出问题(并提供修复)并妥善处理了查询字符串中的&amp; 符号。
代理支持
stephenlane80 提供了通过代理服务器下载的代码(他的更改已添加到 Spider.Download()
和 RobotsTxt.ctor()
)。已添加一个新的 .config 设置来存储代理服务器 URL(如果需要)。

解析来自 Unix 服务器的 robots.txt
maaguirr 建议更改 RobotsTxt
类,以便它能正确处理 Unix 服务器上的 robots.txt 文件(或任何行尾与标准 Windows CRLF 不同的地方)。
试用
为了在不下载和设置代码的情况下“试用”Searcharoo,现在在 searcharoo.net 上有一组测试文件,您可以在此处搜索。这些测试文件包括各种专门编写的文件(例如,用于测试 Frames、IFrames、META 标签等),以及一些来自各种假期的地理标记照片。
总结
显然,此版本最大的变化是能够使用 JPG 格式中的元数据来“索引”图像。还添加了其他元数据(关键字、文件类型),并创建了一个基础结构,如果您愿意,可以索引和存储更多信息。
这些更改为将来提供了许多新的可能性,包括:“搜索附近”功能,按与给定点或搜索结果的距离对结果进行排序;更复杂的标签/关键字显示和处理;解析其他文档类型的附加元数据(例如,Office 文档在其属性中有一个关键字字段);以及您能想到的任何其他内容。