我正在使用requests使用某些API。为了使我的帐户获得授权,我会在退出时将Cookie保存到数据库中,并在下次还原它们。
由于某些原因,请求不会用服务器返回的新值替换旧的cookie。它只是添加一个具有相同名称但值不同的新cookie。
代码:
from requests import Session
import json
local_session = Session()
local_session.cookies.update(json.loads(account.cookies))
resp = local_session.request("GET", "http://example.com/path0/")
# all cookies have been sent, server renewed some of them and return in "Set_Cookie" headers
resp = local_session.request("GET", "http://example.com/path1/")
# here requests send two cookies with same name but different values instead of one with new
数据库中的Cookie:
{
"csrf": "abcdefgh",
"session": "1234567890"
}
在上次请求中发送的Cookie:
{
"csrf": "abcdefgh",
"csrf": "ijklmnop",
"session": "1234567890"
}
我错过了什么,可以合理地解决此问题吗?
UPD。
我已经尝试在控制台中使用httpbin,并且...结果相同,它复制了cookie:
from requests import Session
with Session() as sess:
sess.cookies.update({"sessioncookie": "1234567890"})
# Same behavior with sess.cookies.set('sessioncookie','1234567890')
sess.get("https://httpbin.org/cookies/set/sessioncookie/0987654321")
print(sess.cookies.items())
# here's two pairs of cookies with same name now
答案 0 :(得分:2)
Cookie比 just 键值对复杂一些。浏览器还必须跟踪cookie的有效URL(通过有关主机名,端口号,URL路径的规则以及是否对连接进行加密的规则),以及将它们保留多长时间(它们的到期时间)。有些Cookie的设计目的是在您关闭浏览器时过期,因此没有设置过期时间。
但是,您并未将所有这些信息存储在数据库中。您仅存储了键和值,然后将cookie添加回新的requests.Session()
cookiejar时,这些cookie被标记为通用和永久。 Cookies将被发送到所有URL,永不过期,也不会因任何原因被丢弃。这种类型的Cookie有时称为 supercookie 。但是服务器仍在设置具有完整URL有效性和过期信息的常规cookie,由于这些设置,它们是不同的cookie 。
您可以通过遍历示例httpbin.org
代码中的cookie对象来确认这一点:
>>> from requests import Session
>>> sess = Session()
>>> sess.cookies.update({"sessioncookie": "1234567890"})
>>> __ = sess.get("https://httpbin.org/cookies/set/sessioncookie/0987654321")
>>> for cookie in sess.cookies: print(cookie)
...
<Cookie sessioncookie=1234567890 for />
<Cookie sessioncookie=0987654321 for httpbin.org/>
这里有两个单独的Cookie,一个用于/
(所有路径,所有域),一个单独用于httpbin.org
。两者都会发送。
您需要在数据库中保留更多信息才能重新创建“正常” cookie。如果不需要可读存储,则requests
库将cookiejar显式设置为可腌制的:
import pickle
cookiedata = pickle.dumps(session.cookies, pickle.HIGHEST_PROTOCOL)
这是二进制数据,请按原样存储。您可以使用以下方法还原Cookie:
session.cookies.update(pickle.loads(cookiedata))
否则,如果必须使用JSON,则必须存储all the Cookie
attributes:
cookie_attrs = [
"version", "name", "value", "port", "domain", "path", "secure",
"expires", "discard", "comment", "comment_url", "rfc2109"
]
cookiedata = json.dumps([
{attr: getattr(cookie, attr) for attr in cookie_attrs}
for cookie in session.cookies
])
并使用
从JSON恢复for entry in json.loads(cookiedata):
session.cookies.set(**entry)
从技术上讲,还有一个_rest
属性可以跟踪Cookie的HttpOnly
属性(在嵌套字典中),但是requests
不会忽略该属性,因为它只是适用于无法从JavaScript访问该属性设置为True
的cookie的浏览器。
原则上,domain
,path
,name
属性使cookie成为唯一,但是,如果服务器设置了具有特定ID的cookie, port
的值,然后您将序列化该cookie并稍后将其还原,现在就可以发送该cookie,而与目标URL所使用的端口无关(其他所有条件都相同),如果该站点实际上可能会破坏事情决定在其他任何地方看到这样的cookie都是一个问题。
答案 1 :(得分:1)
最后找到了解决方案,您需要指定域
from requests import Session
with Session() as sess:
#sess.cookies.update({"sessioncookie": "1234567890"})
sess.cookies.set('sessioncookie','1234567890', domain='httpbin.org')
sess.get("https://httpbin.org/cookies/set/sessioncookie/0987654321")
print(sess.cookies.items())
# Only one cookie