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代码

猜你喜欢

转载自blog.csdn.net/godot06/article/details/81700310