Python2.5使用新浪微博Python SDK遇到的问题与解决方法

鉴客 发布于 2011/06/14 15:25
阅读 1K+
收藏 5

上周花了一周时间做了一个GAE(Google App Engine)的与新浪微博交互的网络应用,期间遇到了一些问题,经过一番搜索和思考将它们解决。现在分享给大家,也希望对于不妥的地方得到大家的指正。

首先是OAuth验证问题,这事我折腾了很久才弄明白怎么回事,本想自己写一篇总结了,后来想想还是算了,在网上找到了很多很好用但是比较稀缺的资源,如下:

先来一个官方网站:http://oauth.net/

然后是新浪微博官方网站上的:http://open.t.sina.com.cn/wiki/index.php/Oauth_new

广为流传的:http://blog.csdn.net/hereweare2009/archive/2009/03/08/3968582.aspx

说的比较形象的:http://www.skiyo.cn/2010/08/11/understanding-oauth-and-douban-oauth-test/(虽然这个是说豆瓣的,但是原理是一样的)

然后是我找到的唯一一个和Python有关的,使用Python的SDK的一个OAuth验证的Web应用的详细代码(SDK默认提供的是桌面应用验证的代码,要输入PIN值,网络应用不是很方便)(非常感谢这位作者):http://www.cnblogs.com/fengmk2/archive/2010/09/27/sina-weibo-oauth-on-django.html

有了上面那些,应该就不愁新浪微博的OAuth验证了。

然后开始实战,刚动手就遇到问题了,新浪微博Python 2.x的SDK貌似和Python2.5不兼容,这里是下载地址。(我没用过别的版本的Python,所以希望用过高版本的2.X系列的朋友告诉一生是否可以直接使用。)

我用的SDK是20100906版本的。因为api.py这个文件中的197行(就是下面的最后一行)有问题,运行会直接报错。附近代码如下:

return bind_api(
    path = '/statuses/upload.json',
    method = 'POST',
    payload_type = 'status',
    require_auth = True,
    allowed_param = allowed_param
)(self, *args, post_data=post_data, headers=headers)

后来,我曾尝试上传到GAE的真实环境上,也会报错。说明新浪微博的PythonSDK在Python2.5下是有问题的。最初的解决方法是——暴 力法……我直接将这一行后面的(self, *args, post_data=post_data, headers=headers)给注释掉了。所幸在前期开发一切安好。

但到后来我需要调用upload这个方法进行上传图片微博时,遇到了问题。(我上网找了很久,由于这方面信息太少了,基本搜不到用Python SDK开发新浪微博应用的资料,一般像这种东西外国人写的资料会比较多,但无奈新浪微博是中国的应用,所以外国网站是绝不会搜到的……)

然后我通过了很多方法尝试,最主要的是我将之前把这行代码注掉的事情给忘了……

我只能不断的跟踪源码中的关于生成这个post请求的中,生成的header和MIME的信息的部分,希望从中找到答案。新浪微博API文档中关于upload这个方法的描述中的最后部分,有一个测试方法,可以相关工具观看上传图片时,HTTP传送数据的信息。

我试着对比了几次这个信息和API中生成的header和MIME信息中的差别,跟踪了N次也不得其解。后来才想起来我曾注掉的那行代码,然后将它 做了如下修改。(后来根据进一步跟进调试得知,由于注释掉了那段代码,所以我的数据其实没有传进去,也就没有post给新浪微博API的服务器。)

 

""" statuses/upload """
def upload(self, filename, status, lat=None, long=None, source=None):
    if source is None:
        source=self.source
    headers, post_data = API._pack_image(filename, 1024, source=source, status=status, lat=lat, long=long, contentname="pic")
    args = [status]
    allowed_param = ['status']

    if lat is not None:
        args.append(lat)
        allowed_param.append('lat')

    if long is not None:
        args.append(long)
        allowed_param.append('long')

    if source is not None:
        args.append(source)
        allowed_param.append('source')

    kargs = {
        'post_data': post_data,
        'headers': headers,
    }
    return bind_api(
        path = '/statuses/upload.json',
        method = 'POST',
        payload_type = 'status',
        require_auth = True,
        allowed_param = allowed_param
    )(self, *args, **kargs)

然后就可以用了。这样post_data和headers的信息才绑定给了api,将数据post给新浪微博API相关的地址。

爱颜色还有一个需求,就是上传文件时不是打开本地的一个文件然后上传。然而新浪微博Python的SDK里只有打开文件上传的方法,而我需要的是动态生成一个颜色的图像的二进制数据,并将图像的二进制数据直接上传到微博API的服务器。

我本不想在SDK的内部动手脚,但没有找到更好的办法。后来在内部加了一个新的upload方法,这里涉及到图像处理的一个内部函数 API._pack_image_data,这个方法负责包装post请求的header和MIME信息,重点是包装了图像的二进制信息,它是根据文件名 打开文件,然后读取二进制数据再将其以“Content-Transfer-Encoding: binary”方式post给新浪微博api的服务器。

我在这里将打开文件读取二进制数据,然后修改了API._pack_image_data(修改了部分细节,如判断文件大小,判断文件类型的部分, 见代码中高亮的部分。),改为直接将图像二进制数据传给API._pack_image_data方法参数的方式,然后 API._pack_image_data方法直接处理传进去的二进制数据。具体方法如下:

 

@staticmethod
def _pack_image_data(file_type, data, imagename, max_size=1024, source=None, status=None, lat=None, long=None, contentname="pic"):
    """Pack image from file into multipart-formdata post body"""
    # image must be less than 700kb in size
    try:
        if len(data) > (max_size * 1024):
            raise WeibopError('File is too big, must be less than 700kb.')
    #except os.error, e:
    except os.error:
        raise WeibopError('Unable to access file')

    # image must be gif, jpeg, or png
    if file_type is None:
        raise WeibopError('Could not determine file type')
    if file_type not in ['image/gif', 'image/jpeg', 'image/png']:
        raise WeibopError('Invalid file type for image: %s' % file_type)

    # build the mulitpart-formdata body
    BOUNDARY = 'Tw3ePy'
    body = []
    if status is not None:
        body.append('--' + BOUNDARY)
        body.append('Content-Disposition: form-data; name="status"')
        body.append('Content-Type: text/plain; charset=US-ASCII')
        body.append('Content-Transfer-Encoding: 8bit')
        body.append('')
        body.append(status)
    if source is not None:
        body.append('--' + BOUNDARY)
        body.append('Content-Disposition: form-data; name="source"')
        body.append('Content-Type: text/plain; charset=US-ASCII')
        body.append('Content-Transfer-Encoding: 8bit')
        body.append('')
        body.append(source)
    if lat is not None:
        body.append('--' + BOUNDARY)
        body.append('Content-Disposition: form-data; name="lat"')
        body.append('Content-Type: text/plain; charset=US-ASCII')
        body.append('Content-Transfer-Encoding: 8bit')
        body.append('')
        body.append(lat)
    if long is not None:
        body.append('--' + BOUNDARY)
        body.append('Content-Disposition: form-data; name="long"')
        body.append('Content-Type: text/plain; charset=US-ASCII')
        body.append('Content-Transfer-Encoding: 8bit')
        body.append('')
        body.append(long)
    body.append('--' + BOUNDARY)
    body.append('Content-Disposition: form-data; name="'+ contentname +'"; filename="%s"' % imagename)
    body.append('Content-Type: %s' % file_type)
    body.append('Content-Transfer-Encoding: binary')
    body.append('')
    body.append(data)
    body.append('--' + BOUNDARY + '--')
    body.append('')
    body.append('--' + BOUNDARY + '--')
    body.append('')
    body = '\r\n'.join(body)
    # build headers
    headers = {
        'Content-Type': 'multipart/form-data; boundary=Tw3ePy',
        'Content-Length': len(body)
    }

    return headers, body

至于动态生成图像二进制数据,我使用了pngCanvas,一个使用纯Python代码的生成png图像的工具。于是关于使用Python2.5和新浪微博SDK上传图片微博就大体折腾完了。

本文永久链接:http://qhm123.appspot.com/2010/10/26/python2.5-sina-api-weibo-sdk-problem-solution.html

加载中
返回顶部
顶部