scrapy 中间件

scrapy 中间件

架构图

下载中间件

下载器中间件是介于Scrapy的request/response处理的钩子框架。 是用于全局修改Scrapy request和response的一个轻量、底层的系统。

- 作用:

(1)引擎将请求传递给下载器过程中, 下载中间件可以对请求进行一系列处理。比如设置请求的 User-Agent,设置代理等

(2)在下载器完成将Response传递给引擎中,下载中间件可以对响应进行一系列处理。比如进行gzip解压等。

UA池:User-Agent池

  • 作用:尽可能多的将scrapy工程中的请求伪装成不同类型的浏览器身份。

  • 操作流程:

  1. 在下载中间件中拦截请求
  2. 将拦截到的请求的请求头信息中的UA进行篡改伪装
  3. 在配置文件中开启下载中间件
# 导包
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
import random


# UA池代码的编写(单独给UA池封装一个下载中间件的一个类)
class RandomUserAgent(UserAgentMiddleware):

    def process_request(self, request, spider):
        # 从列表中随机抽选出一个ua值
        ua = random.choice(user_agent_list)
        # ua值进行当前拦截到请求的ua的写入操作
        request.headers.setdefault('User-Agent', ua)


user_agent_list = [
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
    "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
    "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
    "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "
    "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
    "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "
    "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
    "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "
    "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "
    "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
    "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 "
    "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
    "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
    "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 "
    "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 "
    "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
    "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
    "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
    "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
    "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
    "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
    "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
    "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 "
    "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
    "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
    "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 "
    "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
    "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 "
    "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
]

代理池

  • 作用:尽可能多的将scrapy工程中的请求的IP设置成不同的。

  • 操作流程:

    1. 在下载中间件中拦截请求
    2. 将拦截到的请求的IP修改成某一代理IP
    3. 在配置文件中开启下载中间件
# 批量对拦截到的请求进行ip更换
# 单独封装下载中间件类
class ProxyDownloaderMiddleware(object):
    
    def process_request(self, request, spider):
        # 对拦截到请求的url进行判断(协议头到底是http还是https)
        # request.url返回值:http://www.xxx.com
        h = request.url.split(':')[0]  # 请求的协议头
        if h == 'https':
            ip = random.choice(PROXY_https)
            request.meta['proxy'] = 'https://' + ip
        else:
            ip = random.choice(PROXY_http)
            request.meta['proxy'] = 'http://' + ip


# 可被选用的代理IP
PROXY_http = [
    '153.180.102.104:80',
    '195.208.131.189:56055',
]
PROXY_https = [
    '120.83.49.90:9000',
    '95.189.112.214:35508',
]

爬虫中间件

Spider中间件是介入到Scrapy的spider处理机制的钩子框架,您可以添加代码来处理发送给 Spiders的response及spider产生的item和request。

Scrapy中selenium的应用

在通过scrapy框架进行某些网站数据爬取的时候,往往会碰到页面动态数据加载的情况发生,如果直接使用scrapy对其url发请求,是绝对获取不到那部分动态加载出来的数据值。但是通过观察我们会发现,通过浏览器进行url请求发送则会加载出对应的动态加载出的数据。那么如果我们想要在scrapy也获取动态加载出的数据,则需要使用selenium创建浏览器对象,然后通过该浏览器对象进行请求发送,获取动态加载的数据值。

小试牛刀

  1. 案例分析
  • 需求:爬取网易新闻的国内板块下的新闻数据
  • 需求分析:当点击国内超链进入国内对应的页面时,会发现当前页面展示的新闻数据是被动态加载出来的,如果直接通过程序对url进行请求,是获取不到动态加载出的新闻数据的。则就需要我们使用selenium实例化一个浏览器对象,在该对象中进行url的请求,获取动态加载的新闻数据。
  1. selenium在scrapy中使用的原理分析:

    当引擎将国内板块url对应的请求提交给下载器后,下载器进行网页数据的下载,然后将下载到的页面数据,封装到response中,提交给引擎,引擎将response在转交给Spiders。Spiders接受到的response对象中存储的页面数据里是没有动态加载的新闻数据的。要想获取动态加载的新闻数据,则需要在下载中间件中对下载器提交给引擎的response响应对象进行拦截,切对其内部存储的页面数据进行篡改,修改成携带了动态加载出的新闻数据,然后将被篡改的response对象最终交给Spiders进行解析操作。

  2. selenium在scrapy中的使用流程:

    1. 重写爬虫文件的构造方法,在该方法中使用selenium实例化一个浏览器对象(因为浏览器对象只需要被实例化一次)
    2. 重写爬虫文件的closed(self,spider)方法,在其内部关闭浏览器对象。该方法是在爬虫结束时被调用
    3. 重写下载中间件的process_response方法,让该方法对响应对象进行拦截,并篡改response中存储的页面数据
    4. 在配置文件中开启下载中间件
  3. 代码展示

爬虫文件

# -*- coding: utf-8 -*-
import scrapy
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from scrapy.http.response.html import HtmlResponse
from wangyi.items import WangyiItem


class DomesticSpider(scrapy.Spider):
    name = 'domestic'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://news.163.com/domestic/']

    def __init__(self):
        super(DomesticSpider, self).__init__()
        # 创建一个参数对象,用来控制chrome以无界面模式打开
        chrome_options = Options()
        chrome_options.add_argument('--headless')
        chrome_options.add_argument('--disable-gpu')
        # 实例化一个浏览器对象(实例化一次)
        self.bro = webdriver.Chrome(executable_path='chromedriver.exe', chrome_options=chrome_options)

    # 必须在整个爬虫结束后,关闭浏览器
    def closed(self, spider):
        self.bro.quit()

    def parse(self, response: HtmlResponse):
        div_list = response.xpath('//div[@class="ndi_main"]/div')
        for div in div_list:
            detail_url = div.xpath('./a/@href').extract_first()
            news_title = div.xpath('./div/div[@class="news_title"]/h3/a/text()').extract_first()
            keywords = div.xpath('.//div[@class="keywords"]/a/text()').extract()
            keywords = ','.join(keywords)
            item = WangyiItem()
            item['news_title'] = news_title
            item['keywords'] = keywords
            yield item

中间件文件

import time
from scrapy.http.response.html import HtmlResponse


class SeleniumDownloaderMiddleware(object):

    def process_response(self, request, response, spider):
        url_list = ('http://news.163.com/domestic/', 'http://news.163.com/world/', 'http://news.163.com/air/',
                    'http://war.163.com/', 'https://news.163.com/domestic/', 'https://news.163.com/world/',
                    'https://news.163.com/air/',
                    'https://war.163.com/')
        if request.url in url_list:
            spider.bro.get(url=request.url)
            time.sleep(2)
            js = 'window.scrollTo(0,document.body.scrollHeight)'
            spider.bro.execute_script(js)
            time.sleep(2)  # 一定要给与浏览器一定的缓冲加载数据的时间
            spider.bro.execute_script(js)
            time.sleep(2)
            spider.bro.find_element_by_class_name('load_more_btn').click()
            time.sleep(2)
            page_text = spider.bro.page_source  # 页面数据就是包含了动态加载出来的新闻数据对应的页面数据
            # 篡改响应对象
            return HtmlResponse(url=spider.bro.current_url, body=page_text, encoding='utf-8', request=request)
        else:
            return response

配置文件

DOWNLOADER_MIDDLEWARES = {
   'wangyi.middlewares.SeleniumDownloaderMiddleware': 543,
}

posted @ 2019-08-11 16:05  写bug的日子  阅读(198)  评论(0编辑  收藏  举报