Python爬虫(二十二)中间件-拦截请求

248 阅读5分钟

11111.png

中间件这绝对是个老生常谈的话题了。遥想当年,第一次接触中间件的概念还是在使用ThinkPHP5.1框架中,后来在使用laravel5.8框架的时候,也是用了框架中的中间件,二者是何其的相似~

 

跑题了跑题了,今天看的是python框架scrapy的中间件。虽然语言不同,但是中间件的原理都是相同的。这一点没什么质疑的。

 

Scrapy的下载中间件分为:爬虫中间件、下载中间件

 

下载中间件scrapy框架为我们定义了拦截请求(process_request)和拦截响应(process_response)、拦截异常请求(process_exception)其余的方法没啥用,可以删除了。

 

接下来,我们来从字面分析一下三个请求的使用情况。.

拦截请求,当然就是拦截所有请求。

拦截异常请求,就是拦截异常的请求,拦截完异常的请求我们该怎么做呢?正常做法是处理异常请求,再次发起请求,那么我们如何处理异常请求呢?通常都是使用UA伪装。那么,按照正常逻辑,我们应该在拦截异常请求这里设置UA伪装。但是我的习惯一般是在拦截请求这里设置UA伪装。具体UA伪装如何设置,这个稍后再说。

拦截响应,对请求响应进行拦截。

 

一:设置UA池:

这里我们需要尽可能的给不同的请求提供不同的请求载体,因此我们这里可能需要定义一个UA池(其实也就是个列表)

这个去百度上查就好了,有很多类似的文章

我这里参考的是:blog.csdn.net/weixin_4909…

随机UA伪装:

def process_request(self, request, spider):
    # UA伪装
    request.headers['User-Agent'] = random.choice(self.user_agent_list)
    return None

 

二:设置代理池:

一般请求发生异常基本上就是我们的请求IP被封禁了,那我们就需要在请求异常拦截中设置代理IP再次进行请求,那么按照正常道理来说,我们可能就需要准备多个ip,那我们这里就需要封装一个代理IP池,关于代理ip请求的部分请移步《Python爬虫(十二)代理在爬虫中的应用

Ip池如下,我们这里区分http与https请求链接

PROXY_http = [
    '121.41.166.74:8877',
    '182.139.110.14:9000',
]
PROXY_https = [
    '103.37.141.69:80',
    '106.75.171.235:8080',
]

 

接下来,我们在请求异常拦截中对异常的请求做一下处理在转发至请求拦截:

def process_exception(self, request, exception, spider):
    if request.url.split(':')[0] == 'http':
        request.meta['proxy'] = 'http://'+random.choice(self.PROXY_http)
    else:
        request.meta['proxy'] = 'https://' + random.choice(self.PROXY_https)
    # 将修正之后的请求对象进行重新发送
    return request

 

然而,我们测试的时候一般情况下可能测试不到请求异常的情况,那我们直接将在异常拦截部分的代码直接搬到请求拦截即可。

def process_request(self, request, spider):
    # UA伪装
    request.headers['User-Agent'] = random.choice(self.user_agent_list)
    # 显然,我们测试的时候一般情况下可能测试不到请求异常的情况,那我们直接将在异常拦截部分的代码直接搬到请求拦截即可。
    if request.url.split(':')[0] == 'http':
        request.meta['proxy'] = 'http://'+random.choice(self.PROXY_http)
    else:
        request.meta['proxy'] = 'https://' + random.choice(self.PROXY_https)
    return None

 

到这儿,中间件的准备完成了,但是这并不意味着我们直接可以使用他了。使用它之前,我们仍需要在settings.py中开启一下中间件。

我们目前使用的是下载中间件:

DOWNLOADER_MIDDLEWARES = {
   'scrpayProject.middlewares.ScrpayprojectDownloaderMiddleware': 543,
}

 

接下来,我们来定义一下爬虫文件:很简单,就是将页面的内容写入一个html文件中:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
@Time    : 2022/3/31 18:01
@Author  : camellia
@Email   : 805795955@qq.com
@File    : beike.py
@Software: PyCharm

import scrapy
import asyncio

class BeikeSpider(scrapy.Spider):
    name = 'middle'
    start_urls = ['http://www.baidu.com/s?wd=ip']

    def parse(self, response):
        print(response.body.decode(response.encoding))
        page_text = response.body.decode(response.encoding)
        with open('ip.html','w',encoding='utf-8') as fp:
            fp.write(page_text)

 

执行一下:

scrapy crawl middle

 

打开项目根目录,就会出现一个ip.html的文件。双击打开,理论上应该是这个样子的:

微信截图_20220702110733.png

 

但是呢,我玩的次数比较多,爬取到的页面长这个样子:

微信截图_20220702110638.png

 

且玩且珍惜吧。

 

最后放一下中间件的完整代码:

from scrapy import signals
import random
class ScrpayprojectDownloaderMiddleware:
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the downloader middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    user_agent_list = [
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60',
        'Opera/8.0 (Windows NT 5.1; U; en)',
        'Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50',
        'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50',
        'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
        'Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2 ',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
        'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36',
        'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER',
        'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)',
        'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0',
        'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0) ',
    ]

    def process_request(self, request, spider):
        # UA伪装
        request.headers['User-Agent'] = random.choice(self.user_agent_list)
        # 显然,我们测试的时候一般情况下可能测试不到请求异常的情况,那我们直接将在异常拦截部分的代码直接搬到请求拦截即可。
        if request.url.split(':')[0] == 'http':
            request.meta['proxy'] = 'http://'+random.choice(self.PROXY_http)
        else:
            request.meta['proxy'] = 'https://' + random.choice(self.PROXY_https)
        return None

    def process_response(self, request, response, spider):
        return response

    PROXY_http = [
        '121.41.166.74:8877',
        '182.139.110.14:9000',
        '223.82.60.202:8060',
        '119.165.64.183:9999',
        '120.24.33.141:8000',
        '112.6.117.178:8085'
    ]
    PROXY_https = [
        '103.37.141.69:80',
        '106.75.171.235:8080',
        '120.71.3.208:30003',
        '58.220.95.42:10174',
        '116.62.181.105:38888',
        '1.71.132.64:30003',
        '117.28.95.138:57114',
    ]
    def process_exception(self, request, exception, spider):
        if request.url.split(':')[0] == 'http':
            request.meta['proxy'] = 'http://'+random.choice(self.PROXY_http)
        else:
            request.meta['proxy'] = 'https://' + random.choice(self.PROXY_https)
        # 将修正之后的请求对象进行重新发送
        return request

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

 

有好的建议,请在下方输入你的评论。