Python Web服务

Python Web服务器必须实现WSGI接口的服务器端,所有的现代Python Web框架已经实现 了WSGI接口的框架端了,这就让你可以不用修改服务器代码,适应某个框架。

第 13 章 Web 服务

我没有对Twitter 上瘾。我只在有空的时候刷推特,比如,吃饭的时候、休息的时候、这个时候、那个时候、所有时候。

—佚名,早于 2010 年 5 月

本章内容:

  • 简介;
  • Yahoo!金融股票报价服务器;
  • Twitter 的微博。

本章将简要介绍如何使用当今可接触到的一些 Web 服务,如较老的 Yahoo!金融股票报价服务器以及较新的 Twitter。

13.1 简介

网络上有许多 Web 服务和应用,分别提供不同的功能。大部分较大的公司,如 Yahoo!、Google、Twitter 和  Amazon,都会为这些服务提供应用编程接口(API)。过去,API 仅仅用来在使用服务时访问数据。但今日的 API 有所不同,不但有更加丰富的功能,而且通过这些API 可以将服务整合到个人网站和页面中,这种方式通常称为“mash-up”。

这是个非常有趣的领域,后面会进一步探讨其他一些技术(REST、XML、JSON、RSS、Atom 等)。现在先回头看看一个已经存在很久但仍然很有用的 API,即 Yahoo!提供的股票报价服务器,参见 http://finance.yahoo.com。

13.2 Yahoo!金融股票报价服务器

如果访问 Yahoo! Finance 网站,选择任意一只股票的报价,会在页面下方,报价数据下面的“Toolbox”中发现一个标为“Download Data”的 URL 链接。用户通过这个链接下载.csv文件,以导入 Microsoft Excel 或 Intuit Quicken 中。如果访问的是 GOOG 的股票,则 URL 类似下面这样。

http://quote.yahoo.com/d/quotes.csv?s=GOOG&f=sl1d1t1c1ohgv&e=.csv

如果浏览器的 MIME 设置是正确的,浏览器会启动在系统中配置过用来处理 CSV 数据的软件,它们通常是类似 Excel 或 LibreOffice Calc 这样的电子表格应用。这主要是因为链接中的最后一个变量(键值对)是“e=.csv”。服务器实际不使用这个变量,而它总是返回 CSV格式的数据。

如果使用 urllib2.urlopen(),会返回的一个 CSV 字符串,其中含有股票代号。

>>> from urllib2 import urlopen
>>> url = 'http://quote.yahoo.com/d/quotes.csv?s=goog&f=sl1d1c1p2'
>>> u = urlopen(url, 'r')
>>> for row in u:
... print row
... 
"GOOG",600.14,"10/28/2011",+1.47,"+0.25%"

>>> u.close()

接着需要手动解析这个字符串(去除末尾空格,以及根据逗号分割符切分)。除了自行解析数据字符串之外,也可以使用csv 模块(Python 2.3 新增),该模块可以切分字符串并去除空格。通过 csv,可以使用下面的代码替换掉前面示例中的 for 循环,其他内容不变。

>>> import csv
>>> for row in csv.reader(u):
... print row
...
['GOOG', '600.14', '10/28/2011', '+1.47', '+0.25%']

通过分析由 URL 字符串传递给服务器的参数字段f,以及阅读 Yahoo!针对该服务的在线帮助,可以发现符号(sl1d1c1p2)分别对应股票代号、收盘价、日期、变化量、变化百分比。

更多信息可以阅读 Yahoo! Finance 帮助页面,只须在该页面搜索“download data”或 “download spreadsheet format”即可。进一步分析 API 会发现更多选项,如上一次收盘价、52周内的最高和最低价等。表13-1 总结了这些选项,以及返回的格式(这是15 年前真实的Yahoo!股价,不要感到惊讶 )。

表 13-1   Yahoo!股票报价服务器参数

股票报价数据字段名①返回格式②
股票代号s“YHOO”
上次交易价格l1328
上次交易日期d1“2/2/2000”
上次交易时间t1“4:00pm”
与上次收盘价比较c1+10.625
与上次收盘价比较百分比p2“+3.35%”
上次收盘价p317.375
上次开盘价o321.484375
当日最高价h337
当日最低价g317
过去 52 周股价范围w“110 – 500.125”
交易量v6703300
市值j186.343B
每股收益e0.20
市盈率r1586.88
公司名称n“YAHOO INC”

① 字段名称的第一个字符是字母,第二个字符(如果存在)是数字。

② 有些返回值含有额外的引号,尽管这些值都是作为服务器返回的单个 CSV 字符串的一部分。

服务器根据用户指定的顺序显示字段名称。将这些字段连接起来,组成单个字段参数f, 作为请求 URL 的一部分。就像在表 13-1 脚注②中提到的,有些返回的组件分别用引号括起来。这取决于解析器提取数据的方式。观察前面例子中手动解析与使用 csv 模块分别得到的结果子字符串。如果无法获得相应的值,报价服务器会返回“N/A”,如下面的代码所示。

例如,如果使用 f=sl1d1c1p2 向服务器发起一个字段请求,会得到下面这样的字符串(这是我在 2000 年运行的查询结果)。

"YHOO",166.203125,"2/23/2000",+12.390625,"+8.06%"

而对于不再公开交易的股票,会得到下面这样的内容(注意,即使是N/A 也会用引号括起来):

"PBLS.OB",0.00,"N/A",N/A,"N/A"

还可以指定多个股票代号,输出结果中每一行显示一个公司的数据。但要注意 Yahoo!Finace 帮助页面上说的:“在 Yahoo!上显示的任何数据都严格禁止再次发布”,所以只能将这些数据作为个人用途。另外还要注意,这些下载得到的报价有延迟。

根据这些内容,来构建一个读取并显示一些关注的互联网公司股票报价数据的应用,如示例 13-1 所示。

示例 13-1 Yahoo! Finance 股票报价示例(stock.py)
该脚本从 Yahoo! quote 服务器下载并显示股票价格。

当运行该脚本时,输出结果如下所示。

$ stock.py

Prices quoted as of: Sat Oct 29 02:06:24 2011 PDT 
TICKER PRICE CHANGE %AGE
------ ----- ------ ---- 
"YHOO" 16.56 -0.07 "-0.42%"
"DELL" 16.31 -0.01 "-0.06%"
"COST" 84.93 -0.29 "-0.34%"
"ADBE" 29.02 +0.68 "+2.40%"
"INTC" 24.98 -0.15 "-0.60%"

逐行解释

第 1~7 行
这个 Python 2 脚本使用 time.ctime() 显示股票信息从 Yahoo! 下载下来的时间点, urllib2.urlopen()连接到 Yahoo!的服务以获取股票数据。接下来是股票代号的导入语句,以及获取所有数据的固定 URL。

第 9~12 行
这一小块代码显示下载股票信息的时间点,并使用 urllib2.ulropen()获取请求的数据(如果读过本书之前的版本,会注意到这里简化了输出代码,感谢之前眼尖的读者!)。

第 14~18 行
从 Web 下载下来的数据作为一个文件类型的对象打开,遍历该对象获取每一行,分隔由逗号间隔的列表,接着显示到屏幕上。

与从文本文件读取类似,这里依然保留了行末的终止符,所以需要向每个 print 语句的末尾添加一个逗号,来消除换行符的影响;否则,每个数据行之间会有两个空行。

最后,注意,有些返回的字段含有引号。本章末尾会有若干练习,用来改进默认的输出格式。

13.3 Twitter 微博

这一节将会介绍通过 Twitter 服务构成的微博世界。它首先简要介绍社交网络,描述Twitter 所扮演的角色,了解其提供的多种 Python 接口,最后,分别介绍一个简单和一个相对较复杂的示例。

13.3.1 社交网络

过去五年多来,社交媒体得到了长足的发展。它首先仅仅是一个简单的概念,如 Web 登录,或发布一些简短的消息。这种类型的服务需要用户登录账号,用户可以发布文章或其他形式的文体。可以将其想象为公共在线期刊或日记,人们可以对当前事件发布观点或批评, 或任何想让别人知道的事情。

但在线意味着全世界都会知道分享的内容。用户无法只针对特定的人或组织,如朋友或家人,发布信息。因此诞生了社交网络,MySpace、Facebook、Twitter 和 Google+就是最广为人知的。通过这些系统,用户可以与他们的朋友、家人、同事,以及社交圈内的人沟通。

尽管对于用户来说,这些服务大同小异。但这些社交网络依然有各自的区别。比如各自的交互方式就不同,因此,这些社交网络之间并不是完全处于竞争关系。首先介绍各个社交工具, 接着深入了解 Twitter。

MySpace  主要面向年轻人(初中或高中),侧重于音乐。Facebook  原先专注于大学生,但现在面向所有人群。与 MySpace 相比,Facebook 是个更加通用的平台,可以在上面托管应用,这是让 Facebook 成为主流社交工具的特性之一。Twitter 是微型博客服务,与传统博客相比,用户在Twitter 上发布状态,通常是对某件事的观点。而 Google+是这个因特网巨人最近对该领域的尝试,试图提供与其他工具相似的功能,同时也包括一些新的功能。

在这些常见的社交媒体应用中,最基本的是 Twitter。用户可以使用 Twitter 发布短小的状态信息,称为推文。其他人可以“关注”你,即订阅你的推文。同样,你也可以关注其他感 兴趣的人。

将 Twitter 称为微型博客服务是因为与标准博客不同,标准博客允许用户创建任何长度的博文,而每条推文最长限制为 140 个字符。这个长度限制主要是因为该服务原先是面向手机的,它是通过短消息服务(Short Message Service,SMS)支持的 Web 和文本信息,SMS 只能处理 160 个ASCII 字符。这样用户不用阅读冗长的内容,而发布者则必须在 140 个字符内将事情表述清楚。

13.3.2 Twitter 和Python

Twitter API 库中有若干 Python 库。在 Twitter 的开发者文档中有介绍(https://dev.twitter. com/docs/twitter-libraries#python)。这些库之间既有相同点,又有各自的特点,所以建议读者自己分别试用一下,找到最适合自己的库。这里不做过多的限制,本章将会使用到  Twython  和Tweepy。这两个库分别位于 http://github.com/ryanmcgrath/twython 和 http://tweepy.github.com。与大多数 Python 包相同,可以选择 easy_intall 或 pip 分别安装,也可以用一款工具同时安装这两个库。如果对库的源码感兴趣,可以在 GitHub 上了解对应的代码库。另外,可以直接从 GitHub 上下载最新的.tgz 或.zip 格式的文件,然后调用经典的 setup.py install 命令安装。

$ sudo python setup.py install 
Password:
running install 
running bdist_egg 
running egg_info
creating twython.egg-info
. . .
Finished processing dependencies for twython==1.4.4

类似 Twython 这样的库需要通过一些额外的方式与 Twitter 交互。其依赖于 httplib2、oauth2 和 simplejson。(最后一个在 Python 2.5 之前作为外部 json 库,从 Python 2.6 开始成为标准库。)

起步

为了能开始上手,这里用一个简单的示例介绍如何使用 Tweepy 库在 Twitter 上进行搜索。

# tweepy-example.py
import tweepy
results = tweepy.api.search(q='twython3k')
for tweet in results:
    print ' User: @%s' % tweet.from_user 
    print ' Date: %s' % tweet.created_at 
    print ' Tweet: %s' % tweet.text

如果执行这个 Python 2 脚本(在本书编写时,Tweepy 还不支持 Python 3)进行查询,读者会注意到这个搜索的关键字是专门选定的,只能找到很少的搜索结果。也就是说,无论使用Python 3 版本的 Twython 库,还是这个 Tweepy 版,Twitter 只会返回几条推文(在本书编写时)。

$ python twython-example.py 
    User: @wescpy
    Date: Tue, 04 Oct 2011 21:09:41 +0000
    Tweet: Testing posting to Twitter using Twython3k (another  
 story of life on the bleeding edge)

    User: @wescpy
    Date: Tue, 04 Oct 2011 17:18:38 +0000
    Tweet: @ryanmcgrath cool... thx! i also have a "real" twython3k bug i need to file... will do it officially on github. just giving you a heads-up!

    User: @wescpy
    Date: Tue, 04 Oct 2011 08:01:09 +0000
    Tweet: @ryanmcgrath Hey ryan, good work on Twython thus far!
Can you pls drop twitter_endpoints.py into twython3k? It's out-of- date. .. thx! :-)

调用Tweepy 库的 search()会获得一个推文列表。代码遍历列表中的推文并显示其中感兴趣的部分。Twython 是一个类似的Python 库,提供了 Twitter API。

Twython 与Tweepy 类似,但也有自己的特点。Twython 同时支持 Python 2 和 3,但输出的结果不用对象,而是用纯 Python 字典来持有结果数据。将前面的 tweepy-example.py 与这里的 twython-example.py 脚本进行比较,后者兼容 Python 2 和 3。

# twython-example.py
from distutils.log import warn as printf
try:
    import twython
except ImportError:
    import twython3k as twython

TMPL = '''\
    User: @%(from_user)s 
    Date: %(created_at)s 
    Tweet: %(text)s
'''

    twitter = twython.Twython()
    data = twitter.searchTwitter(q='twython3k')
    for tweet in data['results']: 
        printf(TMPL % tweet)

distutils.log.warn()函数是Python 2 中 print 语句和 Python 3 中 print 函数的代理。同时读者还可以尝试导入Python 2 和 3 中的Twython 库,希望至少有一个会成功。

Twython.searchTwitter()的输出结果是一个字典,该对象在字典的“results”键中,每个对象都是一个字典列表,表示一条推文。因为结果是个字典,这样就可以简化显示,还可以更方便地调用(付出的代价是需要使用字符串模板来展开字典中含有的键值)。

这里还有其他改动,如不使用纯单行输出,将所有字符串放到一个大的字符串模板中, 然后将输出的字典传递给字符串模板。这样做的原因是在实际应用中经常会使用某种形式的模板(无论是字符串模板还是 Web 模板)。

这里输出的结果与Tweepy 版本相同,所以这里就不重复了。

13.3.3 稍微长一点的 API 组合应用示例

这些简单短小的示例可以快速地让读者上手。但在现实当中会遇到不同的场景,因此可能需要使用或集成多种类似的 API。下面通过一个稍微长一点的示例来练习。这里将编写一个兼容库,通过同时使用 Tweepy 和 Twython 来支持一些基本的 Twitter 命令。这个练习会帮助读者学习这两个库,并更加熟悉Twitter 的 API。

验证

在继续这个练习之前,读者需要一个 Twitter 账号。如果没有,访问 http:// twitter.com 并注册一个。一般需要用户名和密码进行验证(更现代的方式包括生物方式的验证,如指纹或视网膜扫描)。这些凭证仅用于身份验证,数据访问则是另外一回事。

授权

验证并不意味着可以访问数据(任何人的数据都不行),还需要正确的授权。在通过Twitter或第三方授权后才能访问自己或其他人的数据,如允许外部应用下载  Twitter  消息或通过Twitter 账号发布状态来更新 Twitter 账号。

为了通过 Twitter 获取授权凭证,需要创建一个应用。可以在 http://dev.twitter.com 中完成这个任务。至少拥有一个应用,然后再单击需要授权的应用。URL 类似于 https://dev.twitter.com/apps/ APP-ID/show,这里可以看到访问Twitter 数据所需的OAuth 设置,它包括4 个重要的部分:consumer key、consumer secret、access token 和access token secret,它们都用来访问 Twitter 上的数据。

获取这 4 块有价值的数据后,将其放置在一个安全的地方,也就是说,不能放在源码中! 在这个例子中,将这些数据另存到一个名为 tweet_auth.py 模块的 4 个全局变量里。在最终的应用中会导入这个模块。实际应用中,要么发布编译过的字节码.pyc(不是纯文本),要么通过数据库或网络上的其他位置访问这些数据,最好是加密过的。现在所有内容都设置完毕, 在描述代码前先介绍应用本身。

一个混合的 Twitter API 应用

这个应用执行 4 个操作:首先它输出在 Twitter 上搜索的结果;接着,它获取并输出当前应用更详细的信息;然后,它获取并输出当前用户发布消息的时间线;最后,它为当前用户发布一条推文。这 4 个操作都执行两次:一次使用 Tweepy 库,另一次使用 Twython 库。为了完成这个任务,需要支持 4 个 Twitter API 命令,如表 13-2 所示。

表 13-2 混合 Twitter API 应用的 4 个命令

命 令描 述
search采用最近匹配优先的方法在 Twitter 上进行搜索。这是未经验证的调用(应用中只有这一个),意味着任何人都能调用
verify_credentials判断已验证用户的身份是否合法
user_timeline获取已验证用户最新的推文
update_status更新已验证用户的状态,也就是说发布一条新的推文

这款应用同时使用了 Twython 和Tweepy。最后,代码会在 Python 2 和Python 3 中运行。也就是说,代码中有这 4 个命令,并实例化两个库,接着含有支持每个命令的代码。准备好了吗?查看示例 13-2 中的 twapi.py。

示例 13-2 Twitter API 组合库示例(twapi.py)

这里演示使用 Twython 和 Tweepy 库与 Twitter 交互。

在介绍这个脚本之前,先运行并查看其输出。要确保首先创建一个tweet_auth.py 文件, 其中含有以下这些变量(以及Twitter 应用中正确的对应值)。

# tweet_auth.py
consumer_key = 'SOME_CONSUMER_KEY' 
consumer_secret = 'SOME_CONSUMER_SECRET' 
access_token = 'SOME_ACCESS_TOKEN' 
access_token_secret = 'SOME_ACCESS_TOKEN_SECRET'

现在可以开始了。当然,这是在编写本书时执行程序得到的输出结果,读者得到的肯定会与此不同。下面是执行时的状态(“…”表示省略了一些输出内容以缩短篇幅)。

从中可以看到运行了 4 个函数,这 4 个函数执行时分别使用了 Tweepy 库和 Twython 库。如果安装没有问题,在 Windows 上执行脚本时会获得相同的结果。因为代码也兼容 Python 3, 所以在 Python 3 中也得到类似的输出,但只能看到 Twython 的输出,因为Tweepy 在本书编写时还不支持Python 3。现在来进一步了解代码。

逐行解释

第 1~5 行
这里含有一些导入语句,包括导入标准库(使用 distutils.log.warn()作为 print 语句或函数的代理,具体取决于是 Python 2 还是Python 3,还有一些在 Python 中运行单元测试的基本属性),以及 Twitter 授权凭证。

还要提醒的是,在一般情况下,不鼓励使用“from module import *”(第   5 行),因为标准库和第三方库中可能含有与该模块相同的变量名称,这样会有潜在的问题。在这里,完全了解 tweet_auth.py 并知道其中所有(4 个)变量。该模块的唯一目的是隐藏用户的凭证信息。在实际生产环境中,这样一个文件要么作为编译过的字节码(.pyc)或优化过的文件(.pyo),要么来自数据库或网络调用,需要说明的是,.pyc 和.pyo 两者都是人类不可读的。

第 7~38 行
第一块实际代码只完成了一件事,即让 Python 解释器了解可以使用哪个 Twitter 客户端库,就这样。

CMD 是一个字典,其中还有两个字典项,分别是 Twython(twython)和 Tweepy(tweepy)。在本章末尾的练习中,会添加第三个库。

对于每个库,提供方法名表示前 4 个方法对应的 Twitter API 命令。如果该值是 None, 这意味着方法名完全匹配。如果使用其他值(即不是 None),即表示方法名与 API 命令不一致。

Tweepy 较为简单,其方法名与命令完全匹配。因此,使用 dict.fromkeys()创建字典时, 所有键的值都是 None。Twython 就有点麻烦,因为其使用驼峰命名法,且有与规则不一致的地方。第 71~80 行描述了为什么使用这些名称和方法。

在第 22 行,将所有支持的 API 放到集合中。在 Python 中,最快速检测是否包含的方式是使用集合数据结构,同时遍历这个变量中的所有 API。到目前为止,仅创建了可能的 API, 现在需要看实际当中可能使用哪些,把不能用的去除。

第 25~33 行的代码尝试导入各个库,将无法导入的 API 移动到另一个含有不存在的 API 的 remove 集合中。循环结束后,就知道缺少哪些 API,把这些缺失的 API 从全体 API 中删除(第 35 行)。如果两个库都不可用,则在第 36~38 行抛出 NotImplementedError 异常。

第 40~71 行
Twitter 类是该应用的头等对象。该类定义了初始化函数,获取 api(在这里,要么是twython,要么是  tweepy),以及可选的  auth 标记。该标记默认为  True,因为大部分时候需要验证和授权才能访问用户数据(Search 是唯一不需要验证的函数),然后将选定的 API 缓存到 self.api 中。

这一节剩余的部分用来(第 48~71 行)实例化 Twitter,并将该实例赋值给 self.twitter。该对象是执行Twitter API 命令的处理程序。

第 73~81 行
_get_meth()方法处理特殊情况,将每个 API 的调用与正确的方法名称整合到一起。注意, 该方法前面有单条下划线。这种标记法表示该方法不应该由用户调用,而它是一个内部方法, 由该类中的其他方法调用。

可以在该方法中直接使用 self.api,但众所周知,最佳实践是将经常使用的实例属性赋值给局部变量。使用 self.api 需要两次查询,而“api”只需一次,从 CPU 时间上来看,另一次查询并不耗时很多,但如果在类型循环中或经常执行它,开销就会增大。这就是为什么在第74 行赋值给局部变量。

下一行中,查询来自请求 API 里相应的命令,并赋值给 meth_name。如果它是 None,则默认行为是命令与方法名称相同。对于 Tweepy,很简单,前面提到过,其方法名与命令名完全相同。接下来的几行用于处理特殊情况,来获得正确的名称。

前面提到过,Twython 使用驼峰命名法,而不是通过下划线分割单词。这意味着必须先根据下划线将每个单词分开,接着将其追加到第一个单词后面(第 79~80 行)。最后一个行为是使用这个名字从请,求的 API 中获取方法对象,这是个头等对象,把它直接返回给调用者。

第 83~102 行
支持的 4 个 Twitter 命令由 4 个函数实现:search()、verify_credentials()、user_timeline()、update_status()。除了 search()之外,其他三个很简单,两个库的使用方式几乎相同。首先看后面三个,最后再深入了解 search()。

验证已经通过身份验证的用户信息仅仅是 verify_credentials 命令能做的其中一件事。该命令同时能用编程方式最快地访问最新的推文。用户信息会打包成 ResultsWrapper(后面会进一步介绍),接着返回给调用者。关于使用这条命令的更多信息可以参考  Twitter  文档(http://dev.twitter.com/docs/api/1/get/account/verify_credentials)。

用户的时间线由最近发布的推文和转推组成。user_timeline 这个Twitter 命令默认情况下返回最近的 20 条,而使用 count 参数可以最多请求 200 条,不过这里没有用到,但本章末尾的一个练习中会用到。关于该函数的更多信息可以参考 http://dev. twitter.com/docs/api/1/get/statuses/user_timeline。与 verify_credentials()不同,user_timeline 封装每条单独的推文,而不是返回来自 Twitter 的整个结果,返回一个生成器表达式,迭代返回推文。

Twitter 最基本的功能是用户可以更新自己的状态,也就是,发布推文。如果没有这个功能,就不能称之为 Twitter 了。从代码中可以看出,update_status 接受额外的参数 s,这是推文的文本。返回值是推文本身(同样被 ResultsWrapper 封装),通过最重要的特性(created_at 字段),表示推文已经创建并已发布。下面代码中的 created_at 演示了这个功能。关于该函数的更多用法,参考 http://dev.twitter.com/docs/api/1/post/statuses/update。

现在返回 search。Twython 和 Tweepy 两个库对这个 API 调用方式有所区别,所以代码要比普通情形多。Twython 试图原原本本解释Twitter 返回的结果,将 JSON 装成 Python 字典。这种数据结构含有多个元数据,在“results”键下还会发现一些有用的信息(需要在第 86 行进行查找)。

而 Tweepy 更加现实,直接以列表形式对象返回搜索结果,实际上,ResultsSet 是列表的子类,因为开发者知道用户实际上想要什么。这样更加方便,节省了将来查找的时间。那么额外的元数据呢?那些只是返回的ResultsSet 的属性而已。

第 104~124 行
这一块代码是通用类,可以在任何地方使用,包括在这个应用之外。该类与 Twitter 库没有关系,仅仅用来方便用户,为这些库返回的对象提供通用接口。

读者是否因为不一致的对象类型而感到挫败过,比如字典类型或对象类型?我的意思是对于字典对象,需要通过  getitem   ()调用获取值,即  foo[‘bar’],而对于对象,需要处理对象的属性接口,即 foo.bar。能否做到不管是什么对象,可以同时使用这两个方式?这就是ResultsWrapper 类的工作。

在编写本书时,我刚刚接触这些,所以可能不够完善,但其思想是将任何 Python 对象封装到一个对象中,将查询(通过   getitem   ()或   getattr    ())委托给封装对象(对于委托的内容,可以阅读 Core Python Programming 或者 Core Python Language Fundamentals 的Object-Oriented Programming)。

在初始化函数中(第 106~107 行)封装对象。然后使用    str    以字符串的形式表示对象(第  109~113  行)。大多数变化发生在__getattr
__ ()中。当请求一个没有识别的属性时,
__ getattr
__ ()检查在封装对象中是否是存在该属性(第 116~117 行)。如果没有,也许在字典对象中,所以检查它是否是一个“键”(第 118~119 行)。当然,在使用 in 操作符之前,需要检查该对象是否支持这种类型的检查或访问,即首先检查对象是否含有  contains   属性。如果所有 else 都失败,则告知用户处理失败(第 120~122 行)。

最后一行(第 124 行)用来应对用户试图用字典的方式访问属性,即将属性名称作为键来使用。我们希望   getitem   有与   getattr    ()完全相同的行为。因此,无论封装的对象类型是什么,都能获得并返回用户需要的内容。

第 126~165 行
_demo_*() 函数的名副其实: _demo_search() 演示了使用所有可用 API 搜索条目“twython3k”,并显示搜索推文的结果数据。_demo_ver_creds()执行 verify_credentials 命令,并显示已验证用户最近的推文;_demo_user_timeline()获取最新的 20 条推文,显示每条推文的内容和时间戳。最后,_demo_update_status()发布新的推文,新推文介绍了所用到的 API。

第 167~184 行
这一部分代码用于测试ResultsWrapper 类。_unit_*_wrap()函数测试每个封装的字典(或类似字典的对象),以及含有属性接口的对象。这两个都通过属性访问,无论是通过 obj[‘foo’], 还是通过 obj.foo 都会返回相同的结果“bar”。最后通过 TestSequenceFunctions 测试类完成这种验证(第 179~184 行)。

第 186~196 行
main()函数显示正在测试哪个函数,并调用特定的_demo_*()函数显示其输出。最后一个调用针对 unittest.main()函数,用于执行单元测试。

13.3.4 总结

通过这一节的内容,希望读者扎实地掌握了一些 Web 服务的接口,如 Yahoo!的股票报价服务器,以及 Twitter。更重要的是,认识到 Yahoo 的接口完全是由 URL 驱动的,无须授权。而 Twitter 提供了完全的REST API 和用于访问安全数据的 OAuth 授权。我们能够使用 Python 代码发挥这些强大的功能来完成工作。

这里只介绍了两个 Web 服务,网络上还有许多其他的服务。下一章还会回顾这两个服务。

13.3.5 额外在线资源

Yahoo! Finance

Twitter

13.4 练习

Web 服务

  • Web 服务。使用自己的语言描述什么是 Web 服务。在网上找到这样一些服务,并描述其工作方式。包括服务的 API 以及如何访问这些数据。是否需要认证或授权?
  • REST 和 Web 服务。学习 REST 和XML 或JSON 是如何应用在现代 Web 服务 API 和应用中的。与 Yahoo!报价服务器(它使用 URL 参数)这样的老系统相比,这三者提供了哪些额外功能?
  • REST 和 Web 服务。使用 Python 中对 REST 和 XML 的支持构建一个应用框架, 该框架允许共享并重用一些代码。这些代码包括使用如今新的 Web 服务和 API。展示使用 Yahoo!、Google、eBay、Amazon API 的代码。

练习 13-4~13-11 涉及本章前面介绍的 Yahoo!股票报价示例(stock.py)。

  • Web 服务。更新 stock.py 中下载股票报价数据的内容,添加表 13-1 中列出的额外参数。可以直接在本章前面的stock.py 基础上添加新功能。
  • 字符串处理。读者会注意到有些返回的字段含有引号,移除这些引号。读者能想到几种移除引号的方法?
  • 字符串处理。并不是所有股票代号长度都是 4 个字符。同样,并不是所有股价都在 10~99.99 美元之间。每日涨跌幅和百分比也是如此。对脚本进行适当的修改, 让其可以应对不同长度的结果。对于所有股票输出结果依然需要是格式化的、对齐的和一致的。下面是一个示例。
  • 文件。更新应用,将股票数据保存到文件中,而不是显示在屏幕上。附加题:修改脚本,让用户选择将股票信息显示出来还是保存到文件中。
  • Web 服务和 csv 模块。原来 的 stock.py 文件使用普通的 for 循环,并手动解析数据。将其转成使用 csv 模块解析输入数据,类似在示例代码段中做的那样。
  • 健壮性。Yahoo!倾向于不断修改下载的主机名。今天可能是 quote.yahoo.com,明天可能会变成 finace.yahoo.com。本书示例运行时的链接是“download.finance.yahoo.com”。有时主机名又会变成老的。维护一个主机列表,ping 这些主机上的 Web 服务器, 在获取股价之前先查看服务器是否可用,以此来构建一个健壮的应用。可以定期访问 Yahoo!股票报价页面,从页面底部 Toolbox 部分的 Download Data 链接中抓取主机名。
  • 扩展 API。Yahoo!报价服务器还有许多其他命令。完整的列表可以访问 http://gummy-stuff.org/Yahoo-data.htm 。选择若干新的数据点,并将其集成到stock.py 脚本中。
  • Python 3。将 stock.py 移植到Python 3 中,重命名为 stock3.py。附加题:通过某种方式让脚本同时运行在 Python 2.x 和 3.x 中,并描述用到的方法。
  • 外汇。Yahoo!报价服务器还可以查询货币汇率。查看http:// gummy-stuff.org/forex.htm, 并创建一个新的 forex.py 脚本,查询汇率。
  • 股票图。Yahoo!还提供了自动生成图的方式。下面是一些示例 URL,可以用于了解这个服务。

小图:

1 天: http://chart.yahoo.com/t?s=GOOG

5 天: http://chart.yahoo.com/v?s=GOOG

1 年: http://chart.yahoo.com/c/bb/m/GOOG

大图:

1 天://chart.yahoo.com/b?s=GOOG

5 天: http://chart.yahoo.com/w?s=GOOG

3 个月: http://chart.yahoo.com/c/3m/GOOG

6 个月: http://chart.yahoo.com/c/6m/GOOG

1 年: http://chart.yahoo.com/c/1y/GOOG

两年: http://chart.yahoo.com/c/2y/GOOG

5 年: http://chart.yahoo.com/c/5y/GOOG

最大时间:http://chart.yahoo.com/c/my/GOOG

与练习 13-9 的健壮性类似,域名会在 chart.yahoo.com、ichart.yahoo.com 和ichar.finance. yahoo.com 之间轮换,所以要使用所有这些来检查数据。创建一个应用, 允许用户生成股票投资组合图。同时提供在浏览器中访问的功能,直接显示股票图页面。提示:webbrowser 模块会有帮助。

  • 历史数据。ichart.financial.yahoo.com 还提供历史价格查询。使用下面这个示例的 URL 了解其工作方式,并创建一个应用,用于查询股票历史价格:http://chart.yahoo. com/table. csv?s=GOOG&a =06&b=12&c=2006&d=10&e=2&f=2007。

Twitter

  • Twitter 服务。用自己的语言描述 Twitter 服务。介绍什么是推文,并指出推文的一些限制。
  • Twitter 库。描述 Twython 和 Tweepy 这两个Python 库的异同点。
  • Twitter 库。了解其他可以访问 Twitter API 的Python 库。这些库与本章用到的库有什么区别与联系?
  • Twitter 库。如果既不喜欢 Twython,也不喜欢 Tweepy Python 库。那么自己从头写一个与 Twitter 交互的安全且 RESTful 的库。可以从 https://dev.twitter.com/docs 开始。

下面的练习需要改进本章的twapi.py 示例。

  • 用户查询。添加新功能来查询用户在Twitter 界面上的名称。并返回对应的 ID。注意,有些用户的界面名称就是整数,所以要确保允许用户输入这些数字作为潜在的界面名称,使用 ID 获取用户最新的推文。
  • 发布推文。改进搜索功能,不仅让用户搜索推文,还可以让其转发选择的推文。可以提供命令行、Web 或 GUI 来支持这个功能。
  • 删除推文。与练习 13-20 类似,让用户可以删除自己发布的推文。注意,这只是从 Twitter 删除推文,而推文的内容可能已经扩散到其他地方了。
  • 关注。添加查看用户关注者(粉丝)ID 以及被关注者 ID 的功能。
  • Twitter 库。向 twapi.py 添加对不同 Python/Twitter 客户端库的支持。例如,可以尝试支持 python-twitter,该库参见 http://code.google.com/p/python-twitter,其他库可以在 http://dev.twitter.com/docs/twitter-libraries#python 中找到。
  • 编辑个人资料。让用户可以更新自己的资料,以及上传新的头像。选做题:允许用户更新个人资料的颜色和背景图片。
  • 计数。user_timeline()这个 Twitter 函数还支持 count 变量。默认情况下,Twitter 返回用户时间线上最新的 20 条推文。向 twapi.py 添加对 count 和其他可选参数的支持。
  • 直接消息。支持直接消息(Direct Message),将这些消息发送给指定用户,获取当前已发送的DM 列表、已接收的 DM 列表,并可以删除 DM。

在 twapi.py 示例中,能够检测并修改 Twitter 流,因为应用拥有所有必需的授权信息。但如果需要编写一个应用来帮助用户发送推文就是另外一回事了。在这种情况下,为了能获得 access token 和 secret token,需要支持 OAuth 的完整流程。

最后几个练习需要花点时间,因为必须学习 OAuth 的内容。可以先阅读这两个文档:

https://dev.twitter.com/docs/auth/oauth 和 https://dev.twitter.com/docs/auth/moving-from-basic- auth-to-oauth。

  • 推文归档。创建一个 Twitter 归档服务。由于 Twitter 只保存最近的 200 条推文, 因此很快就会丢失以前的推文。构建一个 Twitter 归档服务,保存已注册用户的推文。如果在网上搜索“twitter archive”或“twitter research tools”,会得到很多内容。希望通过这个练习,能够在读者中诞生下一代 Twitter 分析工具!
  • 短链接、Feed 轮询。为个人或工作博客创建周期扫描器(RSS 或其他),当发布新博客时,自动发布一条短链接以及该博客标题的前 个单词。
  • 其他 Web 服务。阅读关于 Google 的 Prediction API(http://code.google.com/apis/predict),尝试学习其中的“Hello World”教程。上手以后,开发一个自己的模型,来扫描不同的推文(自己或别人的都可以)。创建并训练预测模型,判断一条推文是积极的、消极的,还是中性的。当训练完成后,使用工具以相同的方式判断新的推文。为了完成这个练习, 需要在  Google  的  API  控制台(http://code.google.com/apis/console)上创建一个项目,启用 Google Prediction 和Google Storage。如果不想创建 Google 账号,也可以使用其他类似的 API。

发表评论

邮箱地址不会被公开。 必填项已用*标注