Scrapy+Playwright爬取电商站遇EPIPE错误及内存溢出问题
解决Scrapy+Playwright爬取时EPIPE崩溃与内存泄漏问题
问题根源
EPIPE错误源于Node.js(Playwright依赖)与浏览器实例的通信中断,结合内存持续飙升,核心原因是浏览器页面/上下文/实例未被正确销毁,资源无法释放导致内存耗尽。之前设置的NODE_OPTIONS="--max-old-space-size=8192"仅扩大了Node.js内存上限,属于治标不治本的临时方案。
修复步骤
1. 显式销毁Playwright资源
爬取完成后必须手动关闭页面/上下文,拒绝依赖自动回收:
async def parse(self, response): page = response.meta["playwright_page"] # 执行爬取逻辑... # 强制关闭页面 await page.close() # 若使用了独立上下文,同步关闭 # await response.meta["playwright_context"].close()
2. 严格限制并发实例数量
过高的并发会导致Playwright创建大量浏览器资源,直接拉满内存。修改settings.py:
# Scrapy全局并发控制 CONCURRENT_REQUESTS = 16 # Playwright上下文与页面并发限制 PLAYWRIGHT_CONCURRENT_CONTEXT = 4 PLAYWRIGHT_MAX_PAGES_PER_CONTEXT = 4
3. 启用自动页面回收
不需要保留页面对象时,通过配置让Playwright自动关闭页面:
def start_requests(self): yield scrapy.Request( url="https://target-ecommerce.com", meta={ "playwright": True, "playwright_page_coroutines": [PageCoroutine("wait_for_selector", ".product-card")], "playwright_include_page": False, # 自动关闭页面,释放资源 }, )
4. 清理页面事件监听
绑定的request/response等事件监听若未移除,会持续占用内存:
async def parse(self, response): page = response.meta["playwright_page"] # 定义监听函数 def log_request(request): self.logger.debug(f"Request: {request.url}") # 绑定监听 page.on("request", log_request) # 爬取逻辑... # 移除监听并关闭页面 page.remove_listener("request", log_request) await page.close()
5. 配合Node.js内存参数优化
保留之前的内存配置,但需结合上述修复使用:
export NODE_OPTIONS="--max-old-space-size=8192" scrapy crawl ecommerce_spider
6. 定期重启浏览器实例
若内存泄漏仍存在,可设置每爬取N条数据后重启浏览器:
from scrapy_playwright.handler import PlaywrightHandler class EcommerceSpider(scrapy.Spider): name = "ecommerce_spider" crawl_count = 0 RESTART_THRESHOLD = 1000 # 每爬1000条重启浏览器 async def parse(self, response): self.crawl_count += 1 if self.crawl_count % self.RESTART_THRESHOLD == 0: # 获取Playwright处理器并重启浏览器 handler = self.crawler.engine.downloader.middleware.middlewares[0].handler await handler.close() await handler._launch_browser() # 爬取逻辑...
验证方式
- 用
htop(Linux)或任务管理器(Windows)监控内存,若内存稳定在合理区间,说明修复有效。 - 检查日志,确认EPIPE错误不再出现。
内容的提问来源于stack exchange,提问作者Fabio
相关产品推荐
相关产品推荐

