Scrapy与Selenium/Playwright集成完全指南

📂 所属阶段:第三阶段 — 攻防演练(中间件与反爬篇)
🔗 相关章节:Downloader Middleware · 反爬对抗实战 · 数据去重与增量更新

目录

Selenium与Playwright概述

在现代Web应用中,大量数据通过JavaScript动态加载,传统的Scrapy无法直接获取这些内容。Selenium和Playwright作为浏览器自动化工具,可以完美解决这一问题。

两大工具对比

特性SeleniumPlaywright
成熟度高,社区庞大较新,发展迅速
性能一般,资源占用高优秀,启动快
API复杂度较为复杂简洁易用
等待机制需要手动实现内置智能等待
适用场景复杂兼容性需求新项目,高性能要求

何时使用浏览器自动化

  • JavaScript动态加载内容
  • 单页应用(SPA)内容获取
  • 复杂用户交互模拟
  • Canvas/WebGL内容抓取
  • 动态表单提交

Selenium集成方案

基础Selenium中间件

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from scrapy.http import HtmlResponse
import time

class SeleniumMiddleware:
    """基础Selenium中间件"""
    
    def __init__(self):
        self.driver = self._create_driver()
    
    def _create_driver(self):
        """创建配置优化的Chrome驱动"""
        chrome_options = Options()
        chrome_options.add_argument('--headless')  # 无头模式
        chrome_options.add_argument('--no-sandbox')
        chrome_options.add_argument('--disable-dev-shm-usage')
        chrome_options.add_argument('--disable-blink-features=AutomationControlled')
        
        driver = webdriver.Chrome(options=chrome_options)
        
        # 隐藏webdriver属性
        driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
        return driver
    
    def process_request(self, request, spider):
        """处理需要Selenium的请求"""
        if request.meta.get('use_selenium'):
            try:
                self.driver.get(request.url)
                
                # 等待页面加载完成
                wait = WebDriverWait(self.driver, 10)
                wait.until(EC.presence_of_element_located((By.TAG_NAME, "body")))
                
                # 简单等待动态内容加载
                time.sleep(2)
                
                body = self.driver.page_source.encode('utf-8')
                return HtmlResponse(
                    url=request.url,
                    body=body,
                    encoding='utf-8',
                    request=request
                )
            except Exception as e:
                spider.logger.error(f"Selenium error: {str(e)}")
                return request
    
    def spider_closed(self, spider):
        """爬虫关闭时清理资源"""
        if self.driver:
            self.driver.quit()

使用方法:在Scrapy设置中启用中间件,并在请求中添加meta={'use_selenium': True}

Playwright集成方案

基础Playwright中间件

from playwright.sync_api import sync_playwright
from scrapy.http import HtmlResponse

class PlaywrightMiddleware:
    """基础Playwright中间件"""
    
    def __init__(self):
        self.playwright = None
        self.browser = None
        self.setup_browser()
    
    def setup_browser(self):
        """设置Playwright浏览器"""
        self.playwright = sync_playwright().start()
        self.browser = self.playwright.chromium.launch(
            headless=True,
            args=[
                '--no-sandbox',
                '--disable-setuid-sandbox',
                '--disable-dev-shm-usage'
            ]
        )
    
    def process_request(self, request, spider):
        """处理需要Playwright的请求"""
        if request.meta.get('use_playwright'):
            try:
                page = self.browser.new_page()
                page.goto(request.url, wait_until="networkidle")
                page.wait_for_load_state("domcontentloaded")
                
                content = page.content()
                page.close()
                
                return HtmlResponse(
                    url=request.url,
                    body=content.encode('utf-8'),
                    encoding='utf-8',
                    request=request
                )
            except Exception as e:
                spider.logger.error(f"Playwright error: {str(e)}")
                return request
    
    def spider_closed(self, spider):
        """清理资源"""
        if self.browser:
            self.browser.close()
        if self.playwright:
            self.playwright.stop()

反检测策略

现代网站普遍采用反爬虫技术,我们需要采取措施避免被检测为自动化工具。

反检测配置

import random

class AntiDetectionConfig:
    """反检测配置类"""
    
    @staticmethod
    def get_stealth_args():
        """获取反检测启动参数"""
        return [
            '--disable-blink-features=AutomationControlled',
            '--disable-dev-shm-usage',
            '--disable-gpu',
            '--disable-extensions',
            '--no-sandbox',
            '--disable-web-security'
        ]
    
    @staticmethod
    def get_stealth_script():
        """获取反检测JavaScript脚本"""
        return """
        // 隐藏webdriver属性
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined,
        });
        // 模拟插件
        Object.defineProperty(navigator, 'plugins', {
            get: () => [1, 2, 3, 4, 5],
        });
        // 模拟语言
        Object.defineProperty(navigator, 'languages', {
            get: () => ['zh-CN', 'zh', 'en'],
        });
        """
    
    @staticmethod
    def get_realistic_user_agents():
        """获取真实的用户代理字符串"""
        return [
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0"
        ]

性能优化技巧

浏览器自动化工具往往是爬虫的性能瓶颈,需要合理优化。

性能优化要点

  1. 资源复用:重用浏览器实例和页面,避免重复创建
  2. 并发控制:限制同时运行的浏览器数量
  3. 缓存策略:对相同URL进行缓存
  4. 错误处理:完善的异常处理和重试机制
  5. 禁用不必要资源:如图像、视频等
class PerformanceOptimizedMiddleware:
    """性能优化中间件示例"""
    
    def __init__(self):
        self.driver_pool = []
        self.max_pool_size = 3
        self.cache = {}
    
    def get_cached_result(self, url):
        """获取缓存结果"""
        if url in self.cache:
            return self.cache[url]
        return None
    
    def cache_result(self, url, content):
        """缓存结果"""
        if len(self.cache) > 100:  # 限制缓存大小
            self.cache.pop(next(iter(self.cache)))
        self.cache[url] = content

常见问题与解决方案

浏览器启动失败

现象:ChromeDriver或Playwright无法启动浏览器 解决方案

  • 确保浏览器版本与驱动版本匹配
  • 添加--no-sandbox--disable-dev-shm-usage参数
  • 在Docker环境中增加共享内存大小

内存泄漏

现象:长时间运行后内存使用不断增加 解决方案

  • 定期重启浏览器实例
  • 使用连接池管理浏览器
  • 确保正确关闭不再使用的页面和浏览器

反爬虫检测

现象:被网站识别为自动化工具 解决方案

  • 使用反检测配置和脚本
  • 模拟人类行为(随机延迟、鼠标移动等)
  • 使用真实的用户代理和浏览器指纹
  • 控制请求频率,避免过于频繁的访问

💡 核心要点:Selenium和Playwright是处理JavaScript渲染内容的强大工具,但也是性能瓶颈。合理使用缓存、连接池和反检测策略,可以显著提升爬虫的整体效能。


🔗 相关教程推荐

🏷️ 标签云: Scrapy Selenium Playwright JavaScript渲染 动态页面 浏览器自动化 反检测 爬虫优化