欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CrawlSpider带headers和cookies请求,解决302重定向和禁止访问问题

程序员文章站 2022-05-06 16:09:00
...

一、前言

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带headers和cookies请求,解决302重定向和禁止访问问题

以上是我自己画的流程图,可能学艺不精,仅供参考,欢迎指教。

那么怎样才能给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代码

CrawlSpider带headers和cookies请求,解决302重定向和禁止访问问题