GAE牛刀小试:从twitter到9911同步消息

不是@ifire说起,还真不知道有9911这东东。与twitter, fanfou一样,9911是微博服务;来头不小,属于myspace旗下。我对9911/myspace一点尝新的兴趣也没有。@ifire问我能不能写一款将twitter的消息同步到9911的程序。经过思考,我答道,如果可以接受这两个条件,程序可以写:

  1. 每分钟的新消息不超过20条;
  2. 可以接受1分钟左右的延时;

fanfou的gtalk机器人可以完美地同步弹出好友消息更新。这一点我不知道是如何做到的。我自己的解决思路是这样的:

  1. 解析twitter消息,找出每条消息及其ID
    我是使用正则表达式析取所需内容的。
  2. 过滤掉不需要的消息(例如@开头的)
    依然是使用正则表达式做到。很简单,加一条(?!@)判断约定。
  3. 将符合要求的消息发布到9911上。同时将消息ID保存到数据库中,以备查询最新的消息ID
    需要说明的是,twitterAPI是使用类似于好麻烦呀自推特狐。这样的代码来保存中文的。我从网上借了一段代码,专门用来转换这种“乱码”。
    将消息ID保存在数据库中,也是很有必要的。因为,如果不做上记号,每次重复读取,就会在9911上发布一大堆重复的消息。使用GQL记下消息ID,下次读取时,使用since_id函数就能只读取最新的消息。方便、经济、高效。
  4. 该程序每分钟执行一次
    GAE的cron jobs正好可以为我们提供这样的功能。不过,我发现windows下的Google App Engine SDK功能不如linux下的全,尤其是cron方面,至少缺乏这两种功能cron_info,update_cron。因此不得不重启到linux下完成上传。不过一旦cron同步起来之后,就不用在这方面操心了;主程序代码如有更改,直接update即可。

好了,源程序放在这里,各位看官可以自行修改、借鉴,用在需要的地方。有问题请留言,我尽量回答。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
#to ensure the utf8 encoding environment
import sys
default_encoding = 'utf-8'
if sys.getdefaultencoding() != default_encoding:
	reload(sys)
	sys.setdefaultencoding(default_encoding)
 
import base64
import re
import time                                 #time zone convertion
import urllib
from google.appengine.api import urlfetch
from google.appengine.ext import db
 
class Twitter(db.Model):
	id=db.StringProperty()
 
def unescape(text):
   """Removes HTML or XML character references 
      and entities from a text string.
      keep &, >, < in the source code.
   from Fredrik Lundh
   http://effbot.org/zone/re-sub.htm#unescape-html
   """
   def fixup(m):
      text = m.group(0)
      if text[:2] == "&#":
         # character reference
         try:
            if text[:3] == "&#x":
               return unichr(int(text[3:-1], 16))
            else:
               return unichr(int(text[2:-1]))
         except ValueError:
            print "erreur de valeur"
            pass
      else:
         # named entity
         try:
            if text[1:-1] == "amp":
               text = "&"
            elif text[1:-1] == "gt":
               text = ">"
            elif text[1:-1] == "lt":
               text = "<"
            else:
               print text[1:-1]
               text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
         except KeyError:
            print "keyerror"
            pass
      return text # leave as is
   return re.sub("&#?\w+;", fixup, text)
 
#get one page of to user's replies
def sendMsg(user,sn,msg):
	auth=base64.b64encode(user+":"+sn)
	auth='Basic '+auth   
	msg=unescape(msg)
	form_fields = {
			"status": msg, 
			}
	form_data = urllib.urlencode(form_fields)
	result = urlfetch.fetch(url="http://api.9911.com/statuses/update.xml",
			payload=form_data,
			method=urlfetch.POST,
			headers={'Authorization':auth}
			)
 
	if result.status_code == 200:
		bk=result.content
		if bk.find("true"):
			return True
	else:
		return False
 
def parseTwitter(user,sn="",since_id=""):
	if since_id:
		url="http://twitter.com/statuses/user_timeline/%s.xml?since_id=%s"%(user,since_id)
	else:
		url="http://twitter.com/statuses/user_timeline/%s.xml"%(user)
 
	result = urlfetch.fetch(url)
 
	if result.status_code == 200:
		content=result.content
		m= re.findall(r"(?i)<id>([^<]+)</id>\s*<text>(?!@)([^<]+)</text>", content)
		print "<html><body><ol>"
		for x in reversed(m):
			id=x[0]
			text=x[1]
			print "<li>",id,text,"</li><br />\n"
			if sendMsg("9911username","9911password",text):
			#input 9911 username and password here
				msg=Twitter()
				msg.id=id
				msg.put()
 
		print "</ol></body></html>"
	else:
		print "get twitter data error"
 
def getLatest():
	msg=db.GqlQuery("SELECT * FROM Twitter ORDER BY id DESC")
	x=msg.count()
	if x:
		return msg[0].id
	else:
		return ""
 
print ""
latest=getLatest()
 
#use your own twitter user id here
parseTwitter("zhasm",since_id=latest)
2009年6月17日21:26
  • 2009年6月19日00:27 | #1

    (?!@):表示此处不可出现@字符。(?!…)是negative look-around,表示不允许出现某表达式。请参考正则表达式高级技巧基本概念实例详解一文。http://is.gd/15JfG

    在同步消息时,会有以@开头的消息,这是回复某人的消息,不值得同步。因此需要过滤。该表达式只匹配不是以@开头的消息。

    reversed函数:由于读取的twitter消息是从上到下读取,顺序是从新到旧;在向9911发送时,需要以从旧到新的顺序,才能与twitter顺序一致。该函数是将list逆向排序。

  • learso
    2009年6月19日00:15 | #2

    看你的正则表达式是一种享受,但我自己却一直未能把握要领。上面代码中有两处不明白,可否帮我解惑?
    1、在这句中 r"(?i)<id>([^<]+)</id>s*<text>(?!@)([^<]+)</text>" ,有(?!@)这个式子,不知道其中?!的作用是什么?
    2、for x in reversed(m)中,reversed函数作用是什么?
    谢谢!

  • learso
    2009年6月19日00:15 | #3

    看你的正则表达式是一种享受,但我自己却一直未能把握要领。上面代码中有两处不明白,可否帮我解惑?
    1、在这句中 r"(?i)<id>([^<]+)</id>s*<text>(?!@)([^<]+)</text>" ,有(?!@)这个式子,不知道其中?!的作用是什么?
    2、for x in reversed(m)中,reversed函数作用是什么?
    谢谢!

  • learso
    2009年6月19日00:15 | #4

    看你的正则表达式是一种享受,但我自己却一直未能把握要领。上面代码中有两处不明白,可否帮我解惑?
    1、在这句中 r"(?i)<id>([^<]+)</id>s*<text>(?!@)([^<]+)</text>" ,有(?!@)这个式子,不知道其中?!的作用是什么?
    2、for x in reversed(m)中,reversed函数作用是什么?
    谢谢!

  • learso
    2009年6月19日00:15 | #5

    看你的正则表达式是一种享受,但我自己却一直未能把握要领。上面代码中有两处不明白,可否帮我解惑?
    1、在这句中 r"(?i)<id>([^<]+)</id>s*<text>(?!@)([^<]+)</text>" ,有(?!@)这个式子,不知道其中?!的作用是什么?
    2、for x in reversed(m)中,reversed函数作用是什么?
    谢谢!

  • 2009年6月19日00:21 | #6

    (?!@):表示此处不可出现@字符。(?!…)是negative look-around,表示不允许出现某表达式。请参考正则表达式高级技巧基本概念实例详解一文。http://iregex.org/blog/crucial-concepts-behind-ad...

    在同步消息时,会有以@开头的消息,这是回复某人的消息,不值得同步。因此需要过滤。该表达式只匹配不是以@开头的消息。

  • learso
    2009年6月19日11:33 | #7

    明白了,謝謝

  • 潜行者
    2009年9月2日14:23 | #8

    请教,我不是程序员,也可能用GAE加上你这个代码将我的Twitter同步到我的9911吗?可否赐教下实现的步骤?
    我已经“Application Registered Successfully”,下一步不知道怎么弄了

  • 潜行者
    2009年9月2日14:31 | #9

    刚又下载并安装了GoogleAppEngine_1.2.4.msi,但是不知道怎么搞。

    • rex
      2009年9月3日20:57 | #10

      我在出差,暂不方便。加我gtalk吧,下周三可以线上联系。rex[at]zhasm[dot]com

  • 潜行者
    2009年9月10日14:31 | #11

    看了你的同步新浪微博的文章,是否用本文中的代码替换那篇文章的main.py,就可同步9911了。

    • 深柳堂主
      2009年9月10日16:13 | #12

      对。原理一样,架构也一样。

  • 2009年10月28日19:55 | #14

    嘀嗒的服务可以的,直接从twitter网页更新,同步到hellotxt,hellotxt可以同步其他的微博,不过嘀嗒正在申请OpenCloud,应该不久能实现twitter网页同步到其他微博。

发表评论

XHTML: 您可以使用这些标签: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">