【Scrapy】Cookiejar、Scrapy、Requests的Cookie管理

一、cookiejar

参考:https://www.cnblogs.com/my8100/p/cookie_login.html
用于管理HTTP cookie值、存储HTTP请求生成的cookie。
1、注:cookielib在Python3中已经改成了http.cookiejar

try:
    import cookielib
except:
    import http.cookiejar as cookielib  # 兼容python3方式

2、三个子类:

2.1 FileCookieJar(filename,delayload=None,policy=None)
创建FileCookieJar实例,检索cookie信息并将信息存储到文件中,filename是文件名。
2.2 MozillaCookieJar(filename,delayload=None,policy=None)
创建与Mozilla cookies.txt文件格式兼容的FileCookieJar实例,delayload为True时支持延迟访问访问文件,即只有在需要时才读取文件或在文件中存储数据。。
2.3 LWPCookieJar(filename,delayload=None,policy=None)
创建与libwww-perl Set-Cookie3文件格式兼容的FileCookieJar实例。

3、CookieJar()整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失,需要和本地文件交互,就用 MozillaCookjar() 或 LWPCookieJar()。
4、cookie实例

# cookie:爬虫维持登陆状态的机制
import http.cookiejar,urllib.request
cookie = http.cookiejar.CookieJar() # 声明cookiejar的对象,存放cookie的容器

#https_handle = request.HTTPSHandler() # 创建https管理器
handler = urllib.request.HTTPCookieProcessor(cookie) # 构建handler,即cookie处理器对象

opener = urllib.request.build_opener(handler) # 构建出OPener
# 请求页面,访问之后会自动保存cookie到cookiejar中
response = opener.open('http://www.zhihu.com') 
print(response.read().decode())
# 标准格式将保存的Cookie打印出来
for item in cookie:
    print(item.name + '=' + item.value)

# cookie = cookiejar.MozillaCookieJar(filename)
# cookie.save(ignore_discard=True, ignore_expires=True) # 保存cookie到文件
# ignore_discard: save even cookies set to be discarded. 
# ignore_expires: save even cookies that have expiredThe file is overwritten if it already exists
# cookie.load("httpbin-cookie.txt") # 从文件中读取cookie内容到变量

二、requests.session

参考:http://www.python-requests.org/en/latest/api/#api-cookies
1、requests基础

import requests
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"
}
response = requests.get('http://www.zhihu.com',headers=headers)
print(response.text)
print(response.cookies) #  对象
print(response.cookies.get_dict()) 
# 默认情况下,除了 HEAD, Requests 会自动处理所有重定向,可以使用响应对象的 history 方法来追踪重定向。
# Response.history 是一个 Response 对象的列表,为了完成请求而创建了这些对象。这个对象列表按照从最老到最近的请求进行排序。
print(response.history)

# if response.history: 
#    for r in response.history: 
#        self.cookies.update(r.cookies)

2、session它可以自动识别response中的set cookie,对象会在同一个Session实例发出的所有请求之间保持cookies。

# 使用登录cookie信息
session = requests.session()
session.cookies = cookielib.LWPCookieJar(filename='cookies.txt')
try:
    session.cookies.load(ignore_discard=True)
except:
	print("Cookie 加载失败")
。。。
session.cookies.save()

3、dict与cookiejar的转换

3.1、requests.utils.cookiejar_from_dict
requests.utils.add_dict_to_cookiejar(cj, cookie_dict)
Returns a CookieJar from a key/value dictionary.

参数:	
cj -- CookieJar to insert cookies into.
cookie_dict -- Dict of key/values to insert into CookieJar.
返回类型:	
CookieJar

3.2、requests.utils.dict_from_cookiejar
requests.utils.cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True)[源代码]
Returns a CookieJar from a key/value dictionary.

参数:	
cookie_dict -- Dict of key/values to insert into CookieJar.
cookiejar -- (optional) A cookiejar to add the cookies to.
overwrite -- (optional) If False, will not replace cookies already in the jar with new ones.

# 小例子
# requests.utils.add_dict_to_cookiejar对session对象设置cookie
import requests

session = requests.session()
session.cookies.set('mycookie', 'value')  # 设置会话cookies
# cookie_dict = {'mycookie', 'value'}
# s.cookies.update(cookie_dict)
session.get('https://www.baidu.com')
print(session.cookies.get_dict())  # 输出cookies
session.cookies.clear()  # 删除cookies,也可以使用s.cookies=None的方式将所有cookies删除
c = requests.cookies.RequestsCookieJar()  # 定义一个cookie对象
c.set('cookie-name', 'cookie-value')  # 增加cookie的值
session.cookies.update(c)  # 更新s的cookie
print(session.cookies.get_dict())  # 输出cookies

# 将CookieJar转为字典:
cookie_dict = requests.utils.dict_from_cookiejar(session.cookies)
print(cookie_dict)
# 将字典转为CookieJar:其中cookie_dict是要转换字典
cookies = requests.utils.cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True)
print(cookies)
# 转换完之后就可以把它赋给cookies 并传入到session中了
session.cookies = cookies
class zhihu:

    def __init__(self):
        self.session = requests.Session()
        
    def load_cookies(self):
        with open(cookies_file) as f:
        # 将字典转换成RequestsCookieJar,赋值给session的cookies.
            self.session.cookies = requests.utils.cookiejar_from_dict(pickle.load(f))
    
    def save_cookies(self):
        with open(cookies_file, 'w') as f:
        # 将cookiejar转换成字典
            pickle.dump(requests.utils.dict_from_cookiejar(self.session.cookies), f)

4、补充:

# 传递URL参数
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get("http://httpbin.org/get", params=payload)

三、scrapy与cookiejar

class scrapy.contrib.downloadermiddleware.cookies.CookiesMiddleware
Scrapy通过CookiesMiddleware会自动管理cookies,所有的requests和response都要经过它的处理。
1、配置项:
COOKIES_ENABLED 默认: True 是否启用CookiesMiddleware
COOKIES_DEBUG 默认: False 如果启用,Scrapy将记录所有在request(Cookie 请求头)发送的cookies及response接收到的cookies(Set-Cookie 接收头)。
2、单spider单cookie session:
可以在第一次发起请求(start_request)时将cookie手动添加到scrapy.Request的cookies参数中,cookie在后续的请求中会自行流转。

scrapy.Request(url, callback=self.xxx, cookies=cookies)

3、单spider多cookie session:
例如我们注册了两个账号 account1 和 account2,一个爬虫想同时登录两个账号对网站进行爬取,为了避免 Cookie 冲突(通俗地讲,登录一个会替换掉另一个),此时可以让每个账号发送的 HTTP 请求使用不同的 CookieJar,在构造 Request 对象时,可以通过 meta 参数的cookieJar 字段指定所要使用的 CookieJar,这样就维持了多个会话。

Scrapy通过使用 cookiejar Request meta key来支持单spider追踪多cookie session,默认情况下其使用一个cookie jar(session),可以传递一个标示符来使用多个,键‘cookiejar’是一个特殊的键,scrapy在meta中见到此键后会自动将cookie传递到要callback的函数中。

for i, url in enumerate(urls):
    yield scrapy.Request("http://www.example.com", meta={'cookiejar': i},
        callback=self.parse_page)

cookiejar meta key不是”黏性的(sticky)”需要在之后的request请求中接着传递。

def parse_page(self, response):
    # do some processing
    return scrapy.Request("http://www.example.com/otherpage",
        meta={'cookiejar': response.meta['cookiejar']},
        callback=self.parse_other_page)

从response header中的’Set-Cookies’中提取cookie,可以使用cookiejar.extract_cookies(response, response.request),但只会提取当前响应设置的cookie。

四、CookieMiddle源码

参考:http://www.cnblogs.com/thunderLL/p/8060279.html
CookiesMiddleware用于实现单Spider多cookie,通过在Request meta中添加cookiejar来支持单spider追踪多cookie session.默认情况下其使用一个cookie jar(session),不过您可以传递一个标示符来使用多个。
例如:

for i, url in enumerate(urls):
	yield scrapy.Request("http://www.example.com", meta={'cookiejar': i},callback=self.parse_page)

注意:meta中的cookiejar仅存储了cookiejar的标识,真是的cookiejar存储在CookiesMiddleware实例的jars属性中

import os
import six
import logging
from collections import defaultdict

from scrapy.exceptions import NotConfigured
from scrapy.http import Response
from scrapy.http.cookies import CookieJar
from scrapy.utils.python import to_native_str

logger = logging.getLogger(__name__)
class CookiesMiddleware(object):
    """This middleware enables working with sites that need cookies"""
# 中间件在Scrapy启动时实例化
    def __init__(self, debug=False):
    # jars属性是一个默认值为CookieJar对象的dict.
        self.jars = defaultdict(CookieJar)
        self.debug = debug

    @classmethod
    def from_crawler(cls, crawler):
    
        if not crawler.settings.getbool('COOKIES_ENABLED'):
            raise NotConfigured
        return cls(crawler.settings.getbool('COOKIES_DEBUG'))

    def process_request(self, request, spider):
        if request.meta.get('dont_merge_cookies', False):
            return
		# 每个cookiesjar的key都存储在 meta字典中,如果在request meta中使用了cookiejar, cookiejarkey为对应的标识,否则为None
        cookiejarkey = request.meta.get("cookiejar")
        jar = self.jars[cookiejarkey]
        # 第一次执行jars会为每个key产生一个默认值cookiejar对象.默认为{None: cookiejar}
        cookies = self._get_request_cookies(jar, request)
        
        # 把requests的cookies存储到cookiesjar中
        for cookie in cookies:
            jar.set_cookie_if_ok(cookie, request)

        # set Cookie header
        request.headers.pop('Cookie', None)
        # 添加cookiesjar中的cookies到requests header
        jar.add_cookie_header(request)
        self._debug_cookie(request, spider)

    def process_response(self, request, response, spider):
        if request.meta.get('dont_merge_cookies', False):
            return response

        # extract cookies from Set-Cookie and drop invalid/expired cookies
        cookiejarkey = request.meta.get("cookiejar")
        jar = self.jars[cookiejarkey]
        # 在请求允许的情况下(?),从response中提取cookie并入当前的cookiejar
        jar.extract_cookies(response, request)
        self._debug_set_cookie(response, spider)

        return response

    def _debug_cookie(self, request, spider):
        if self.debug:
            cl = [to_native_str(c, errors='replace')
                  for c in request.headers.getlist('Cookie')]
            if cl:
                cookies = "\n".join("Cookie: {}\n".format(c) for c in cl)
                msg = "Sending cookies to: {}\n{}".format(request, cookies)
                logger.debug(msg, extra={'spider': spider})

    def _debug_set_cookie(self, response, spider):
        if self.debug:
            cl = [to_native_str(c, errors='replace')
                  for c in response.headers.getlist('Set-Cookie')]
            if cl:
                cookies = "\n".join("Set-Cookie: {}\n".format(c) for c in cl)
                msg = "Received cookies from: {}\n{}".format(response, cookies)
                logger.debug(msg, extra={'spider': spider})

    def _format_cookie(self, cookie):
        # build cookie string
        # 对以字典或字典的列表的形式传入的cookie进行格式化
        cookie_str = '%s=%s' % (cookie['name'], cookie['value'])

        if cookie.get('path', None):
            cookie_str += '; Path=%s' % cookie['path']
        if cookie.get('domain', None):
            cookie_str += '; Domain=%s' % cookie['domain']

        return cookie_str

    def _get_request_cookies(self, jar, request):
    # 将request中cookies参数添加的cookie合并到当前的cookiejar中
        if isinstance(request.cookies, dict):
            cookie_list = [{'name': k, 'value': v} for k, v in \
                    six.iteritems(request.cookies)]
        else:
            cookie_list = request.cookies

        cookies = [self._format_cookie(x) for x in cookie_list]
        headers = {'Set-Cookie': cookies}
        # 使用刚才获取的cookie构造一个响应对象
        response = Response(request.url, headers=headers)
		# cookiejar.make_cookies方法从response中提取cookie放入当前cookiejar中.
        return jar.make_cookies(response, request)

简要分析:
request:
1、使用字典初始化多个cookies jar
2、把每个requests指定的cookies jar 提取出来
3、然后根据policy把requests中的cookies添加cookies jar
4、最后把cookies jar中合适的cookies添加到requests首部
response:
1、先从cookies jar 字典中把requests对应的cookiesjar提取出来.
2、使用extract_cookies把response首部中的cookies添加到cookies jar

你可能感兴趣的:(Python)