如何将请求(Python)的cookies保存到文件?

69

如何在 Python 中使用库 requests 发出请求后响应的处理

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
bot = requests.session()
bot.get('http://google.com')

将所有的cookie保存到一个文件中,然后从文件中恢复cookie。

12个回答

97

暂时没有直接的方法可以这样做,但这并不难。

你可以从会话中获取一个 CookieJar 对象,使用 session.cookies,然后使用 pickle 将其存储到文件中。

完整示例:

import requests, pickle
session = requests.session()
# Make some calls
with open('somefile', 'wb') as f:
    pickle.dump(session.cookies, f)

加载过程如下:

session = requests.session()  # or an existing session

with open('somefile', 'rb') as f:
    session.cookies.update(pickle.load(f))

requests库使用requests.cookies.RequestsCookieJar()子类,该子类明确支持pickling和类似字典的API。RequestsCookieJar.update()方法可用于使用从pickle文件加载的cookie更新现有会话cookie jar。


12
requests.utils.dict_from_cookiejarrequests.utils.cookiejar_from_dict不是必需的。它们无法保存不同域名下相同名称的cookies,也无法保存所有必需的cookie数据。由于这些问题,我曾经花费了很长时间进行调试。 - Elmo
1
将 cookiejar 进行 pickling 似乎无法保存其关联的主机。当您再次加载时,所有 cookie 都只在主机“”中。 - MattCochrane
9
@MattClimbs,问题在于dict_from_cookiejar函数没有保存主机信息。实际上,在当前版本中,session.cookies可以直接进行pickle和unpickle,无需转换为dict。 此外,requests.utils.dict_from_cookiejar可以替换为session.cookies.get_dict()cookiejar_from_dict可以替换为session.cookies.update(my_dict) - MarSoft
7
编辑注:我更新了这篇顶级答案而不是添加新帖子。这篇答案已经足够接近,但需要更新API的更改,这样这个任务会变得更加容易。不要使用字典实用程序,它们根本不需要,并且不保留重要的cookie元数据。如果作者希望恢复我的更改,我可以发布自己的答案;在这种情况下,我会感激提前通知。 - Martijn Pieters
在我的情况下(一个 Python 新手),在 open() as f 之后应该有一个 f.close()。否则,两个连续的 open() 将会引发 "TypeError: can't pickle _thread.RLock objects"。 - Weekend
1
@周末,当使用with语句时,您不需要显式调用f.close()。_上下文管理器_会为您隐式调用f.close()。请参见https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files答案中的两个单独的`with`语句未嵌套。 - GordonAitchJay

37

在像 r = requests.get() 这样的调用之后,r.cookies 将返回一个 RequestsCookieJar 对象,你可以直接使用 pickle 来对其进行序列化。

import pickle
def save_cookies(requests_cookiejar, filename):
    with open(filename, 'wb') as f:
        pickle.dump(requests_cookiejar, f)

def load_cookies(filename):
    with open(filename, 'rb') as f:
        return pickle.load(f)

#save cookies
r = requests.get(url)
save_cookies(r.cookies, filename)

#load cookies and do a request
requests.get(url, cookies=load_cookies(filename))

如果您想将cookie以人类可读的格式保存,您需要做一些工作,将RequestsCookieJar提取到LWPCookieJar中。

import cookielib
def save_cookies_lwp(cookiejar, filename):
    lwp_cookiejar = cookielib.LWPCookieJar()
    for c in cookiejar:
        args = dict(vars(c).items())
        args['rest'] = args['_rest']
        del args['_rest']
        c = cookielib.Cookie(**args)
        lwp_cookiejar.set_cookie(c)
    lwp_cookiejar.save(filename, ignore_discard=True)

def load_cookies_from_lwp(filename):
    lwp_cookiejar = cookielib.LWPCookieJar()
    lwp_cookiejar.load(filename, ignore_discard=True)
    return lwp_cookiejar

#save human-readable
r = requests.get(url)
save_cookies_lwp(r.cookies, filename)

#you can pass a LWPCookieJar directly to requests
requests.get(url, cookies=load_cookies_from_lwp(filename))

all(isinstance(c, cookielib.Cookie) for c in r.cookies)True,那么为什么在 save_cookies_lwp 中还要再次创建每个 cookie 项呢? - consatan
1
请在您的回答中使用会话(session)。 - User

28

我提供一种使用 JSON 的方法:

保存 cookie -

import json
with open('cookie.txt', 'w') as f:
    json.dump(requests.utils.dict_from_cookiejar(bot.cookies), f)

并加载 cookie -

import json
session = requests.session()  # or an existing session

with open('cookie.txt', 'r') as f:
    cookies = requests.utils.cookiejar_from_dict(json.load(f))
    session.cookies.update(cookies)

5
requests.utils.dict_from_cookiejar <-- 这正是我想要的 - Mr. Developerdude
5
请注意,dict_from_cookiejar 函数会丢失 host 信息,因此如果存在多个不同 host 的同名 cookie 时,该函数将无法正常工作。 - MarSoft
这应该是最佳答案 - 没有默认包含的外部库。 - Emil
1
这不正确,dict_from_cookiejar 会丢失许多关于 cookies 的信息。 - Jurass
如果你使用这个,你会丢失很多信息(比如主机)。 - undefined

25

对@miracle2k的回答进行扩展,requests documented 与任何cookielib CookieJar兼容。 LWPCookieJar(和MozillaCookieJar)可以将其 cookie 保存到文件中并从文件中加载。以下是一个完整的代码片段,可用于为请求会话保存和加载 cookie。 ignore_discard参数用于与 httpbin 进行测试,但您可能不想在真正的代码中包含它。

import os
from cookielib import LWPCookieJar

import requests


s = requests.Session()
s.cookies = LWPCookieJar('cookiejar')
if not os.path.exists('cookiejar'):
    # Create a new cookies file and set our Session's cookies
    print('setting cookies')
    s.cookies.save()
    r = s.get('http://httpbin.org/cookies/set?k1=v1&k2=v2')
else:
    # Load saved cookies from the file and use them in a request
    print('loading saved cookies')
    s.cookies.load(ignore_discard=True)
    r = s.get('http://httpbin.org/cookies')
print(r.text)
# Save the session's cookies back to the file
s.cookies.save(ignore_discard=True)

10

我发现其他答案存在以下问题:

  • 它们不适用于会话。
  • 它们无法正确保存和加载。只有cookie的名称和值被保存,过期日期、域名等都丢失了。

这个答案解决了这两个问题:

import requests.cookies

def save_cookies(session, filename):
    if not os.path.isdir(os.path.dirname(filename)):
        return False
    with open(filename, 'w') as f:
        f.truncate()
        pickle.dump(session.cookies._cookies, f)


def load_cookies(session, filename):
    if not os.path.isfile(filename):
        return False

    with open(filename) as f:
        cookies = pickle.load(f)
        if cookies:
            jar = requests.cookies.RequestsCookieJar()
            jar._cookies = cookies
            session.cookies = jar
        else:
            return False

然后只需调用 save_cookies(session, filename) 来保存,或者调用 load_cookies(session, filename) 来加载。就是这么简单。


1
这对我有效,没有在每个函数中加入os.path.isdir行。调用open()会创建文件(如果不存在),并且对于我的使用情况正确地进行写入。 - Rontron
这个很好用,但需要针对Python 3进行小更新。Pickling会创建二进制数据,因此打开调用应分别为:open(filename,“wb”)和open(filename,“rb”)。 - djkrause

9
这将完成工作:
session.cookies = LWPCookieJar('cookies.txt')

CookieJar API需要你手动调用load()和save()方法。如果你不关心cookies.txt格式,我有一个ShelvedCookieJar实现可在变更时自动持久化


3
这个答案缺少几个步骤。以下是完整的代码:cj = cookielib.LWPCookieJar(cookie_file) cj.load() session.cookies = cj - ChaimG
我尝试使用ShelvedCookieJar,但我发现除非调用jar.shelf.close(),否则结果不会被刷新到磁盘上,而且在调用jar.shelf.close()后该存储器不能再被使用,因此管理该存储器的生命周期并不简单。 - Jason R. Coombs

5

Python 3 代码:

请注意,网络上绝大多数的 cookies 都是 Netscape cookies。因此,如果您想将 cookies 保存到 Mozilla cookies.txt 文件格式中(该格式也被 Lynx 和 Netscape 浏览器使用),请使用 MozillaCookieJar。

from http.cookiejar import MozillaCookieJar
import requests

s = requests.Session()
s.cookies = MozillaCookieJar('cookies.txt')
# or s.cookies = MozillaCookieJar() and later use s.cookies.filename = 'cookies.txt' or pass the file name to save method.

response = s.get('https://www.msn.com')

s.cookies.save()

如果文件已经存在,写入的文件将覆盖原有内容,从而擦除其中包含的所有cookie。可以使用load()或revert()方法恢复保存的cookie。

请注意,如果没有传递ignore_discard参数并请求保存会话cookie,save()方法不会保存会话cookie。

s.cookies.save(ignore_discard=True)

使用load方法:

从文件加载cookies。

旧的cookies会被保留,除非被新加载的cookie覆盖。

s.cookies.load()

使用还原方法:

清除所有的 Cookie,并从保存的文件中重新加载 Cookie。

s.cookies.revert()

如果您使用load或revert方法,可能需要传递一个true ignore_discard参数。

关于使用MozillaCookieJar的注意事项:

注意:这会丢失有关RFC 2965 cookie以及端口等较新或非标准cookie属性的信息。

更多阅读


这基本上可以正常工作,但是您无法像使用requests.update()一样添加/更新cookie。 - user136036
1
我要如何将一个“RequestsCookieJar”转换为“MozillaCookieJar”,以便我可以以此格式保存它? - user3064538

4

将Cookies转换为字典列表并保存到JSON或数据库的简单方法。 这是一个具有session属性的类的方法。

def dump_cookies(self):
    cookies = []
    for c in self.session.cookies:
        cookies.append({
            "name": c.name,
            "value": c.value,
            "domain": c.domain,
            "path": c.path,
            "expires": c.expires
        })
    return cookies

def load_cookies(self, cookies):
    for c in cookies:
        self.session.cookies.set(**c)

我们所需要的只有五个参数,分别是:名称域名路径过期时间

4

您可以直接将cookies对象进行序列化:

cookies = pickle.dumps(session.cookies)

字典表示法遗漏了很多信息:过期时间、域名、路径等等......

这取决于您打算如何使用cookies,但是如果您没有关于过期时间的信息,例如,您应该手动实现跟踪过期的逻辑。

将库返回的对象进行pickling(序列化)可以让你轻松地重构状态,然后您可以依赖于库的实现。

显然,以这种方式,pickled(序列化的)对象的使用者需要使用相同的库。


2
session = requests.session(cookies=cookies)

对我来说,这引发了一个异常,说session()不接受参数。

我通过使用cookie.get_dict上的键/值并手动将它们添加到会话中来解决了这个问题:

session.cookies.set(cookies.keys()[n],cookies.values()[n])

使用 session.cookies.set(cookies.keys()[n],cookies.values()[n]) 对我也起作用了,谢谢! - Javier C. H.

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