将“Cookie”放入“CookieJar”

45

我正在使用Python的Requests库进行HTTP请求。我从服务器获取到的cookie是文本形式的。如何将其转换为包含该cookie的CookieJar对象?


2
使用requests.Session(),您无需担心cookie jars的问题。会话对象会为您管理接收和发送cookies。 - Martijn Pieters
参见此答案,了解在不使用Session对象的情况下实现该功能的方法。 - Marcus Junius Brutus
9个回答

51

Requests库的旧版本(0.14.2及更早版本)在您传递一个CookieJar对象时,会自动将新的cookie添加到cookie jar中:

import requests
import cookielib

URL = '...whatever...'
jar = cookielib.CookieJar()
r = requests.get(URL, cookies=jar)
r = requests.get(URL, cookies=jar)

第一次请求URL时将填充cookie jar,第二次请求会将cookie发送回服务器。

这在Requests 1.0.0(于2012年发布)之后的版本中不再适用。


3
这对我不起作用,所以我提出了一个类似的问题来澄清。 - tommy_o
3
请注意,如果您正在使用requests.Session对象,则不需要这样做;它将完全处理cookie。 - Martijn Pieters
8
想要指出的是,"import cookielib" 只适用于 Python 2.x 版本,而在 Python 3.x 版本中则应该使用 "import http.cookiejar"。请注意区分。 - Doormatt
4
我无法使这段代码正常工作。即使在每个请求中传递了一个cookie jar,我的cookies也没有被保留,但是在评论部分列出并向下移动的requests.Session可以完美地运行。 - djsumdog
2
这个答案在2012年发布的Requests 1.0.0版本中已经失效。我已经确认它可以在Python 2.7.13和Requests 0.14.2中使用。 - user3064538
显示剩余2条评论

34

一个 Requests Session 可以接收和发送 cookies。

s = requests.Session()

s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get("http://httpbin.org/cookies")

print(r.text)
# '{"cookies": {"sessioncookie": "123456789"}}'

上面的代码是从 会话对象 中偷来的。

如果您想要使 cookie 在代码运行之间持久化保存在磁盘上,可以直接使用 CookieJar 并进行保存和加载:

from http.cookiejar import LWPCookieJar
import requests

cookie_file = '/tmp/cookies'
jar = LWPCookieJar(cookie_file)

# Load existing cookies (file might not yet exist)
try:
    jar.load()
except:
    pass

s = requests.Session()
s.cookies = jar

s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get("http://httpbin.org/cookies")

# Save cookies to disk, even session cookies
jar.save(ignore_discard=True)

然后查看文件 /tmp/cookies

#LWP-Cookies-2.0
Set-Cookie3: sessioncookie=123456789; path="/"; domain="httpbin.org"; path_spec; discard; version=0

2
我真的希望这个被投票到顶部。所选的最佳答案在使用requests-2.3.0时根本不起作用。 - djsumdog
1
谢谢你提供 ignore_discard=True 的提示!我之前找不出为什么我的 JAR 文件保存了一个空文件。 - Michael

14

我认为许多答案都没有抓住重点。有时候,那个库不是在使用Requests。或者它不会暴露它正在使用的cookiejar。有时候我们所拥有的只是cookie字符串。在我的情况下,我正在尝试借用来自pyVmomi的auth cookie。

import requests
import http.cookies

raw_cookie_line = 'foo="a secret value"; Path=/; HttpOnly; Secure; '
simple_cookie = http.cookies.SimpleCookie(raw_cookie_line)
cookie_jar = requests.cookies.RequestsCookieJar()
cookie_jar.update(simple_cookie)

这给了我们以下的cookie_jar:

In [5]: cookie_jar
Out[5]: <RequestsCookieJar[Cookie(version=0, name='foo', value='a secret value', port=None, port_specified=False, domain='', domain_specified=False, domain_initial_dot=False, path='/', path_specified=True, secure=True, expires=None, discard=False, comment='', comment_url=False, rest={'HttpOnly': True}, rfc2109=False)]>

我们可以像平常一样使用它:

requests.get(..., cookies=cookie_jar)

5
为了帮助您,我写了一个完整的模块。 我在我的个人网页和 Google 的 cookies 中尝试了它,所以我认为它是有效的。
我从如何在 Python 中向现有的 cookielib CookieJar 实例添加 cookie?中获得了帮助。
这里有很多不符合Python语法规范的代码,包括半成品修补程序,因此您的使用可能会有所不同。可以根据需要进行微调,特别是针对假定项(例如端口80)。下面作为参数的"request"是类型为requests.request的,并且我意识到"method"参数必须全部大写。
注意:我还没有时间添加澄清注释,所以您将不得不使用源代码。
import Cookie,cookielib,requests,datetime,time  # I had this out, but I realized later I needed it when I continued testing

def time_to_tuple(time_string):
    wday = {'Mon':0,'Tue':1,'Wed':2,'Thu':3,'Fri':4,'Sat':5,'Sun':6}
    mon = {'Jan':1,'Feb':2,'Mar':3,'Apr':4,'May':5,'Jun':6,'Jul':7,'Aug':8,'Sep':9,'Oct':10,'Nov':11,'Dec':12}
    info = time_string.split(' ')
    info = [i.strip() for i in info if type(i)==str]
    month = None
    for i in info:
        if '-' in i:
            tmp = i.split('-')
            for m in tmp:
                try:
                    tmp2 = int(m)
                    if tmp2<31:
                        mday = tmp2
                    elif tmp2 > 2000:
                        year = tmp2
                except:
                    for key in mon:
                        if m.lower() in key.lower():
                            month = mon[key]
        elif ':' in i:
            tmp = i.split(':')
            if len(tmp)==2:
                hour = int(tmp[0])
                minute = int(tmp[1])
            if len(tmp)==3:
                hour = int(tmp[0])
                minute = int(tmp[1])
                second = int(tmp[2])
        else:
            for item in wday:
                if ((i.lower() in item.lower()) or (item.lower() in i.lower())):
                    day = wday[item]
            if month is None:
                for item in mon:
                    if ((i.lower() in item.lower()) or (item.lower() in i.lower())):
                        month = mon[item]
    return year,month,mday,hour,minute,second

def timefrom(year,month,mday,hour,minute,second):
    time_now = time.gmtime()
    datetime_now = datetime.datetime(time_now.tm_year,time_now.tm_mon,
                                     time_now.tm_mday,time_now.tm_hour,
                                     time_now.tm_min,time_now.tm_sec)
    then = datetime.datetime(year,month,mday,hour,minute,second)
    return (datetime_now-then).total_seconds()

def timeto(year,month,mday,hour,minute,second):
    return -1*timefrom(year,month,mday,hour,minute,second)



##['comment', 'domain', 'secure', 'expires', 'max-age', 'version', 'path', 'httponly']
def parse_request(request):
    headers = request.headers
    cookieinfo = headers['set-cookie'].split(';')
    name = 'Undefined'
    port=80
    port_specified=True
    c = Cookie.SmartCookie(headers['set-cookie'])
    cj = cookielib.CookieJar()
    for m in c.values():
        value = m.coded_value
        domain = m['domain']
        expires = m['expires']
        if type(expires) == str:
            tmp = time_to_tuple(expires)
            expires = timeto(tmp[0],tmp[1],tmp[2],tmp[3],tmp[4],tmp[5])
        max_age=m['max-age']
        version = m['version']
        if version == '':
            version = 0
        path = m['path']
        httponly = m['httponly']
        if httponly == '':
            if 'httponly' in headers['set-cookie'].lower():
                httponly = True
        else:
            httponly = False
        secure = m['secure']
        comment=m['comment']
        port = 80
        port_specified=False
        domain_specified=True
        domain_initial_dot = domain.startswith('.')
        path_specified=True
        discard = True
        comment_url=None
        rest={'HttpOnly':httponly}
        rfc2109=False
        ck = cookielib.Cookie(version,name,value,port,port_specified,domain,
                              domain_specified,domain_initial_dot,path,path_specified,
                              secure,expires,discard,comment,comment_url,rest,rfc2109)
        cj.set_cookie(ck)
    return cj

我有一个name="undefined"的问题,因为我无法找出它的名字在哪里。如果有人能指出来,我会很高兴更新代码。 - Snakes and Coffee
如果你有太多的cookies(考虑到cookies的功能,我不太相信这种情况会发生),可以随意使用yield代替return。 - Snakes and Coffee
1
刚刚发布了一个新版本(v0.6.0),它允许您使用简单的字典将cookie附加到请求中。 http://docs.python-requests.org/en/latest/user/quickstart/#cookies - Kenneth Reitz
这段代码不就等同于现有的 cookielib.CookieJar.extract_cookies(response, request) 吗? - MestreLion

3

好的,cookielib.LWPCookieJar类有load和save方法。看一下格式并检查是否与本机cookie格式匹配。您可能可以使用StringIO将Cookie直接加载到CookieJar中。

或者,如果Requests在后台使用urllib2,则可以向默认打开程序添加一个cookie处理程序。


2

我正在尝试做同样的事情。目前为止,我已经做了一些工作,但出于某种原因,它没有在头部发送cookie。不过这可能已经足够让你解决问题了。

import requests
import cookielib
import logging

log = logging.getLogger(__name__)

def auth(auth_url, cookies):
    cj = cookielib.CookieJar()
    for x in cookies:
         if len(cookies[x]) > 0:
             ck = cookielib.Cookie(version=1, name=x, value=cookies[x], 
                    port=None, port_specified=False, domain='.example.com', 
                    domain_specified=True, 
                    domain_initial_dot=True, path='/', 
                    path_specified=True, secure=False, 
                    expires=None, discard=True, 
                    comment=None, comment_url=None, 
                    rest=None, rfc2109=True)
             log.info(ck)
             cj.set_cookie(ck)

    log.info("cookies = %s " % cj)
    response = requests.get(auth_url, cookies=cj)
    log.info("response %s \n" % response)
    log.info("response.headers %s \n" % response.headers)
    log.info("response.content %s \n" % response.content)

2

我假设你已经请求了url,并且你得到了headers作为响应。 url的类型是字符串。 headers的类型是列表。

import urllib2
import cookielib

class dummyResponse:
    def __init__(self, headers):
        self.headers = headers
    def info(self):
        return dummyInfo(self.headers)

class dummyInfo:
    def __init__(self, headers):
        self.headers = headers
    def getheaders(self, key):
        # Headers are in the form: 'Set-Cookie: key=val\r\n'. We want 'key=val'
        newMatches = []
        for header in self.headers:
            if header.lower().startswith(key.lower()):
                clearHeader = header[len(key) + 1:].strip()
                newMatches.append(clearHeader)
        return newMatches

req = urllib2.Request(url)
resp = dummyResponse(headers)

jar = cookielib.CookieJar()
jar.extract_cookies(resp, req)

2

以下是一个简化版的overthink's answer,介绍如何在Python 3中获取cookiejar并持久化cookies:

import requests

s = requests.Session()

r1 = s.get('https://stackoverflow.com')
print("r1", r1.cookies) # Have cookie
print("s", s.cookies)  # Have cookie(jar)

r2 = s.get('https://stackoverflow.com') #The cookie from r1 is resend
print("r2", r2.cookies) #No cookie (could be a new one)
print("s", s.cookies)  #Keep the cookie(jar) from r1

为了在不同的会话之间保持cookie,您需要保存和重复使用Session中的cookiejar(变量s)。
如果在其他网站上r1/r2/s得到不同的答案,请检查是否存在重定向。例如,r1/r2将无法获取https://www.stackoverflow.com的任何cookie,因为它被重定向到没有www的网站。

1

正如dstanek所回答的那样,Requests会自动将响应cookie放入cookie jar中。

然而,如果您手动指定了Cookie头条目,Requests将不会为您将这些cookie放入jar中。这意味着任何后续请求都将缺少您最初设置的cookie,但它将拥有任何新的cookie。

如果您确实需要手动为请求创建cookie jar,请使用requests.cookies.RequestsCookieJar。以防他们的示例代码发生更改:

jar = requests.cookies.RequestsCookieJar()
jar.set('tasty_cookie', 'yum',   domain='httpbin.org', path='/cookies')
jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
url = 'http://httpbin.org/cookies'
r = requests.get(url, cookies=jar)

请注意,如果您提供了一个 cookie jar 和一个 Cookie header,则 header 优先级更高,但 cookie jar 仍将保留以供未来请求使用。

dstanek的答案在2012年停止工作,随着Requests 1.0.0的推出。 - user3064538

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接