CrawlSpider带headers和cookies请求,解决302重定向和禁止访问问题
一、前言
CrawlSpider是抓取整个网站的非常适合的爬虫。CrawlSpider:Scrapy:Spider源码分析扩展-CrawlSpider使用分析(详解)
然而,在使用的使用,我们发现只使用基本的rules和满足rules的callback函数来爬取指定规则页面的数据,似乎很多网站都无法适用。因为我们知道很多网站现在独有反爬虫机制,如判断请求的headers和cookies,所有如我们之前使用的spider一样我们需要携带headers和cookies作为请求参数一起向网站请求参数。
然而,在crawlspider中,只写rule和处理满足rules的回调函数,似乎不能让我们像之前一样随便的添加我们想要的参数(headers,cookies等),因为这两个地方都没有我们添加参数的位置:
rules = (
# 提取匹配 'category.php' (但不匹配 'subsection.php') 的链接并跟进链接(没有callback意味着follow默认为True)
Rule(LinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),
# 提取匹配 'item.php' 的链接并使用spider的parse_item方法进行分析
Rule(LinkExtractor(allow=('item\.php', )), callback='parse_item', follow=True),
)
def parse_item(self, response):
for each in response.xpath("//div[@class='main-top']"):
......
二、策略分析
CrawlSpider是继承Spider,原则上spider能完成的crawlspider肯定一样能完成的,所以现在不能添加只能说明我们没有找到相应的入口 。那入口怎么找呢?我们回顾下在spider中,我们是在哪里加的cookies和headers等参数呢?是不是在scrapy.Request()中,所以我们的目标要定位在,如何在合适位置合适时机发起一个带参数的Request()?
为什么说是合适时机、合适位置呢?---->因为我们的crawlspider是要根据指定规则rules来爬取整个网站的,如果我们随意的发起一个Request请求,一来会打断crawlspider全站循环找寻满足规则的url解析;二是其实你也很难找到一个插入crawlspider执行的入口,因为你根本不知道什么时候发起request。
那怎么做呢?答案,我们要才从源码着手,我这篇文章介绍了crawlspider的用法和源码执行流程:Scrapy:Spider源码分析扩展-CrawlSpider使用分析(详解)。crawlspider执行流程:
以上是我自己画的流程图,可能学艺不精,仅供参考,欢迎指教。
那么怎样才能给crawlspider项目带上cookies和headers呢?
1.第一步,重写start_request():
我们要明白,crawlspider继承基类是spider,所以它的开始入口也是start_request(),然后默认回调parse。
那么找到了入口,其实就有事可做了,我们可以在这里做登录等操作,来获取cookies,并把cookies,headers等值带上请求网络,这样就可以通过网站简单通过headers判断禁止爬取的限制,因为我们把爬虫伪装成浏览器请求了。
因为scrapy会记录我们的cookies所以,通过这一步给我们crawlspider加上请求必须要带的cookies和headers后,之后的请求就是有cookies状态了(scrapy会记住cookies,但是似乎不会记住headers,如果在后面还有发起请求Request,那么后面还是要带上headers的)
所以在这一步,我们就可以加上我们的headers和cookies,在生成的crawlspider项目中,重写start_request()
def start_requests(self):
# 这里就不在演示登录,想看怎么做的可以看我之前的知乎豆瓣实战文章
# cookies 是从浏览器复制的,只要复制name和value在通过dict连接一起即可
cookie=(
{'name':'HMACCOUNT','value':'BC8D793E3952254D'},
{'name': 'Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6', 'value': '1534264754'},
{'name': 'Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6', 'value': '1534260003'},
{'name': 'JSESSIONID', 'value': 'ABAAABAAAFCAAEGDE2646250B4936B8E77A4CF0D1A8D806'},
{'name': 'LGRID', 'value': '20180815003911-92a7ab11-9fe0-11e8-bc1d-525400f775ce'},
{'name': 'LGSID', 'value': '20180815003903-8deecd02-9fe0-11e8-bc1d-525400f775ce'},
{'name': 'LGUID', 'value': '20180815003903-8deece64-9fe0-11e8-bc1d-525400f775ce'},
{'name': 'PRE_HOST', 'value': ''},
{'name': 'PRE_LAND', 'value': 'https%3A%2F%2Fwww.lagou.com%2F'},
{'name': 'PRE_SITE', 'value': 'https%3A%2F%2Fwww.lagou.com%2F'},
{'name': 'PRE_UTM', 'value': ''},
{'name': '_ga', 'value': 'GA1.2.486468146.1534264745'},
{'name': '_gat', 'value': '1'},
{'name': '_gid', 'value': 'GA1.2.232571625.1534264745'},
{'name': 'index_location_city', 'value': '%E6%9D%AD%E5%B7%9E'},
{'name': 'user_trace_token', 'value': '20180815003903-8deecbfc-9fe0-11e8-bc1d-525400f775ce'}
)
cookies=dict()
for cook in cookie:
cookies[cook['name']]=cook['value']
# scrapy.Request发起请求,callback一定要是parse,不然我们的crawlspider就乱套了
# 并在请求中,带上我们的cookies和headers
yield scrapy.Request(url=self.start_urls[0],
cookies=cookies,
headers=self.heasers,
callback=self.parse,
dont_filter=True)
这样就可以满足发起请求带上cookies和headers了,cookies对需要登录才能访问的网站是很管用的,因为scrapy会记住cookies,所以这里带上之后,整个scrapy都是有cookies状态的请求。但是还要记住一点callback函数一定是parse,不能变,parse在crawlspider源码中有它非常重要的桥梁作用。
2.第二步,我们还有修改(重写)一个地方_build_request()。
为什么是_build_request?在上面crawlspider执行流程中我写过,crawlspider源码执行中,_build_request是crawlspider对每一个url发起请求的方法,里面明显有一个Request回调方法,有Request就可以给我们的请求带上headers和其他请求要的参数:
def _build_request(self, rule, link):
r=Request(url=link.urlcallback=self._response_downloaded)
r.meta.update(rule=rule,link_text=link.text)
return r
为什么要重新它?这个方法已经可以发起请求了,为什么还要重写?因为scrapy会记录请求的cookies,但是它不会记录请求的headers,网站对headers有判断的话,哪怕一次Request中不带headers它也会禁止你的爬取。所以我们要给每一个Ruquest加上headers。
很简单,把方法复制到crawlspider项目中重写,并在Ruquest请求中带上headers即可:
def _build_request(self, rule, link):
# 给Request带上headers,因为scrapy会记录cookies,此处不带cookies也可。
# 当然给Request带上cookies操作也很简单,加上cookies=获得cookies即可
r=Request(url=link.url,headers=self.headers,callback=self._response_downloaded)
r.meta.update(rule=rule,link_text=link.text)
return r
修改这两个方法,皆可以给我们的crawlspider请求带上headers和cookies,这样就网站就不会直接重定向我们的爬取请求了。
3,完整源码
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy import Request
class LagouSpider(CrawlSpider):
name = 'lagou'
allowed_domains = ['www.lagou.com']
start_urls = ['https://www.lagou.com/']
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0",
"HOST": "www.lagou.com"
}
# 抓取urls的规则--目的是找到招聘职位的详情页面,然后爬取职位的详细数据
# 如https://www.lagou.com/jobs/3959551.html,这是一个详细python招聘职位的描述
# 那怎么才能从lagou这个站中那么多urls找到类似上述的url呢?所以要给定抓取urls规则或者范围
# 分析lagou发现从以下两个地方可以进入招聘职位页面:
# 1、招聘分类:这里面有各类招聘的所有职位; https://www.lagou.com/zhaopin/***** 招聘页面urls共性
# 2、招聘需要公司发布,我们从公司页面也可以抓取到所有的职位; https://www.lagou.com/gongsi/** 公司页面共性
rules = (
# 根据上面分析,可以写一些规则也圈定抓取的urls
# 爬取urls中有 zhaopin/的urls,并却对这类页面中的所有的url进行跟进follow
Rule(LinkExtractor(allow=('.*/zhaopin/.*',)),follow=True),
# 也可以再加规则,如果发现公司页面也进行follow然后找到公司中的发布的职位,也可以爬取到
Rule(LinkExtractor(allow=(r'.*/gongsi/.*')),follow=True),
# 对页面进行根据时,如果发现/jobs/.*.html的url则说明找到了要爬取的页面,那么调用parse_item进行解析
Rule(LinkExtractor(allow=r'/jobs/.*.html'), callback='parse_item', follow=True),
)
# 符合规则的进行页面爬取解析response
def parse_item(self, response):
print(response)
item = {}
#i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
#i['name'] = response.xpath('//div[@id="name"]').extract()
#i['description'] = response.xpath('//div[@id="description"]').extract()
return item
def start_requests(self):
cookie=(
{'name':'HMACCOUNT','value':'BC8D793E3952254D'},
{'name': 'Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6', 'value': '1534264754'},
{'name': 'Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6', 'value': '1534260003'},
{'name': 'JSESSIONID', 'value': 'ABAAABAAAFCAAEGDE2646250B4936B8E77A4CF0D1A8D806'},
{'name': 'LGRID', 'value': '20180815003911-92a7ab11-9fe0-11e8-bc1d-525400f775ce'},
{'name': 'LGSID', 'value': '20180815003903-8deecd02-9fe0-11e8-bc1d-525400f775ce'},
{'name': 'LGUID', 'value': '20180815003903-8deece64-9fe0-11e8-bc1d-525400f775ce'},
{'name': 'PRE_HOST', 'value': ''},
{'name': 'PRE_LAND', 'value': 'https%3A%2F%2Fwww.lagou.com%2F'},
{'name': 'PRE_SITE', 'value': 'https%3A%2F%2Fwww.lagou.com%2F'},
{'name': 'PRE_UTM', 'value': ''},
{'name': '_ga', 'value': 'GA1.2.486468146.1534264745'},
{'name': '_gat', 'value': '1'},
{'name': '_gid', 'value': 'GA1.2.232571625.1534264745'},
{'name': 'index_location_city', 'value': '%E6%9D%AD%E5%B7%9E'},
{'name': 'user_trace_token', 'value': '20180815003903-8deecbfc-9fe0-11e8-bc1d-525400f775ce'}
)
cookies=dict()
for cook in cookie:
cookies[cook['name']]=cook['value']
print(cookies)
yield scrapy.Request(url=self.start_urls[0],
cookies=cookies,
headers=self.headers,
callback=self.parse,
dont_filter=True)
def _build_request(self, rule, link):
r=Request(url=link.url,headers=self.headers,callback=self._response_downloaded)
r.meta.update(rule=rule,link_text=link.text)
return r
文章思路来源:CrawlSpider解决302问题。https://blog.csdn.net/qq_26582987/article/details/79703317
以上仅供参考,如需要源码或者交流可加微信:第一行Python代码