Python使用Cookie字符串发起HTTP请求的几个方法(2)

前篇介绍了直接使用httplib发送Cookie,简洁直观。创建cookielib.CookieJar对象自动管理Cookie稍繁琐一些,但是一旦创建,可供urllib2创建opener,后续的所有cookie更新和过期删除都是自动处理的。

1. 解析Cookie Str,得到Cookie.SimpleCookie对象

我们得到的Request Header中,Cookie是这样的格式:

Cookie: key1=val1; key2=val2; key3=val3

第一步是解析它,得到Cookie.SimpleCookie对象。通过字符串构造即可: Cookie.SimpleCookie(cookie_str)

得到的是一个dict-like对象,它的每一个值都是一个Set-Cookie语句,上述例子解析后是:

from Cookie import SimpleCookie
>>> sc = SimpleCookie('key1=val1; key2=val2; key3=val3')
>>> for c,v in sc.items():
...     print c,v
...
key3 Set-Cookie: key3=val3
key2 Set-Cookie: key2=val2
key1 Set-Cookie: key1=val1

2. 用SimpleCookie创建cooklib.Cookie对象

上述SimpleCookie不能直接使用,因为一个完整的Cookie,还必须包括额外的字段,如:domain、path、expires等。第二步工作是创建cooklib.Cookie对象,直接将key, value传入cooklib.Cookie类的构造函数即可。

得到一系列cookielib.Cookie对象,便可以依次用它们来更新CookieJar了。

3. 完整的示例代码

import cookielib
import Cookie
import urllib2

def build_opener_with_cookie_str(cookie_str, domain, path='/'):
    simple_cookie = Cookie.SimpleCookie(cookie_str)    # Parse Cookie from str
    cookiejar = cookielib.CookieJar()    # No cookies stored yet

    for c in simple_cookie:
        cookie_item = cookielib.Cookie(
            version=0, name=c, value=str(simple_cookie[c].value),
                     port=None, port_specified=None,
                     domain=domain, domain_specified=None, domain_initial_dot=None,
                     path=path, path_specified=None,
                     secure=None,
                     expires=None,
                     discard=None,
                     comment=None,
                     comment_url=None,
                     rest=None,
                     rfc2109=False,
            )
        cookiejar.set_cookie(cookie_item)    # Apply each cookie_item to cookiejar
    return urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))    # Return opener


if __name__ == '__main__':
    cookie_str = 'tLargeScreenP=1; Authorization=Basic%20HereIsMySecret; subType=pcSub; TPLoginTimes=2'
    opener = build_opener_with_cookie_str(cookie_str, domain='192.168.1.253')
   
    html_doc = opener.open('http://192.168.1.253').read()
    import re
    print 'Open With Cookie:', re.search('(.*?)', html_doc, re.IGNORECASE).group(1)

    html_doc = urllib2.urlopen('http://192.168.1.253').read()
    print 'Open Without Cookie:', re.search('(.*?)', html_doc, re.IGNORECASE).group(1)

运行结果:

build-cookiejar-from-str

192.168.1.253是我房间的路由器,第一次使用带Cookie的Opener请求,第二次不带Cookie请求。

解决python urllib2 302重定向后丢cookie的问题

通过昨天写的python脚本,我已经注册激活了50个box.net账号,用作上传文件。

今天我继续写代码,用来自动登录box.net并获取所有文件的分享链接。

不过测试的时候出现了点问题,账号信息正确,但总是登录不成功。

headers中referer、user-agent都有伪造,cookie也有发送。

通过设置debuglevel=1跟踪http请求,最终发现了问题:

        httpHandler = urllib2.HTTPHandler(debuglevel=1)
        httpsHandler = urllib2.HTTPSHandler(debuglevel=1)
        self.opener = urllib2.build_opener(httpHandler, httpsHandler)

urllib2很聪明,在发现HttpResponse中有重定向(301, 302)时会自动转向请求这个新的URL,

但urllib2有个严重的问题,它没有带着cookie去请求新的URL。

这也是说,前期我们通过一个POST请求来获取cookie(对应着服务器上的session),

但urllib2却没有带着必要的cookie去访问需要授权的页面。

一开始我是想直接用httplib的,考虑到前后一致性才全部用urllib2,结果urllib2又出问题。。。

解决这个问题,可以:

1. 换httplib来实现,它不会像urllib2会自动处理重定向,cookie不会丢

2. 截获重定向,禁止urllib2自动处理

我选择了重写urllib2.HTTPRedirectHandler的http_error_302方法,截获302,让urllib2不再处理302:

class HttpRedirect_Handler(urllib2.HTTPRedirectHandler):
    def http_error_302(self, req, fp, code, msg, headers):
        pass

然后在urllib2.build_opener方法中用HttpRedirect_Handler的一个实例做参数,例如:

self.opener = urllib2.build_opener(HttpRedirect_Handler(),
                                   urllib2.HTTPCookieProcessor(self.cookie))

这样,当我们用上述opener去POST登录时,遇到302就不会再自动转向了,

登录成功获取到的cookie也不会丢。

后面再带着self.cookie去请求需要授权的页面,就可以获取到正确的内容了。