预览模式: 普通 | 列表

通过编程访问Google账户和服务

本文介绍通过桌面程序访问Google账户和Google服务的方法。以Google Calendar服务为例,使用Google Calendar API 2.0版。

1、Google账户登录
通过程序登录Google账户的方法有很多,最简单的方法是使用ClientLogin这个API,在桌面程序中一般可以使用这种方式。为了测试,可以直接在浏览器中调用这个API来看结果,URL类似这样:

https://www.google.com/accounts/ClientLogin?accountType=HOSTED_OR_GOOGLE&Email=YourAccount@gmail.com&Passwd=PASSWORD&service=xapi&source=YourCompany-YourApp-Version

其中Email=YourAccount@gmail.com是你的Google账户Email地址,Passwd=PASSWORD是账户的密码。service是要访问的服务名,对于Google Calendar服务,要传递cl。source是登录来源,也就是你的程序的名称之类。注意上面的关键字是区分大小写的,比如“Email=”中的“E”一定要大写。

以REALbasic语言为例,来看看怎么用程序来处理。在这里我们使用同步模式的HTTPSecureSocket来发送请求。
Dim https As New HTTPSecureSocket
https.Yield=True // 同意让渡CPU时间,以防在发送请求过程中界面长时间停止响应
Const PostURL="https://www.google.com/accounts/ClientLogin"
Dim form As New Dictionary // 使用字典组装表单,这比用“+”运算符连接字符串来组装URL要方便
form.Value("accountType")="HOSTED_OR_GOOGLE"
form.Value("Email")=“YourAccount@gmail.com"
form.Value("Passwd")="PASSWORD"
form.Value("service")="cl" // 指定访问Google Calendar服务
form.Value("source")="REALSoftware-REALStudio-"+RBVersionString
https.SetFormData(form) // 将表单指派给https,会对特殊字符编码,比如空格会被转为“%20”。
Dim ResultValue As String
ResultValue=https.Post(PostURL,20) // POST表单到指定地址,20秒无返回则自动超时

如果成功(https.HTTPStatusCode=200),ResultValue中将得到类似这样的3行返回结果(失败的话https.HTTPStatusCode=403并会返回错误信息):
SID=DQAAAH4AAADifYYKy7p9Lg7KdT4BW3zb7RvFmKJm7iJgQ2hjpTG6CLhVnQPFybD8t_odSpU5ZgODxcXnGi50cc-Ky8u-O0KaQiCEydE9RurCkoLEywY7a6trnN6mQW5SmcgjiNCwqiIzhAvQcu-0RFrvKQNJX78GQE4fFE_fs4uX2GTYleQGQg
LSID=DQAAAIAAAABWZhQlKvg2elXVWVttR23V2AWMu57XXvOiJJbGrHlUZW-96nsaokBwCzxs8_edWSDZOU3lU71R14u8QntrODC7WAFlBMkG04GZzIND7WPqX73t--dUAkYVlDoyfwP4N7bTNrg5VsOeeYervrxs_ADg8MBpqlnw-bbjIcyIub8org
Auth=DQAAAIAAAABWZhQlKvg2elXVWVttR23V2AWMu57XXvOiJJbGrHlUZW-96nsaokBwCzxs8_edWSC5iZ0YUERmdfIogOo05uk-YFGm0na7lYS_yT-_BNOLSBZeDcwtOOp9osxbv_7oUdGHbfhOoDza6G-ISUZM4dYgkDNmWyOqjTUwwbWAbbjU4w

这里我们只需要使用最后一行“Auth=”后面的内容(称为验证令牌)。将它解析出来保存到字符串变量AuthToken中(解析过程略),然后要设置一下后续的Google账户访问时需要发送的HTTP报头:
https.SetRequestHeader("Authorization","GoogleLogin auth="+AuthToken) // 将验证令牌放入Authorization请求头中
https.SetRequestHeader("GData-Version","2") // 如本文开头所说,我们要使用2.0版的API,所以设置GData-Version头

2、调用Google Calendar API
下面这个API用于获取已登录的用户的所有日历:
https://www.google.com/calendar/feeds/default/allcalendars/full
要使用这个API,只要继续用上面那个设置好验证令牌的HTTPSecureSocket实例向这个地址发送GET命令即可。
Const AllCalendarURL="https://www.google.com/calendar/feeds/default/allcalendars/full"
dim v As String=https.Get(AllCalendarURL,20) // 这次用GET(不要用POST),超时仍然选20秒

但得到的返回值v中的多半不是XML格式的日历列表,而是像这样一个HTML页面:
- <HTML>
- <HEAD>
- <TITLE>Moved Temporarily</TITLE>
- </HEAD>
- <BODY BGCOLOR="#FFFFFF" TEXT="#000000">
- <H1>Moved Temporarily</H1>
The document has moved <A HREF="https://www.google.com/calendar/feeds/default/allcalendars/full?gsessionid=gC7NAlsQASjnE_wXPF1kxQ">here</A>.
- </BODY>
- </HTML>

此时,如果检查https的HTTPStatusCode属性,会发现它的值是302,而不是200(OK,正常)。 302在HTTP协议里是Redirect(重定向)的意思,重定向到了上面那个HTML中显示的地址。我们不需要从HTML里解析它,一则这样太麻烦,二则Google可以自己决定对于302这个状态应该返回什么样的正文文本,如果Google做了些改动,我们解析方法可能就失效了。那怎么办呢?应该想到HTTP协议返回的内容不光只有页面正文,还有页面报头。我的经验是,进行通过HTTP访问某个网站开放的API一类的开发,经常检查一下返回的页面的报头是个好习惯。可以通过HTTPSecureSocket的PageHeaders方法,或在调试器中看_pageHeaders属性来查看报头,对于上面的GET操作,我们会看到这样一组报头:
Expires: Sat, 31 Jul 2010 12:43:48 GMT
Date: Sat, 31 Jul 2010 12:43:48 GMT
Set-Cookie: S=calendar=gC7NAlsQASjnE_wXPF1kxQ;Expires=Sun, 31-Jul-2011 12:43:48 GMT;Secure
Location: https://www.google.com/calendar/feeds/default/allcalendars/full?gsessionid=gC7NAlsQASjnE_wXPF1kxQ
Content-Type: text/html; charset=UTF-8
Cache-Control: private, max-age=0
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Server: GSE

可以看到Location头的值正是跳转目标的URL,和HTML中显示的URL一致。
那么我们也进行“重定向”,向新地址发送GET命令:
Dim headers As InternetHeaders=https.PageHeaders // 读出所有InternetHeaders
Dim Result As String
If headers<>Nil Then
  Dim RedirectTo As String=headers.Value("Location") // 读取Location头,以得到重定向地址
  If RedirectTo<>"" Then Result=https.Get(RedirectTo,20) // 继续GET,向新地址请求数据
End If

但是结果很可惜,我们得到了又一个类似上面那样的HTML和一个新的302,如果继续下去你会得到永无止境的大体相同的HTML和302状态。为什么会这样呢?比较一下各次得到的正文内容或Location头,你会发现差异在URL里的gsessionid=xxxxxxxx一段。这段内容给出了会话ID(或者叫会话种子,Seed),这意味着我们必须使用这个会话才能继续执行后续的API。

要注意,HTTP协议是一种无连接协议,客户端发送请求时建立连接,服务器响应请求返回了数据后,连接立即断开。对于Google API,为了避免每执行一个API函数就进行一次登陆验证,服务器端会以会话ID为索引保存一个上下文。相应地,在客户端向服务器发送请求时也必须同时发送这个会话ID,以使服务器知道它所保存的哪个上下文和这个客户端对应。因此客户端也要保存这个会话ID和一些信息,以便在发送请求时使用,客户端所保存的东西被称为Cookie。

在REALbasic语言的HTTPSecureSocket类中怎么使用这些“曲奇饼干”呢?回过头来再看上面的那组报头,发现有一个名为Set-Cookie报头,可以看到其中也包含gsessionid的内容。这就是我们要保存并在后续访问中使用的Cookie了。所谓的“在后续访问中使用”,意思就是在后续的请求中作为报头发送给服务器。因此可以不必使用额外的变量,只要像上面保存验证令牌和GData版本号那样将Cookie缓存到https对象的RequestHeader里面就行了。现在修改上面的代码如下:
Dim headers As InternetHeaders=https.PageHeaders // 读出所有InternetHeaders
Dim Result As String
If headers<>Nil Then
  Dim Cookie As String=headers.Value("Set-Cookie") // 从Set-Cookie头读出Google要求我们使用的Cookie
  Dim RedirectTo As String=headers.Value("Location") // 从Location读出重定向地址
  If RedirectTo<>"" Then
    If Cookie<>"" Then https.SetRequestHeader("Cookie",Cookie) // 将Cookie写入请求报头中
    Result=https.Get(RedirectTo,20) // 向新地址请求数据
  End If
End If

这回Result中将收到含有日历列表的一个XML格式的字符串(实际上是Google Data格式的,也可以是RSS或Atom格式)。接下来只要用XMLDocument和相关的类进行解析,就能获取我们想要的数据了。


(转载请注明来源:http://www.3exware.com/home/default.asp?id=77

查看更多...

Tags: REALbasic Google

分类:编程开发 | 固定链接 | 评论: 0 | 引用: -11 | 查看次数: 9894
估计是由于独生子女和城市小家庭的原因,绝大多数80后似乎都下意识地认为自己的堂兄妹与表兄妹的数量差不多。并且觉得如果两者数量差距较大的话,那么原因在于祖父母的子女和外祖父母的子女数量本身差别很大。但实际上并非如此。总体而言,一个人的表兄弟姐妹肯定比堂兄弟姐妹在数量要多上许多。以三代以内旁亲为限。可以看到,堂兄弟姐妹都是你祖父或其兄弟的孙子、孙女(与你同姓)。
但同辈的表亲则要复杂多了,劳动了一下脑细胞,发现可以分为以下十一类:
1、外祖父及其兄弟的孙子、孙女(舅表,与母亲同姓)
2、外祖父的姐妹的孙子、孙女(舅表,姓氏不定)
3、外祖父及其兄弟姐妹的外孙、外孙女(舅表,姓氏不定,自己不算哦)
4、外祖母的兄弟的孙子、孙女(舅表,与外祖母同姓)
5、外祖母的姐妹的孙子、孙女(舅表,姓氏不定)
6、外祖母的兄弟姐妹的外孙、外孙女(舅表,姓氏不定)
7、祖父的姐妹的孙子、孙女(姑表,姓氏不定)
8、祖母的兄弟的孙子、孙女(姑表,与祖母同姓)
9、祖母的姐妹的孙子、孙女(姑表,姓氏不定)
10、祖父及其兄弟姐妹的外孙、外孙女,其中包括父亲的姐妹(姑姑)所生的儿女(姑表,姓氏不定)
11、祖母及其兄弟姐妹的外孙、外孙女(姑表,姓氏不定)
总之,我把仨爷爷家的所有同辈堂亲加载一起也不会超过10个,但表兄弟姐妹却多得……十有八九能编成一个团。

查看更多...

分类:思考感悟 | 固定链接 | 评论: 0 | 引用: -11 | 查看次数: 2998

艳遇

入夏以来,由于门窗敞开,我的八平米的斗室已经被许多六条腿的小精灵们造访过了。

其中少不了蚊子若干,苍蝇一对。对这一类的不速之客,一经发现,我一向是不顾念什么生态平衡或生物伦理直接送他们往生极乐的。这也免不了大义灭亲地扑杀了几只跟我血脉相连体内流着我的血的小蚊──我可不指望他们给我养老送终哦。

在目送了一只误入的蜜蜂乖乖飞去,以及撵走了一羽对鄙人的陋室颇为留恋的蛾子之后。前天晚上又被一花里胡哨的小瓢虫光顾了。问题是咱没有饲养这种小不点儿的经验,思来想去,最后还是隔着一小片餐巾纸轻轻捏住它,将它送回了苍茫的夜空。

然后我决定把窗关小,傍晚开灯后不光拉好窗帘,还将两头压住。本以为这下可以大幅减少这类没有物权观念的小东西对私人领地的随意入侵了,哪知道昨天晚上竟然出现了一头牛蹲坐在我的凉席上。乍看上去还以为是只小强,随手抄起本书欲拍,才发现这个同样伸着两只长长的触角的家伙,身体黝黑而狭长,慢条斯理懒洋洋的也不像小强那么机谨,原来是只墨天牛。要是早上十几年,它估计就被我圈养起来了,慢慢玩弄了。要是早上二十来年,它肯定会被我好好的欺负一下,搞不好还会惨死于吾之魔爪。不过现在我对这些飞禽走兽们却心怀厌恶,略感恶心──尤其是当她们一登门造访就急于跟你同床共枕的时候。无奈之下,我只好作了一回放牛娃,将这头天上来的仙牛赶回它的天然牧场。在它不甚情愿的被驱离的一霎那,我下意识地忽然想,竟然没请人家吃上一顿便饭就将人送走了,好像不是待客之道哩……

法布尔老爷子泉下有知一定会羡慕我的种种艳遇,不过我看来是没耐性消受这种艳福的。唉,我是不是该想办法给这两扇该死的窗户装上个纱窗啊,否则难保哪天家里就变成第二座荒石园了!

分类:生活旅行 | 固定链接 | 评论: 0 | 引用: -3 | 查看次数: 2456

[断章碎语]男为悦己者穷

最近听到句怪话叫“士为知己者死,男为悦己者穷”,那遇到异性知己非得死穷死穷的不可。不过,《孙子兵法·九地》篇中有云:投之亡地而后存,陷之死地然后生。《易经·系辞》中又讲:穷则变,变则通,通则久。死而后生,穷以致久,虽人生如是,岂为恶事哉?!
分类:思考感悟 | 固定链接 | 评论: 0 | 引用: -3 | 查看次数: 2740

分班制的背后是对“差生”的歧视

观点概述:高级中学通常会以成绩对学生进行分班,并将教学水平最高的教师集中于最好的几个班级中,这是一种有悖公平的歧视性做法,是现行招考体制所带来的一种副作用。

中学,尤其是高级中学里,按成绩分快慢班的状况非常普遍。每个年级通常会有一到两个快班,又称好班、重点班或直升班,剩下的均为普通班,其中重点班的人数一般占年级学生人数的20%左右。表面上看这是一种惯例,但实际上是一种不合理的歧视性做法。

分班制为成绩好的学生提供了更好的学习环境和一定的竞争压力,但它的的目的并不仅在与此,而主要在于将更好的教师资源提供给重点班,让普通班那些在成绩上缺乏优势的学生配着教学水平不够突出的老师去一起自生自灭。这是一种性质恶劣的公开化的歧视行为,完全有悖于公平原则。从社会学角度看,这是一种人身不公——对每个人给与不同的待遇。从经济学角度看,这又是一种市场不公——对支付相同费用的人给与不同质量的服务。

前一阵子发生了这样一件事,上海某实验性示范性高中一个高三重点班的学生和家长们,竟然为了某一个学科的老师的教学水平相比同样教这个班的其他学科的老师来有所不足,而与校方争执不休。那位老师已经连续带过几届高三普通班,并未有学生反映其教学能力或方法有什么严重问题,只是由于同学科组的一位资深的老教师退休,他才开始改带重点班。随高考临近,争吵逐渐升级,家长们竟然扬言要考后集体上访控告学校。难道“好班必须要配最好的老师”被写入什么法律了?最终,校方让步,取消了那名教师本学期剩余的教学工作,令其回家临时“待业”,并更换了另一位老师来负责这个班级考前最后几周的复习课程。由此可见,大家对这种歧视性的做法不但习以为常,而且还广泛认可,将其作为理所应当的行径。

那么这种歧视性做法是由于什么而产生的呢?可能有人会觉得采用这种按成绩分班并将教学资源向快班倾斜的方案,其目的在于提高升学率。我觉得这最多不过是个次要的间接原因。广义的升学率中通常包括两个比拼指标。其一是升入高一级的普通学校的学生比例,这里的“普通学校是”指专科和职业技术类以外的学校,这个比例即狭义的升学率。其二是升入高一级的重点学校的比率,即重点率,对高中而言,这个比例也被称为一本率。此外还有一个叫做一本达线率的指标,是一本率的毛值。对于本地区最优秀的几所学校,它们可能还要互相比较另一个指标──升入公认的最顶级的几所高一级学校的学生的比例。但是在许多学校中,统计入上述任意一个指标范围的学生的总数都大于甚至远大于重点班的学生人数。上海考生的一本录取率显然也远高于20%。因而这种歧视性做法与升学率或许具有一定的正相关性,但并无必然的因果联系。

不可否认还有其他一些诱导因素,但笔者认为这种现象实际上是双向选择的招生制度的一种负外部性,或者说副作用,因此其直接原因是学生所具有的择校权利。这种双向选择制度通过升学考填报志愿,免试保送或自主推荐等充分体现出来。双向选择意味着成绩优秀的学生有一定的择校权,那么许多高级中学为了升学率、名声之类的绩效必然需要为这些学生提供“优待”,以吸引他们选择本校而不是更好一些的学校。比如,对一些前来参加学校自主推荐测试的优秀学生,给予如果在中考时填报并考入该校,保证能进入重点班的承诺。而直升班这个别称也就是由于它是保送生的云集之地而得名的。一旦有学校采用这种歧视性的分班制,其他学校就受到了这种从道德上看是不正当的生源方面的竞争,而不得不去仿效。道德上的不正当,只要不上升为政策乃至法律的不正当,就可以为之,以至于大行其道,“风靡”全国,现在则习惯成自然了。这也解释了为什么上海的初级中学并没有都采取这种“差生”歧视的分班制度,即便有分班也不完全是按成绩来分的,而主要是在普通班之外设立了科技、艺术或体育方面的特色班。这是因为从1997年开始,上海的初中生便以就近入学为主,大幅降低了学生择校的余地。

在九年义务教育阶段之外,采用双向选择的招生制度是无可厚非的,其本身也经受了时间的检验,总体上是公平有效的。但正像市场经济制度具有负的外部性,或称市场失灵(比如为了经济利益而浪费资源、污染环境、偷工减料、以次充好等)一样,教育制度也同样不可避免的存在一些负外部性。依成绩分班来因材施教本来不是坏事,但是以分班结果为接口,故意将最优秀的师资力量集中在少数几个班级,这是有悖于“有教无类”的基本原则的。由于智力和家庭环境等因素,要保证教育的起点公平无法做到,但至少要尽可能地确保过程公平,而这种歧视性的教师分配方案,显然对促进过程公平不利。

要解决这一问题并不需要取消学生的择校权,而且总体看来这种择校权是利大于弊的。避免市场失灵需要依靠政府监管,应对招生制度的外部性也一样。教育主管部门必须要注意到现行招生制度带来的副作用,采用一些手段抑制学校的错误做法。这既可以使学生在选择学校时,能真正以学校的办学理念、师资水平、评价口碑和设备设施等软硬件的情况为依据,而不是以能否进入好班为一个重要的考量,又可以避免类似的甚至更严重的事件再次发生,使校园更加和谐。

查看更多...

分类:思考感悟 | 固定链接 | 评论: 0 | 引用: -3 | 查看次数: 3233