Quantcast
Channel: qhm123(鸣)'s blog » Python
Viewing all articles
Browse latest Browse all 7

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

$
0
0

上周花了一周时间做了一个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上传图片微博就大体折腾完了。


Viewing all articles
Browse latest Browse all 7

Latest Images

Trending Articles





Latest Images