 | 级别: 初级 Scott Archer, 软件架构师, GlowingOrb, Inc. Uche Ogbuji, 首席顾问, Fourthought, Inc.
2003 年 10 月 01 日 本专栏涵盖了可以用于 Web 服务处理的主要 Python API,通过简单的客户端和服务器的使用演示了基本的工具和方法。所有的这些都为使用“现实世界的” Web 服务打下了基础。笔者现在把这些工具和理解应用到几个现实世界的 Web 服务应用程序中。这里的着重点是 Google Web API-- 应用程序可以通过 SOAP 连接到它们,以便程序化地搜索 Web 并获取缓存的 Web 页面。
安装
对于下面的示例,我们将使用
Python version 2.2 和 SOAPpy version 0.10.2。也可以使用其他版本的 SOAPpy 和 Google
API,不过,您需要修改这里列出的实例,以符合
SOAPpy 中对导入和方法签名所作的更改。SOAPpy
的安装是相当简单的:untar / unzip 软件包(在 Windows 中使用 winzip
或 cygwin),然后使用标准的
Python 包安装工具(安装一般作为 root 运行)。参见
清单1。同时参见本专栏结尾的
参考资料部分,以获得下载信息。
清单1. 使用标准的
python 包安装工具
[scott@daedalus SOAPpy-0.10.2]$ python setup.py build
running build
...
[root@daedalus SOAPpy-0.10.2]# python setup.py install
running install
...
|
Google API
为了使用
Google 的 API,您必须首先注册 Google,以获取一个 license-key。您将需要提供一个有效的电子邮件地址,这样就可以把您的 key
到其中。这个 key
不用花费任何任何费用,并且每天可以给您提供多达 1000
次查询。在写作本文时, Google 的 API 服务还是 beta
版本,没有商用(免费或者其他方式)的计划宣布。阅读
API 下载文档中的 license 文件或登陆 Google Web
站点,您就可以了解到关于有效使用和限制条件的细节和其他信息(参见
参考资料)。
下载
Google API 工具集。它不包含任何 Python,但是包括了很好的封装库和
Java 以及 .NET(使用 C# 和 Visual Basic) 的示例。在下载的工具集中包含一些我们可以立即使用的 GoogleSearch WSDL
文件和 Google API 参考手册(采用 HTML 格式)。正如您在
清单2的
WSDL 文件中看到的,Google API 提供了三种操作:
-
doGoogleSearch( )
,用于执行通过 Google 的 Web
查询(目前,该服务不支持搜索图形或者组)。
-
doGetCachedPage( ) ,它将检索
Web 页面,在它的 Web-crawlers 遇到 Web 页面时,由 Google
来进行缓存。
-
doSpellingSuggest( ) ,它将返回所提交的术语清单的正确拼写。
同时也请注意返回的
GoogleSearchResult
是复杂类型。
现在开始
对于第一次登陆
Google,我们将编写 Python 客户端程序来直接构造 XML
字符串形式的 SOAP 请求,并且使用
Python httplib 模块来发送一个 HTTP 消息(参见
清单3)。我们将搜索两个词“spotted
owl”,请求不超过 10 个的结果项('maxResults')并以第一个开头('start',
zero-indexed)——而且,我们将不使用任何过滤器和限制条件。将搜索限制在特定的语言('lr')并从结果中过滤掉成人内容是可能的。要获得关于这些方面的其他详细信息,请参见
Google Web API 参考文档(参见
参考资料)。这里,您可以开始看到连接到服务的简单性。
清单3.
使用 httplib 对 API 的直接
XML
访问
import sys, httplib
_post = '/search/beta2'
_host = 'api.google.com'
_port = 80
# envelope_template is a simple string template that matches the required
# Google API SOAP envelope as described in the WSDL
envelope_template = """<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:doGoogleSearch xmlns:ns1="urn:GoogleSearch"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<key xsi:type="xsd:string">%s</key>
<q xsi:type="xsd:string">%s</q>
<start xsi:type="xsd:int">%d</start>
<maxResults xsi:type="xsd:int">%d</maxResults>
<filter xsi:type="xsd:boolean">%s</filter>
<restrict xsi:type="xsd:string">%s</restrict>
<safeSearch xsi:type="xsd:boolean">%s</safeSearch>
<lr xsi:type="xsd:string">%s</lr>
<ie xsi:type="xsd:string"></ie>
<oe xsi:type="xsd:string"></oe>
</ns1:doGoogleSearch>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>"""
# Google search options - for populating the envelope-template
_license_key = 'INSERT YOUR KEY HERE'
_query = 'spotted owl'
_start = 0
_maxResults = 10
_filter = 'false'
_restrict = ''
_safeSearch = 'false'
_lang_restrict = ''
# populate the outbound SOAP envelope
envelope = envelope_template%( _license_key, _query,
_start, _maxResults,
_filter, _restrict,
_safeSearch, _lang_restrict )
# now, we open an HTTP connection, set required headers, and send the SOAP envelope
envlen = len(envelope)
http_conn = httplib.HTTP(_host, _port)
http_conn.putrequest('POST', _post)
http_conn.putheader('Host', _host)
http_conn.putheader('Content-Type', 'text/xml; charset="utf-8"')
http_conn.putheader('Content-Length', str(envlen))
http_conn.putheader('SOAPAction', '')
http_conn.endheaders()
http_conn.send(envelope)
# fetch HTTP reply headers and the response
(status_code, message, reply_headers) = http_conn.getreply()
response = http_conn.getfile().read()
# dump raw xml
print "----------------------------------------"
print "send headers:\\n", http_conn.headers
print "----------------------------------------"
print "send body:\\n", envelope
print "----------------------------------------"
print " status code: ", status_code
print "status message: ", message
print " reply headers:\\n", reply_headers
print "----------------------------------------"
print "response body:\\n", response
|
清单3中的源代码注释将功能分为几个步骤。第一,在初始的导入之后,我们创建了一个字符,它包含
SOAP-Envelop 的模板,该模板与
清单2的
WSDL 中所定义的结构相匹配。下一步,为了更清楚地进行说明,我们设置了一些变量,以使用
Python 的字符串处理来填充
envelop_template 。然后,我们打开
POST 请求的 HTTP 连接来传送标准的消息头(header),然后发送请求信封(envelope)。在
清单4中我们可以看到来自 GoogleSearch 服务的 XML SOAP 响应。
清单4.
清单2的部分结果 -- 只显示10个结果的第1个(添加了缩排)。
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:doGoogleSearchResponse xmlns:ns1="urn:GoogleSearch"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<return xsi:type="ns1:GoogleSearchResult">
<documentFiltering xsi:type="xsd:boolean">false</documentFiltering>
<estimatedTotalResultsCount xsi:type="xsd:int">
117000
</estimatedTotalResultsCount>
<directoryCategories
xmlns:ns2="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="ns2:Array" ns2:arrayType="ns1:DirectoryCategory[1]">
<item xsi:type="ns1:DirectoryCategory">
<specialEncoding xsi:type="xsd:string"></specialEncoding>
<fullViewableName xsi:type="xsd:string">
Top/Regional/North_America/United_States/Oregon/Localities/S/Sweet_Home
</fullViewableName>
</item>
</directoryCategories>
<searchTime xsi:type="xsd:double">0.074759</searchTime>
<resultElements
xmlns:ns3="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="ns3:Array" ns3:arrayType="ns1:ResultElement[10]">
<item xsi:type="ns1:ResultElement">
<cachedSize xsi:type="xsd:string">2k</cachedSize>
<hostName xsi:type="xsd:string"></hostName>
<snippet xsi:type="xsd:string">
A presentation of bird photographs, songs, identification tips,
distribution maps, and life history information for North American
birds, and a forum for <b>...</b>
</snippet>
<directoryCategory xsi:type="ns1:DirectoryCategory">
<specialEncoding xsi:type="xsd:string"></specialEncoding>
<fullViewableName xsi:type="xsd:string"></fullViewableName>
</directoryCategory>
<relatedInformationPresent xsi:type="xsd:boolean">
true
</relatedInformationPresent>
<directoryTitle xsi:type="xsd:string"></directoryTitle>
<summary xsi:type="xsd:string"></summary>
<URL xsi:type="xsd:string">
http://www.mbr-pwrc.usgs.gov/id/framlst/i3690id.html
</URL>
<title xsi:type="xsd:string">
<b>Spotted</b> <b>Owl</b>
</title>
</item>
...
</resultElements>
<endIndex xsi:type="xsd:int">10</endIndex>
<searchTips xsi:type="xsd:string"></searchTips>
<searchComments xsi:type="xsd:string"></searchComments>
<startIndex xsi:type="xsd:int">1</startIndex>
<estimateIsExact xsi:type="xsd:boolean">false</estimateIsExact>
<searchQuery xsi:type="xsd:string">spotted owl</searchQuery>
</return>
</ns1:doGoogleSearchResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
|

 |

|
更好的方法
尽管直接使用
XML 构造 SOAP 请求,接着解析 XML SOAP
响应,是相当简单的,但是还有几个更高层次的库,它们提供了更加强大和直观的接口。在本专栏前面的讨论中已经探讨了一些这样的接口。尽管
Google Web API 是非常简单的,任何支持复杂类型的 SOAP
库或许都能够正常运行,但是我们将使用 Python 的 SOAPpy
库来与 Google Web 服务交谈。
在这个方法中,我们使用
SOAPProxy
来创建一个 Web 服务的代理(参见
清单5)。通过此
SOAP 代理,我们可以直接调用 Google Web API 上公开的方法——在本例中为
doGoogleSearch( ) 。SOAPpy
以透明的方式处理字符串和整型参数(key、q、start、maxResults、restrict、lr、ie
和 oe)的编组(marshall)。但是,我们必须显式地编组布尔型的数值(filter
和 safeSearch)。注意,根据 Google Web API
文档,最后的两个参数(ie 和 oe)不再使用,因此需要忽略。如果成功地调用了远程方法,SOAPProxy
会自动解析包含结果的 SOAP 对象来创建一个 Python 对象,这个
Python 对象与
WSDL
文档中所描述的结构相匹配。对结果对象的访问是直接通过
Python 属性进行的。
另外, SOAPpy 有一个调试模式,在 SOAPpy
安装中的 Config.py 的第 68 行,将 self.debug 更改为 1,这样就可以启用这个模式(在
*NIXes上,Config.py 文件位于:/usr/lib/Python2.2/site-packages/SOAPpy)。不幸的是,没有别的方法来启用这个特性。在启动了调试模式之后,所有在调用时交换的
HTTP Header 和原始 SOAP XML 将转处到到 stdout 中。
清单5.
使用 SOAPpy 来访问 Google API
from SOAPpy import SOAPProxy
from SOAPpy import Types
# CONSTANTS
_url = 'http://api.google.com/search/beta2'
_namespace = 'urn:GoogleSearch'
# need to marshall into SOAP types
SOAP_FALSE = Types.booleanType(0)
SOAP_TRUE = Types.booleanType(1)
# create SOAP proxy object
google = SOAPProxy(_url, _namespace)
# Google search options
_license_key = 'INSERT YOUR KEY HERE'
_query = 'spotted owl'
_start = 0
_maxResults = 10
_filter = SOAP_FALSE
_restrict = ''
_safeSearch = SOAP_FALSE
_lang_restrict = ''
# call search method over SOAP proxy
results = google.doGoogleSearch( _license_key, _query,
_start, _maxResults,
_filter, _restrict,
_safeSearch, _lang_restrict, '', '' )
# display results
print 'google search for " ' + _query + ' "\\n'
print 'estimated result count: ' + str(results.estimatedTotalResultsCount)
print ' search time: ' + str(results.searchTime) + '\\n'
print 'results ' + str(_start + 1) + ' - ' + str(_start + _maxResults) +':\\n'
numresults = len(results.resultElements)
for i in range(numresults):
title = results.resultElements[i].title
noh_title = title.replace('<b>', '').replace('</b>', '')
print 'title: ' + noh_title
print ' url: ' + results.resultElements[i].URL + '\\n'
|
这时,我们可以简单地使用 SOAPpy
中的
SOAPProxy
对象,而不用显式地创建 SOAP 信封(envelope)并通过
HTTP 发送它。如果为
Web 服务指定了可调用的 URL 和适当的命名空间,那么 SOAPpy
就可以用 Python 的动态绑定,来把调用参数甚至方法名称(
doGoogleSearch( ) )编组为代理服务对象上的一个远程过程调用。然后,就可以访问响应,因为它作为一个聚合对象,与
WSDL 中所指定的
GoogleSearchResult
结构是相匹配的。为了显示这个结果,我们直接访问
results
对象的特性。
清单6. 清单5的结果
[scott@daedalus google_test]$ python google_test.py
google search for " spotted owl "
estimated result count: 117000
search time: 0.070122
results 1 - 10:
title: Spotted Owl
url: http://www.mbr-pwrc.usgs.gov/id/framlst/i3690id.html
title: Mexican Spotted Owl - Home
url: http://mso.fws.gov/
title: EO Study: Spotting the Spotted Owl
url: http://earthobservatory.nasa.gov/Study/SpottedOwls/
title: Western Spotted Owl Printout- EnchantedLearning.com
url:
http://www.enchantedlearning.com/subjects/birds/printouts/Spottedowlprintout.shtml
title: Northern Spotted Owl
url: http://biology.usgs.gov/s+t/SNT/noframe/pn172.htm
title: AMNH - Expedition : Endangered url: http://www.amnh.org/nationalcenter/Endangered/owl/owl.html
title: Northern Spotted Owl - USFS History - Forest History Society
url: http://www.lib.duke.edu/forest/usfscoll/policy/northern_spotted_owl/
title: North American Owl Identification Guide
url: http://www.owlinstitute.org/owls/spotted.html
title: Spotted Owls - Strix occidentalis
url: http://www.owlpages.com/species/strix/occidentalis/Default.htm
title: The Northern Spotted Owl Debate
url: http://www.spa3.k12.sc.us/WebQuests/endangeredanimals/endangered.htm
|
清单6是运行
清单5中的代码的纯文本输出。在显示了搜索中的一些状态之后,我们将遍历每个结果——这里显示了这些结果的标题和
URL。Google API 限定每次搜索取10个结果。注意作为 SOAP
响应的一部分返回的搜索时间——从33亿个 Web
页面中找到超过100,000个(估计)纪录的时间少于十分之一秒。 Google Web API
为强大的 Web 服务提供了易于使用的接口。
Google
如前所述,Google API
提供了比搜索更多的特征——通过 SOAP
接口也可以进行拼写建议和检索缓存的 Web
页面。下面继续我们前面讲过的“owl”主题,在
清单7中,我们使用 SOAPpy
中的
SOAPProxy 来获取 “www.owl.org” 的缓存版本。注意,获取缓存的 Web
页面的请求和响应笔搜索 Web 页面的请求和响应(分别为
doGetCachedPage()
和
results )要容易得多。我们只需通过代理调用
doGetCachedPage() 来传递我们的 license key 和请求的 Web
页面就可以了。
清单7.
使用 SOAP.py 通过 Google API 获取缓存的 Web 页面
from SOAPpy import SOAPProxy
from SOAPpy import Types
# CONSTANTS
_url = 'http://api.google.com/search/beta2'
_namespace = 'urn:GoogleSearch'
# create SOAP proxy object
google = SOAPProxy(_url, _namespace)
# Google search options
_license_key = 'INSERT YOUR KEY HERE'
_query = 'www.owls.org'
# call search method over SOAP proxy
results = google.doGetCachedPage( _license_key, _query )
# store results
of = open('cached_page_response.html', 'w')
of.write(results)
of.close()
|
Google API 返回缓存的base64
编码的页面。在接收到响应之后,SOAPpy 可以很方便地把这种格式的页面解码为 html——然后,我们把它保存到文件系统中,这样就可以通过浏览器进行查看了。这是一个简单的接口,也许是世界上最大的(也是最完整的)整个
Web 的缓存的接口。
图1显示了返回的页面,它已装载到浏览器中。注意,Google
带有一个消息头(header)来标识由 Google 缓存的页面。
参考资料
作者简介  | 
|  | Scott Archer
是一位软件架构师,也是
GlowingOrb, Inc.
的创始人,一个软件工具开发人员。他主要研究模型驱动的解决方案及其与核心业务流程的集成。Archer
从香港大学(University of Hong Kong)获得了 Computational Molecular Biology 的 M.Phil
学位。您可以通过
scott.archer at glowingorb.com
与 Mr. Archer联系。
|
 | 
|  | Uche Ogbuji
是一名顾问和
Fourthought Inc.
的创始人,一个软件供应商和顾问,专长于企业知识管理应用程序的
XML 解决方案。 Fourthought 开发了
4Suite
,XML 中间件的开放源码平台。Ogbuji
是一个计算机工程师和作者,出生于 Nigeria,生活和工作在美国科罗拉多州
Boulder。您可以通过
uche.ogbuji at fourthought.com
与 Ogbuji 联系。
|
对本文的评价
|  |