第 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” |
上次交易价格 | l1 | 328 |
上次交易日期 | d1 | “2/2/2000” |
上次交易时间 | t1 | “4:00pm” |
与上次收盘价比较 | c1 | +10.625 |
与上次收盘价比较百分比 | p2 | “+3.35%” |
上次收盘价 | p | 317.375 |
上次开盘价 | o | 321.484375 |
当日最高价 | h | 337 |
当日最低价 | g | 317 |
过去 52 周股价范围 | w | “110 – 500.125” |
交易量 | v | 6703300 |
市值 | j1 | 86.343B |
每股收益 | e | 0.20 |
市盈率 | r | 1586.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
- http://dev.twitter.com/docs/twitter-libraries#python
- http://github.com/ryanmcgrath/twython
- http://tweepy.github.com
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 库。描述 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 或其他),当发布新博客时,自动发布一条短链接以及该博客标题的前 N 个单词。
- 其他 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。