@shellex说: paipai

用GAE同步推特到人人网状态

Google App Engine
事情是这样的。死狐狸@yegle做了个脚本,专门用来刷人人网上各位同学的屏幕。Shellex很赞赏这样的精神,但是Shellex不能整天开着Linux跑crontab吧。

而且Shellex也不是@yegle这样的土豪,买个VPS上面装了虚拟的私人网大发国难财,也不像@showfom那样的土豪,买个VPS在上面跑5个bash。唉唉,穷人。

为了达到去人人网恶心各位同学、增加Shellex的上镜率、提高Shellex的知名度的目的,Shellex在GAE的第一次就献给了这个脚本。

这个东西用到了webpy(居然用到了webpy!我坦白!Shellex当时想挖坑,但是考虑Shellex的人身安全还是不挖为妙…),其实没必要的哈,但是无所谓嘛,拿出来就好了。

写得有点乱七八糟的,见谅见谅,这是code.py:

#!/usr/bin/env python2.6
#coding=utf8
import web
import Cookie,urllib
import simplejson as json

from google.appengine.api import urlfetch
from setting import app
from google.appengine.ext import db
from google.appengine.api import users

renren_usr = '你的人人网用户名'
renren_passwd = '人人网密码'
twitter_usr = '推特用户名'
twitter_passwd = '推特密码'

cookie_buf = Cookie.SimpleCookie();

class LastTweetRecord(db.Model):
    id = db.StringProperty(multiline=True);

def make_cookie_header(cookie):
    ret = ''
    for v in cookie.values():
        ret += '%s=%s;' % (v.key, v.value)
    return ret

def get_tweets (usr, passwd):
    last_tweet_id = 0
    last_tweet_record = None
    last_tweet_records = db.GqlQuery('SELECT * FROM LastTweetRecord');
    for record in last_tweet_records:
        try:
            last_tweet_id = int(record.id)
            last_tweet_record = record
        except Exception, e:
            last_tweet_id = 1

    if last_tweet_records.count() == 0:
        last_tweet_id = 1
        last_tweet_record = LastTweetRecord()
        last_tweet_record.put()

    new_timeline = []
    timeline_uri = 'http://%s:%s@twitter.com/statuses/user_timeline.json?count=100&since_id=%d' % (usr, passwd, last_tweet_id)
    timeline = urllib.urlopen(timeline_uri).read();

    timeline = json.loads(timeline)

    if len(timeline) == 0:
        return []
    new_id = timeline[0]['id']
    if new_id == '' :
        return []

    last_tweet_record.id = str(new_id)
    db.put(last_tweet_record)

    for tweet in timeline:
        if tweet['text'][0] != '@' :
            new_timeline.append((tweet['text'].encode('utf8')+' via 唧唧'));
    return new_timeline

def login2renren():
    verify_url = 'http://passport.renren.com/PLogin.do'
    verify_data= urllib.urlencode(
        {
        'domain':'renren.com',
        'email':  renren_usr,
        'password': renren_passwd,
        'origURL':'http://home.renren.com/Home.do',
        })
    result = urlfetch.fetch(
        url=verify_url,
        headers={'Cookie':make_cookie_header(cookie_buf),
                'Content-Type': 'application/x-www-form-urlencoded',
                'user-agent':'Mozilla/5.0 (Linux; U; Linux i686; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.4.2.80 Safari/525.13',},
        method=urlfetch.POST,
        payload=verify_data,
        follow_redirects = False,
        )
    return result

def do_redirect(url, cookie):
    result = urlfetch.fetch(
        url=url,
        headers={'Cookie':make_cookie_header(cookie),
                'Content-Type': 'application/x-www-form-urlencoded',
                'user-agent':'Mozilla/5.0 (Linux; U; Linux i686; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.4.2.80 Safari/525.13',},
        method=urlfetch.GET,
        follow_redirects = False,
        )
    return result

def send_status(status, cookie):
    status_url = 'http://status.renren.com/doing/update.do'
    status_data = urllib.urlencode({
        'c': status,
        'raw': status,
        'isAtHome': 0,
        })
    result = urlfetch.fetch(
        url=status_url,
        headers={
            'Cookie':make_cookie_header(cookie),
            'Content-Type': 'application/x-www-form-urlencoded',
            'user-agent':'Mozilla/5.0 (Linux; U; Linux i686; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.4.2.80 Safari/525.13',
            'Referer': 'http://status.renren.com/ajaxproxy.htm'
        },
        method=urlfetch.POST,
        payload=status_data,
        )
    return result

class sync:
    def GET(s):
        global cookie_buf
    #get timeline
        timeline = get_tweets(twitter_usr, twitter_passwd)
        if len(timeline) == 0:
            return 'no tweet to sync.'
        #login to renren
        result = login2renren()
        cookie_buf = Cookie.SimpleCookie(result.headers.get('set-cookie', ''));
        callback_url = result.headers.get('location','xx');

        result = do_redirect(callback_url, cookie_buf)

        cookie_buf = Cookie.SimpleCookie(result.headers.get('set-cookie', ''))
        for tweet in timeline:
            result = send_status(tweet, cookie_buf)
        return 'ok'

if __name__ == "__main__":
    app.cgirun();
    #app.run();

然后是设置,为了防止被misuse,建议把url改得奇怪一些:

import web

urls = (
    '/task/sync', 'code.sync',
    )
app = web.application (urls, globals())

接下来是cron.yaml,每5分钟触发一次:

cron:
- description: every 5 minutes
  url: /task/sync
  schedule: every 5 minutes

接下来是app.yaml:

application: 你的app名字
version: 6
runtime: python
api_version: 1

handlers:
- url: /.*
  script: code.py

对了,如果需要技术支持的话,直接找我就好,5金一人,童叟无欺。

  1. On December 22, 2009 at 9:59 pm

    搞了两天才出来,因为对gae不熟悉,urllib2的支持有限,所以只能fetchurl来提取cookies; 最囧的是版本…版本…今天捣鼓了一个下午才发现我一直在错误的version 2上纠结…而不是正确的version 6…

    Notify
  2. On December 22, 2009 at 10:04 pm
    pjq :

    这招确实狠,发多了,应该会招来很多人的反感,偶尔发发,应该还不错。

    Notify
  3. On December 22, 2009 at 10:19 pm
    alswl :

    好东西啊,Mark…

    Notify
  4. On December 23, 2009 at 1:36 am

    围观土豪

    Notify
  5. On December 23, 2009 at 11:14 pm
    flashclouds :

    非常棒,请问你是如何尝试出这种方法的呢?

    我在本地能个取得cookie,但是在gae上就取不到。用了你的方法终于能在gae上取到了。请问,你的思路是如何来的?是抓包分析,然后模拟整个过程吗?

    Notify
  6. On December 24, 2009 at 7:35 am

    yegle明明是在发民难财。

    Notify
  7. On December 24, 2009 at 2:00 pm

    @flashclouds,
    额,用urllib2+cookie得不到cookies的。
    因为在GAE环境下,urllib2的功能是通过urlfetch来实现的。
    而cookies相关的能力貌似没法正常工作。
    所以得用urlfetch手工提取HTTP头部set-cookies的内容

    Notify
  8. On December 24, 2009 at 5:07 pm

    cody.py里面的“from setting import app” 这一行中的setting是什么啊?名字太普通了我也没搜到,加上去后在GAE中会提示找不到,我把它注释到了后却正常,也没看出有什么不同来。

    Notify
  9. On December 24, 2009 at 5:45 pm
    flashclouds :

    我当时在gae上用urllib失败后,也尝试了urlfetch结果也失败了,得到的cookie为空,看你的代码里面登录成功之后进行了重定向操作,然后就取得了cookie:
    do_redirect(callback_url, cookie_buf)

    我比较奇怪的是为什么我在本地不用重定向就能取得cookie,到了gae之后就需要程序自己来重定向呢?

    是不是urllib能够在本地自己进行重定向啊,多谢,多谢!!

    Notify
  10. On December 24, 2009 at 8:26 pm

    @liufeng,
    shellex在文章中把setting翻译成“设置”了..

    Notify
  11. On December 25, 2009 at 3:19 am

    @shellex,

    原来如此。不知道能不能把tweet-archive发送到日志上呢,这样子太水了。手机版的人人好像有点门。

    Notify
  12. On December 25, 2009 at 9:55 pm
    pjq :

    上传后,不能运行,好像是提示不能 import web和simplejson.
    在本地也是同样的提示,请问需要另外怎样的配置吗,对python和gae都不太懂。

    Notify
  13. On December 26, 2009 at 12:59 pm
    pjq :

    @pjq,
    弄好了,还要把webpy和simplejson上传上去。

    Notify
  14. On January 4, 2010 at 11:08 am
    qubic :

    改url那段放到哪个文件下

    Notify
  15. On February 6, 2010 at 10:39 pm
    hzq :

    对我这样的人并不是完整的攻略,放弃了。

    Notify
  16. On February 20, 2010 at 6:11 pm
    小米 :

    冲着这个教程,先订阅了你再说~~

    Notify

Trackbacks

  1. Tweets that mention 用GAE同步推特到人人网状态 | ShelleX is Not ShelleXtend -- Topsy.com
  2. 梦.:如此短暂
  3. GReader分享到人人脚本 « 这里没有橄榄

Leave a Reply