Scrapy自动保存cookie并使用

每次面对要爬取的网站时,都不可避免地面对一个问题,如何使用自动保存的cookie,而不是使用人为的方式提取cookie?

所谓人为的方式,其实就是hard code。大致分为三个步骤,
(1)注册账号,登录网站。
(2)从浏览器后台提取相关cookie,多次尝试后,确定核心的几个cookie值。
(3)如果cookie过期,重复第二步。

这种硬编码的方式最大的不便就是要不停地更新cookie值。所以应该怎样才能自动保存cookie?

自动保存cookie

scrapy提供了CookiesMiddleware中间件,来自动保存cookie。原话是这样的

Cookies middleware enables working with sites that require cookies, such as those that use sessions. It keeps track of cookies sent by web servers, and send them back on subsequent requests (from that spider), just like web browsers do.

其中有两个设置和CookiesMiddleware有关,

  • COOKIES_ENABLED
  • COOKIES_DEBUG
COOKIES_ENABLED

CookiesMiddleware中间件在得到网站响应之后,会自动保存cookie值。如果将COOKIES_ENABLED置为False,表明不再使用cookie,也就是说每次请求,都不会带上cookie值。默认为True。

COOKIES_DEBUG

如果想要观察每次请求以及响应时,使用了哪些cookie,保存了哪些cookie,推荐将该值置为True,CookiesMiddleware中间件会追踪记录cookie的值。默认为False。

使用自动保存的cookie

使用cookiejar元键(meta key)关键字,可以实现发送请求时,带上之前自动保存的cookie。也分三个步骤,
(1)请求登录界面,设置cookiejar的值
(2)请求session接口,并带上cookiejar的值。
(3)请求具体接口,并带上cookiejar的值。

以模拟登录github,然后从github上爬取自己的issue列表为例来演示整个过程。

第一步,请求登录界面,设置cookiejar的值

有些网站,比如github,在提交session接口的参数里,有一些隐藏参数,需要在登录界面动态获取。
同样地,有些网站在登录界面里还要求输入验证码,这里不讨论验证码的情况,假定只需要提交简单的参数。

请求登录界面,目的是为了获取隐藏参数。发送请求时带上meta参数,给键为cookiejar的值赋值,这里赋值为1。也可以是其它数字。

1
2
3
def start_requests(self):
return [FormRequest("https://github.com/login",
meta={'cookiejar': 1}, callback=self.post_login)]

第二步,请求session接口,并带上cookiejar的值

获取到隐藏参数值之后,请求session接口,同样要带上meta参数,其键cookiejar的值来自于上一个请求里的meta,也是就是1。从程序的角度来分析,cookiejar的值使用的是上一个请求里的值,其值为1,并不是说cookie的值为1,1指代的只是一个标识符,类似于表里的id,其真正指向的是上一个请求响应之后自动保存的cookie值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
post_headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.8,en;q=0.6",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36",
"Referer": "https://github.com/",
}

def post_login(self, response):
authenticity_token = response.xpath(
'//input[@name="authenticity_token"]/@value').extract_first()
logging.info('authenticity_token=' + authenticity_token)

return [FormRequest.from_response(response,
url='https://github.com/session',
meta={'cookiejar': response.meta['cookiejar']},
headers=self.post_headers,
formdata={
'utf8': '✓',
'login': 'username',
'password': '******',
'authenticity_token': authenticity_token
},
callback=self.after_login,
dont_filter=True
)]

第三步,请求具体接口,并带上cookiejar的值

session接口的响应会重新给cookiejar的标识符指向的cookie值赋值。接下来所有的请求,都基于该cookie,而且必须带上该cookie。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
start_urls = [
'https://github.com/issues',
]

def after_login(self, response):
for url in self.start_urls:
logging.info('letter url=' + url)
yield Request(url, dont_filter=True,
meta={'dont_redirect': True,
'cookiejar': response.meta['cookiejar'],
'handle_httpstatus_list': [302]},
callback=self.parse_page, )

def parse_page(self, response):
"""这个是不使用LinkExtractor我自己手动处理链接以及下一页"""
logging.info(response.url)
for each_msg in response.xpath('//ul[@class="Msgs"]/li'):
logging.info('--------------消息分割线-----------------')
logging.info(''.join(each_msg.xpath('.//div[@class="msg"]//*/text()').extract()))
next_page = response.xpath('//li[@class="page next"]/a')
if next_page:
logging.info(u'继续处理下一页')
yield Request(response.url + next_page.xpath('@href').extract())
else:
logging.info(u"已经处理完成,没有下一页了")

总结

Scrapy通过CookiesMiddleware中间件来自动保存响应的cookie,然后通过请求时带上cookiejar元键来使用上一个响应自动保存的cookie。

avatar

chilihotpot

You Are The JavaScript In My HTML