第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!股票报价服务器参数
① 字段名称的第一个字符是字母,第二个字符(如果存在)是数字。
② 有些返回值含有额外的引号,尽管这些值都是作为服务器返回的单个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个命令
这款应用同时使用了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'
现在可以开始了。当然,这是在编写本书时执行程序得到的输出结果,读者得到的肯定会与此不同。下面是执行时的状态(“…”表示省略了一些输出内容以缩短篇幅)。
$ twapi.py
* SEARCH
TWYTHON
@ryanmcgrath
Status: #twython is now version 1.4.4; should fix some utf-8 decoding issues, twython3k should be caught up, etc: http://t.co/s6fTVh0P /cc@wescpy
Posted at: Thu, 06 Oct 2011 20:25:17 +0000
@wescpy
Status: Testing posting to Twitter using Twython3k (another story of life on the bleeding edge)
Posted at: Tue, 04 Oct 2011 21:09:41 +0000
@wescpy
Status: @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!
Posted at: Tue, 04 Oct 2011 17:18:38 +0000
@wescpy
Status: I'm wondering: will future Apple products visually be designed as well & have as much impact on the market? What do you think?
Posted at: Thu Oct 06 00:02:16 +0000 2011
…
TWEEPY
Status: .@imusicmash That's great that you're enjoying corepython.com!
Note: there will be lots of cookies at #SVCC: yfrog.com/kh1azqznj
Posted at: 2011-10-07 22:37:37
…
* UPDATE STATUS
Posted at: Sat Oct 08 05:18:51 +0000 2011
Posted at: 2011-10-08 05:18:51
* RESULTS WRAPPER
..
-
Ran 2 tests in 0.000s
OK
$
从中可以看到运行了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://gummy-stuff.org/Yahoo-data.htm
http://gummy-stuff.org/forex.htm
http://dev.twitter.com/docs/twitter-libraries#python
http://github.com/ryanmcgrath/twython
13.4 练习
Web服务
13-1 Web服务。使用自己的语言描述什么是Web服务。在网上找到这样一些服务,并描述其工作方式。包括服务的API以及如何访问这些数据。是否需要认证或授权?
13-2 REST和Web服务。学习 REST和XML或JSON是如何应用在现代Web服务API和应用中的。与Yahoo!报价服务器(它使用URL参数)这样的老系统相比,这三者提供了哪些额外功能?
13-3 REST和Web服务。使用Python中对REST和XML的支持构建一个应用框架,该框架允许共享并重用一些代码。这些代码包括使用如今新的Web服务和API。展示使用Yahoo!、Google、eBay、Amazon API的代码。
练习13-4~13-11涉及本章前面介绍的Yahoo!股票报价示例(stock.py)。
13-4 Web服务。更新stock.py中下载股票报价数据的内容,添加表13-1中列出的额外参数。可以直接在本章前面的stock.py基础上添加新功能。
13-5 字符串处理。读者会注意到有些返回的字段含有引号,移除这些引号。读者能想到几种移除引号的方法?
13-6 字符串处理。并不是所有股票代号长度都是4个字符。同样,并不是所有股价都在10~99.99美元之间。每日涨跌幅和百分比也是如此。对脚本进行适当的修改,让其可以应对不同长度的结果。对于所有股票输出结果依然需要是格式化的、对齐的和一致的。下面是一个示例。
C:\py>python stock.py
Prices quoted as of: Sat Oct 29 02:38:53 2011
TICKER PRICE CHANGE %AGE
——— ——- ——— ——
YHOO 16.56 -0.07 -0.42%
GOOG 600.14 +1.47 +0.25%
T 29.74 +0.60 +2.06%
AMZN 217.32 +10.54 +5.10%
BAC 7.35 +0.30 +4.26%
BRK-B 79.96 +0.065 +0.08%
13-7 文件。更新应用,将股票数据保存到文件中,而不是显示在屏幕上。附加题:修改脚本,让用户选择将股票信息显示出来还是保存到文件中。
13-8 Web服务和csv模块。原来 的stock.py文件使用普通的for循环,并手动解析数据。将其转成使用csv模块解析输入数据,类似在示例代码段中做的那样。
13-9 健壮性。Yahoo!倾向于不断修改下载的主机名。今天可能是quote.yahoo.com,明天可能会变成finace.yahoo.com。本书示例运行时的链接是“download.finance.yahoo.com”。有时主机名又会变成老的。维护一个主机列表,ping这些主机上的Web服务器,在获取股价之前先查看服务器是否可用,以此来构建一个健壮的应用。可以定期访问Yahoo!股票报价页面,从页面底部Toolbox部分的Download Data链接中抓取主机名。
13-10 扩展 API。Yahoo!报价服务器还有许多其他命令。完整的列表可以访问 http://gummy-stuff.org/Yahoo-data.htm。选择若干新的数据点,并将其集成到stock.py脚本中。
13-11 Python 3。将stock.py移植到Python 3中,重命名为stock3.py。附加题:通过某种方式让脚本同时运行在Python 2.x和3.x中,并描述用到的方法。
13-12 外汇。Yahoo!报价服务器还可以查询货币汇率。查看http:// gummy-stuff.org/forex.htm,并创建一个新的forex.py脚本,查询汇率。
13-13 股票图。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模块会有帮助。
13-14 历史数据。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。
13-15 Twitter服务。用自己的语言描述Twitter服务。介绍什么是推文,并指出推文的一些限制。
13-16 Twitter库。描述Twython和Tweepy这两个Python库的异同点。
13-17 Twitter库。了解其他可以访问Twitter API的Python库。这些库与本章用到的库有什么区别与联系?
13-18 Twitter库。如果既不喜欢Twython,也不喜欢Tweepy Python库。那么自己从头写一个与Twitter交互的安全且RESTful的库。可以从https://dev.twitter.com/docs开始。
下面的练习需要改进本章的twapi.py示例。
13-19 用户查询。添加新功能来查询用户在Twitter界面上的名称。并返回对应的ID。注意,有些用户的界面名称就是整数,所以要确保允许用户输入这些数字作为潜在的界面名称,使用ID获取用户最新的推文。
13-20 发布推文。改进搜索功能,不仅让用户搜索推文,还可以让其转发选择的推文。可以提供命令行、Web或GUI来支持这个功能。
13-21 删除推文。与练习13-20类似,让用户可以删除自己发布的推文。注意,这只是从Twitter删除推文,而推文的内容可能已经扩散到其他地方了。
13-22 关注。添加查看用户关注者(粉丝)ID以及被关注者ID的功能。
13-23 Twitter库。向twapi.py添加对不同Python/Twitter客户端库的支持。例如,可以尝试支持 python-twitter,该库参见 http://code.google.com/p/python-twitter,其他库可以在http://dev.twitter.com/docs/twitter-libraries#python中找到。
13-24 编辑个人资料。让用户可以更新自己的资料,以及上传新的头像。选做题:允许用户更新个人资料的颜色和背景图片。
13-25 计数。user_timeline()这个 Twitter 函数还支持 count 变量。默认情况下,Twitter返回用户时间线上最新的20条推文。向twapi.py添加对count和其他可选参数的支持。
13-26 直接消息。支持直接消息(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。
13-27 推文归档。创建一个Twitter归档服务。由于Twitter只保存最近的200条推文,因此很快就会丢失以前的推文。构建一个Twitter归档服务,保存已注册用户的推文。如果在网上搜索“twitter archive”或“twitter research tools”,会得到很多内容。希望通过这个练习,能够在读者中诞生下一代Twitter分析工具!
13-28 短链接、Feed轮询。为个人或工作博客创建周期扫描器(RSS或其他),当发布新博客时,自动发布一条短链接以及该博客标题的前N个单词。
13-29 其他 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。
[1].现在还是在用这个,没有发布新的了。——译者注
[2].没有特殊格式的非二进制文件,如XML。——译者注
[3].除非到了开发阶段,否则无需Web服务器,因此可以在后面安装。Django自带了开发服务器(刚刚已经看到),可以用于创建和测试应用。
[4].Windows系统用户可以修改PATH环境变量。首先右击“我的电脑”,接着选择“属性”。在弹出的对话框中,选择“高级”标签,最后单击“环境变量”按钮。
[5].术语硬件包括物理设备(磁盘和内存)、电源设备、冷却设备、网络设备。
[6].http://media.amazonwebservices.com/AWS_Overview.pdf。
[7].现在跳槽到Dropbox了。——译者注
[8].现已完全迁移至Python 2.7。——译者注
