锻造坊 - 谷歌网络服务
在过去的几个月中,我们一直在研究亚马逊提供的一些 Web 服务,这些服务使我们能够相对轻松地搜索其目录。亚马逊几年前决定将其 Web 服务基本上免费提供,他们认为这将增加最终在其网站上购买商品的人数。 事实上,现在有大量的开发人员使用亚马逊 Web 服务来创建从定制书店到可以帮助进行书店管理的程序等各种内容。
亚马逊并不是唯一一家向外界开放其目录的商业网站。谷歌,另一家 900 磅重的互联网巨头,也在几年前发布了其 Web API。这些 API 使搜索谷歌庞大的 Web 内容目录成为可能。 我们无法知道这个目录是否是世界上最大的,但从我的角度来看,这在某种程度上是无关紧要的。 谷歌的目录足够大,并且更新频率足够高,以至于我可以在大多数时候依靠它作为我的主要搜索引擎。
在过去的几年中,谷歌已经提供了许多不同的 API。本月,我们来看看其中最简单的 API,用于执行 Web 存档的基本搜索。 我们将研究谷歌如何使用 WSDL(Web 服务描述语言)来宣传其 Web 服务,以及我们如何进行 SOAP 调用来搜索谷歌庞大的库以满足我们自己的目的。
如果您使用过亚马逊 Web 服务,那么谷歌 API 的入门过程不会让您感到非常惊讶。 首先,这两家公司都要求您注册才能使用其服务。 这两种情况下的注册都是免费的,并为您提供一个身份验证密钥,该密钥放置在发送到服务器的每个请求中。
要获得谷歌密钥,您首先需要注册一个谷歌帐户。 现在,我有一段时间拥有一个“谷歌帐户”,用于 Gmail 和其个性化新闻页面等服务。 但是,API 似乎链接到另一组帐户。 即使在最初登录我的“主要”谷歌帐户后,我仍然必须注册并登录 API 系统,这让我觉得有点奇怪。
话虽如此,创建帐户非常简单明了。 转到主要的谷歌 API 页面 (www.google.com/apis),单击“创建谷歌帐户”,然后填写表格。 提交 HTML 表单后不久,您将收到来自谷歌的电子邮件,确认您的帐户已创建,并包含您的谷歌密钥,以及一个用于访问以确认帐户创建的 URL。 确认帐户创建后,您就可以继续使用您的谷歌密钥,创建利用谷歌 Web 服务的程序了。
不过,在我们这样做之前,我们应该考虑谷歌对服务以及我们通过该服务检索到的数据施加的限制。 亚马逊允许参与者每秒仅进行一次 API 调用,这意味着在给定的 24 小时内最多可以进行 86,400 次调用。 相比之下,谷歌允许用户在给定的 24 小时内仅进行 1,000 次调用。
此外,定义这些最大值的方式表明了将如何处理违规行为。 如果您在过去 24 小时内进行了超过 1,000 次查询,谷歌将返回错误消息,而亚马逊仅当查询在前一个查询的一秒钟内到达时才会报错。 这两种服务都不会在返回错误消息之前跟踪这些数字,但是显然,从违反亚马逊的限制(休眠一秒钟并重试)中恢复比谷歌的限制更容易(因为程序可能需要休眠长达 24 小时才能重试)。
这两个网站的服务之间存在许多法律差异。 亚马逊率先提出了网络上的联盟供应商的想法,鼓励人们围绕其数据库创建商业服务。 相比之下,谷歌明确声明禁止用户围绕其搜索结果创建商业服务。 (如果您有兴趣创建基于互联网搜索数据的商业服务,请考虑亚马逊的 Alexa Web 搜索平台服务,该服务没有这些限制。 同时,每次 1,000 次请求将花费您 25 美分,对于一个受欢迎的网站来说,这可能会很快累积起来。)
最后,这两个网站之间存在一些技术差异。 亚马逊的 API 通过 SOAP 和 REST 工作,允许开发人员在这两种格式之间进行选择。 相比之下,谷歌仅为其搜索引擎提供 SOAP 接口。 因此,为了创建我们的搜索系统,我们需要安装并使用 SOAP 客户端库。 幸运的是,大多数语言都有允许进行 SOAP 调用的高级库。
SOAP,以前是简单对象访问协议,但现在是一个首字母缩写词,官方上不代表任何东西,它提供了一种相对简单的方法,用于向服务器发送 XML 封装的查询。 然后,服务器会以 XML 编码的响应进行响应。 多年来,SOAP 已经远远偏离了其简单的根基。 虽然 SOAP 仍然比某些更复杂的协议(例如 CORBA)更容易理解、实现和使用,但它比大多数人愿意承认的要困难。 如果可以避免,我个人更喜欢使用 XML-RPC 进行 Web 服务。 虽然 XML-RPC 没有提供 SOAP 的所有功能,但它更容易使用。
话虽如此,谷歌要求我们使用 SOAP,并且现在有许多优秀的 SOAP 客户端库可用,我们不应该害怕使用它。 Perl 程序员可以使用一个特别强大的实现,称为 SOAP::Lite。 对于本文中的编程示例,我们使用 Perl 和 SOAP::Lite。 请注意,模块名称的 Lite 部分描述了程序员可以轻松实现 Web 服务,而不是 SOAP 的精简版本。 您可以通过键入以下内容从 CPAN 安装最新版本的 SOAP::Lite
perl -MCPAN -e 'install SOAP::Lite'
SOAP::Lite 安装程序将要求您指示在安装模块之前要执行哪些测试(如果有)。 我通常接受默认设置,但您可能需要根据您的需求添加或删除这些设置。
安装 SOAP::Lite 后,就该编写一个查询谷歌的程序了。 但是,为此,我们需要知道服务的 URL,以及我们将在谷歌的计算机上调用的方法,以及我们要发送的任何参数的名称和类型。 我们可以手动指定这些,但这将意味着我们要做很多工作。 此外,谷歌目前希望 SOAP 请求指向 api.google.com/search/beta2。 如果谷歌决定在没有警告的情况下更改该 URL,许多人可能会感到惊讶和不安。
幸运的是,谷歌提供了一个 WSDL 文件,描述了通过谷歌 API 提供的服务,以及系统接受的请求和响应参数。 它还描述了查询的端点,允许谷歌(理论上)在不预先通知开发人员的情况下更改服务。 当然,这假设 WSDL 文件本身将保留在相同的位置。 它还假设服务的名称不会更改,并且每个服务都在某个地方进行了文档记录,因为选择要调用的方法仍然需要人工干预。
WSDL 是用 XML 编写的,并且相当容易理解,一旦您意识到它描述的仅仅是在特定服务器上可用的各种 Web 服务,包括输入的数量、名称和类型。 因此,执行 Web 内容基本谷歌搜索的 doGoogleSearch 的 WSDL 条目定义如下
<message name="doGoogleSearch"> <part name="key" type="xsd:string"/> <part name="q" type="xsd:string"/> <part name="start" type="xsd:int"/> <part name="maxResults" type="xsd:int"/> <part name="filter" type="xsd:boolean"/> <part name="restrict" type="xsd:string"/> <part name="safeSearch" type="xsd:boolean"/> <part name="lr" type="xsd:string"/> <part name="ie" type="xsd:string"/> <part name="oe" type="xsd:string"/> </message>
要从 Perl 程序中使用 SOAP::Lite 中的 WSDL,我们使用 WSDL 文件的 URL 调用 SOAP::Lite->service。 如果文件位于本地文件系统上,请确保 URL 以 file: 开头。 例如
my $google_wsdl = "http://api.google.com/GoogleSearch.wsdl"; my $query = SOAP::Lite->service($google_wsdl);
然后,SOAP::Lite 足够智能地浏览 WSDL 并动态地使所有声明的方法可用,这样我们就可以执行以下操作
my $results = $query->doGoogleSearch($google_key, $query_string, $starting_page, $max_results, $filter, $geographic_restriction, $safe_search, $language_restriction, 'utf-8', 'utf-8');
您看到这里发生了什么吗? 在 WSDL 中描述的输入和我们传递给 $query->doGoogleSearch() 的参数之间存在一对一的映射。
我们现在已经看到了用 Perl 编写的谷歌搜索程序的核心。 剩下的就是回顾输入参数和 $results 的内容,其中包含从谷歌返回的结果。
www.google.com/apis/reference.html 上的 API 文档描述了输入参数。 它们都是强制性的,但其中一些比其他参数更重要。 特别是,通常会设置谷歌密钥和查询字符串,其他参数将设置为简单的默认值,如清单 1 所示。
清单 1. google-query.pl
#!/usr/bin/perl use strict; use diagnostics; use warnings; use SOAP::Lite; # ------------------------------------------------------------ # Get the Google key from ~/.google_key my $google_key_file = "/Users/reuven/.google_key"; open GOOGLE_KEY, $google_key_file or die "Cannot read '$google_key_file': $! "; my ($google_key) = <GOOGLE_KEY>; chomp $google_key; close GOOGLE_KEY; # ------------------------------------------------------------ # Get the command-line argument if ($#ARGV != 0) { print "$0: Invoke with a single argument, your Google search term.\n"; exit; } my $query_string = shift @ARGV; # ------------------------------------------------------------ # Get the WSDL file my $google_wsdl = "http://api.google.com/GoogleSearch.wsdl"; my $query = SOAP::Lite->service($google_wsdl); # ------------------------------------------------------------ # Use the WSDL to make the query my $starting_page = 1; my $max_results = 10; my $filter = 'false'; my $geographic_restriction = ''; my $safe_search = 'false'; my $language_restriction = ''; my $results = $query->doGoogleSearch($google_key, $query_string, $starting_page, $max_results, $filter, $geographic_restriction, $safe_search, $language_restriction, 'utf-8', 'utf-8'); my @results = @{$results->{resultElements}}; if (@results) { # Iterate through each result we got my $counter = 1; foreach my $result (@results) { print "Result $counter of ", $#results + 1, ":\n"; foreach my $key (sort keys %{$result}) { my $value = $result->{$key}; # Is this a hash value? If so, display it accordingly if (UNIVERSAL::isa($value, 'HASH')) { print "\t'$key':\n"; foreach my $subkey (sort keys %{$value}) { print "\t\t'$subkey' => '$value->{$subkey}'\n"; } } # Display the value as a simple string else { print "\t'$key' => '$value'\n"; } } $counter++; } } else { print "There were no results for your query of '$query_string'.\n"; }
包括我自己内的大多数人通常都希望使用我们的查询来查询尽可能多的网页; 但是,有时更适合仅从特定地理位置或特定语言的服务器检索数据。 谷歌的 API 使这成为可能和直接的事实为许多不同的有趣应用打开了大门。
正如我们通过 SOAP 编码的 XML 向谷歌发送查询一样,我们也会收到 SOAP 编码的 XML 结果。 但是,由于 SOAP::Lite 使我们不必为查询编写哪怕是一点 XML,因此在响应方面,我们同样会受到保护。 $results 变量提供了对我们在响应中接收到的数据的 Perl 接口。
我们究竟会收到什么数据? 要知道这一点,我们可以再次查看 WSDL 文件。 它(除其他外)表明我们将收到一组结果作为响应,每个结果看起来都像这样
<xsd:complexType name="ResultElement"> <xsd:all> <xsd:element name="summary" type="xsd:string"/> <xsd:element name="URL" type="xsd:string"/> <xsd:element name="snippet" type="xsd:string"/> <xsd:element name="title" type="xsd:string"/> <xsd:element name="cachedSize" type="xsd:string"/> <xsd:element name="relatedInformationPresent" type="xsd:boolean"/> <xsd:element name="hostName" type="xsd:string"/> <xsd:element name="directoryCategory" type="typens:DirectoryCategory"/> <xsd:element name="directoryTitle" type="xsd:string"/> </xsd:all> </xsd:complexType>
换句话说,我们从谷歌收到的每个搜索结果(最多十个)都将提供创建看起来像谷歌结果页面的所有信息。 此外,我们可以挑选和选择我们要显示的元素,例如,仅显示标题和 dmoz 目录类别和标题。 或者我们可以显示搜索页面的简短摘要。 或者所有这些。 或者什么都不显示。
doGoogleSearch 不是 WSDL 文件中描述的唯一方法。 还有其他方法,例如使用谷歌的缓存页面和检查单个单词的拼写。 当 Web 服务首次向公众公开时,一个常见的例子是,文字处理器现在将能够调用远程 Web 服务进行拼写检查,而不是配备内置系统。 那一天在未来仍然遥遥无期,但您可以想象使用谷歌的 API 来实现这种服务的实验版本。
此外,我们可以将这些输出用作另一个 Web 服务调用的输入,无论是在本地还是远程。 结合来自多个站点的数据正变得越来越流行,尤其是在与谷歌地图 API 结合使用时。 看到以这种方式组合服务会发生什么真是令人惊叹——我们将在未来几个月内探索这一点。
本月,我们简要了解了谷歌的搜索 API。 使用一些简单的工具,包括用于 Perl 的 SOAP::Lite 模块,我们能够构建一个简单的谷歌搜索页面的命令行版本。 在接下来的几个月中,我们将研究谷歌地图 API,并开始了解我们如何创建组合多个数据源的混合服务。
本文的代码可在 ftp.ssc.com/pub/lj/listings/issue145/8866.tgz 获取。
本文资源: /article/8881。
Reuven M. Lerner 是一位长期的 Web/数据库顾问,目前是伊利诺伊州埃文斯顿西北大学学习科学专业的博士生。 他和他的妻子最近庆祝了他们的儿子 Amotz David 的诞生。