scrapy Middleware 和 selenium 结合

本文介绍如何使用Scrapy和Selenium结合爬取某些无法直接抓取的网页内容。针对特定网站的反爬措施,通过Selenium模拟浏览器行为,解决爬虫获取数据不完整的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

scrapy Middleware 和 selenium 结合

scrapy框架的结构图和顺序图

在这里插入图片描述
在这里插入图片描述

middleware结合selenium

  • 问题描述:某些网页无法正常通过爬虫去爬取或者仅仅通过爬虫获取到的网页中信息不全,这时需要用selenium模拟浏览器返回最终渲染后的页面。

  • 遇到的困难:在新创建了一个ChromeDownloaderMiddleware,但是最终并没有完成预期(返回浏览器渲染之后的页面,然后进行爬取),download在下载过程中遇到403,之后爬虫就结束了。

代码

# 如果下载器返回的html中的纯文本不足二百个字符,那就启动selenium得到渲染之后的页面
class ChromeDownloaderMiddleware:

    def __init__(self):
        print('Chrome driver begin...')
        options = webdriver.ChromeOptions()
        options.add_argument('--headless')  # 设置无界面
        options.add_experimental_option('excludeSwitches', ['enable-automation'])
        options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2,
                                                  "profile.managed_default_content_settings.flash": 0})
        self.webdriver = webdriver.Chrome(executable_path=r'E:\python配置文件\chromedriver', options=options)

    def __del__(self):
        self.webdriver.close()

    # 这个处理响应到底在程序运行过程中的哪一步?
    def process_response(self, response, requset,spider):
        print('Chrome driver begin...')
        try:
            print('Chrome driver begin...')
            pure_text = BeautifulSoup(response.body).get_text()
            if pure_text.len < 200:
                # self.webdriver.get(url=response.url)
                # wait = WebDriverWait(self.webdriver, timeout=20)
                return HtmlResponse(url=response.url, body=self.webdriver.page_source, encoding='utf-8',
                                    )  # 返回selenium渲染之后的HTML数据
            else:
                return response
        except TimeoutException:
            return HtmlResponse(url=response.url, encoding='utf-8', status=500)
        finally:
            print('Chrome driver end...')

程序log

在这里插入图片描述

解决方案

分析scrapy框架在运行过程中的整个流程

详见文章最初的框架图和官网的解释。

分析middleware种类

  • Spider middleware
    spider中间件是一个与Scrapy的spider处理机制挂钩的框架,在这个框架中,可以插入自定义功能来处理发送给spider进行处理的响应,以及处理从spider生成的请求和项目。
    在这里插入图片描述
    其实看代码也能看出其作用的位置:
    在这里插入图片描述

  • Downloader Middleware
    下载器中间件是介于Scrapy的request/response处理的钩子框架。 是用于全局修改Scrapy request和response的一个轻量、底层的系统。
    在这里插入图片描述
    看代码也能看出作用的位置:
    在这里插入图片描述

分析自己的代码

理论原因:没有搞清楚spider中间件和download中间件作用的位置。错误的将ChromeDownloaderMiddleware放在了spider中间件的位置上。

在这里插入图片描述

流程原因:
  • 在1过程中,由于网页的限制措施,download相当于并没有爬取到相应的response,这也是为什么日志中出现了2021-04-10 11:02:20 [scrapy.spidermiddlewares.httperror] INFO: Ignoring response <403 https://www.XXXX.com/home/>: HTTP status code is not handled or not allowed
  • 既然1过程都没有返回response给引擎,那引擎自然也不会给spider相应的response。因此,在2过程中部署的ChromeDownloaderMiddleware也就不会发挥作用。与程序展现出来的效果一致。

在这里插入图片描述

代码修改

直接将ChromeDownloaderMiddleware放到DOWNLOADER_MIDDLEWARES中,在setting.py中打开即可。成功爬到数据。
在这里插入图片描述

### 结合ScrapySelenium构建高效稳定爬虫项目 #### 使用场景分析 当面对既包含静态内容又含有需动态加载的数据页面时,采用Scrapy+Selenium组合方案能显著提升效率。具体而言,Scrapy负责快速抓取网页中的静态部分,而Selenium则专注于处理依赖JavaScript渲染或交互行为产生的动态内容[^3]。 #### 实现方法概述 为了实现两者的无缝集成,在实际开发过程中通常会创建自定义下载中间件来控制请求流程: - 对于常规HTML资源链接,继续沿用高效的Twisted异步I/O机制由Scrapy直接发起HTTP GET/POST请求; - 遇到AJAX接口调用或是需要模拟浏览器环境的情况,则交由Selenium实例化WebDriver对象完成相应操作并返回最终DOM树给Spider解析器进一步提取所需信息。 #### 关键技术要点说明 ##### 安装必要的库文件 确保安装了最新版本的`scrapy`, `selenium`以及对应平台下的Webdriver驱动程序(如ChromeDriver)。可通过pip工具轻松搞定这些依赖项: ```bash pip install scrapy selenium webdriver_manager ``` ##### 编写Downloader Middleware类 通过继承`scrapy.downloadermiddlewares.DownloaderMiddleware`基类来自定义逻辑判断哪些URL应该交给Selenium去访问。下面是一个简单的例子展示如何区分不同类型的页面从而决定采取何种方式获取响应体: ```python from selenium import webdriver import time from scrapy.http import HtmlResponse class SeleniumDownloadMiddleware(object): def __init__(self): options = webdriver.ChromeOptions() options.add_argument('--headless') # 设置无头模式运行chrome self.driver = webdriver.Chrome(options=options) @classmethod def from_crawler(cls, crawler): middleware = cls() crawler.signals.connect(middleware.spider_closed, signal=signals.spider_closed) return middleware def process_request(self, request, spider): if 'dynamic' in request.meta.get('type', ''): # 判断是否为动态页 self.driver.get(request.url) time.sleep(2) # 等待js执行完毕 body = str.encode(self.driver.page_source) return HtmlResponse(url=self.driver.current_url,body=body,status=200,encoding='utf-8') def spider_closed(self): self.driver.quit() ``` 此段代码实现了基本的功能需求——即针对标记有特定元属性(`meta`)字段值为'dynamic'的目标网址启动真实的浏览器进程加载完整文档后再传递回给后续pipeline组件做持久化存储等动作;而对于其他普通GET请求依旧保持原有的工作流不变。 ##### 修改Item Pipeline配置 考虑到某些特殊业务场景下可能还需要额外处理经由Selenium捕获下来的富媒体素材(图片、视频),因此建议适当调整item pipeline环节以适应新的输入源特性变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值