当前位置:  首页>> 技术小册>> Python3网络爬虫开发实战(下)

15.9 Scrapy 对接 Selenium

在Python网络爬虫领域,Scrapy作为一个快速高级的Web爬虫框架,以其强大的异步请求处理能力、易于扩展的架构和丰富的中间件支持而广受好评。然而,面对复杂的动态网页内容(如JavaScript渲染的页面),Scrapy直接通过HTTP请求和响应的方式往往难以有效抓取数据。这时,将Scrapy与Selenium结合使用便成为了一个强大的解决方案。Selenium是一个自动化测试工具,它能够模拟用户在浏览器中的操作,包括点击、滚动、填写表单等,并且能够处理JavaScript渲染的页面内容。将Scrapy与Selenium对接,可以使得Scrapy能够抓取到那些仅通过JavaScript动态生成的页面内容。

15.9.1 为什么需要Scrapy对接Selenium

  • 动态内容抓取:现代Web页面越来越多地依赖于JavaScript来动态生成内容,而Scrapy默认不支持执行JavaScript。
  • 复杂的用户交互:有些网页需要用户登录、填写表单或进行复杂的交互操作后才能显示数据,这些场景Scrapy难以直接处理。
  • AJAX请求:页面数据可能通过AJAX异步请求加载,Scrapy难以直接捕获这些数据。

15.9.2 Scrapy与Selenium结合的基本思路

Scrapy与Selenium的结合主要通过自定义Scrapy的Downloader Middleware(下载中间件)来实现。Scrapy的请求首先通过Selenium Middleware处理,由Selenium驱动浏览器执行请求,并等待页面加载完成(包括JavaScript执行完毕),然后将Selenium渲染后的页面源码作为响应返回给Scrapy,由Scrapy继续进行后续的解析和抓取工作。

15.9.3 实现步骤

1. 安装必要的库

首先,确保安装了Scrapy和Selenium。同时,还需要安装一个WebDriver,它是Selenium与浏览器交互的桥梁。以Chrome为例,需要下载ChromeDriver,并确保其路径已添加到系统的环境变量中。

  1. pip install scrapy selenium
2. 创建Selenium Middleware

在Scrapy项目中创建一个新的middleware文件,用于处理Selenium相关的操作。

  1. # myproject/middlewares.py
  2. from selenium import webdriver
  3. from selenium.webdriver.chrome.options import Options
  4. from scrapy.http import HtmlResponse
  5. class SeleniumMiddleware:
  6. def __init__(self, settings):
  7. # 配置Selenium
  8. chrome_options = Options()
  9. chrome_options.add_argument('--headless') # 无头模式
  10. chrome_options.add_argument('--no-sandbox')
  11. chrome_options.add_argument('--disable-dev-shm-usage')
  12. self.driver = webdriver.Chrome(options=chrome_options)
  13. def process_request(self, request, spider):
  14. # 使用Selenium发起请求
  15. self.driver.get(request.url)
  16. # 等待页面加载(根据需要调整等待策略)
  17. # 例如:使用WebDriverWait和expected_conditions
  18. # 这里简单使用time.sleep作为示例
  19. import time
  20. time.sleep(5) # 等待时间需根据实际情况调整
  21. # 获取渲染后的页面源码
  22. body = self.driver.page_source
  23. # 创建一个Scrapy的Response对象,并将Selenium获取的页面源码传递给它
  24. return HtmlResponse(url=request.url, body=body.encode('utf-8'), request=request, encoding='utf-8', status=200)
  25. def spider_closed(self, spider):
  26. # 爬虫结束时关闭浏览器
  27. self.driver.quit()
3. 配置Scrapy使用Selenium Middleware

在Scrapy项目的settings.py文件中,添加或修改MIDDLEWARES配置,以启用Selenium Middleware。

  1. # settings.py
  2. DOWNLOADER_MIDDLEWARES = {
  3. 'myproject.middlewares.SeleniumMiddleware': 543,
  4. # 确保SeleniumMiddleware的优先级高于Scrapy默认的Downloader Middleware
  5. }
4. 编写Spider抓取数据

编写Spider时,就像平常使用Scrapy一样编写解析函数,因为Selenium Middleware已经处理了动态内容的加载问题,所以Spider可以直接从Response中解析数据。

  1. # myproject/spiders/example_spider.py
  2. import scrapy
  3. class ExampleSpider(scrapy.Spider):
  4. name = 'example'
  5. start_urls = ['https://example.com/dynamic_page']
  6. def parse(self, response):
  7. # 从Selenium渲染后的页面中解析数据
  8. for item in response.css('selector::text').getall():
  9. yield {'data': item}
5. 运行Scrapy项目

现在,你可以像平常一样运行Scrapy项目了。Scrapy会先通过Selenium Middleware加载页面,等待JavaScript执行完毕,然后将渲染后的页面内容传递给Spider进行解析。

  1. scrapy crawl example

15.9.4 注意事项与优化

  • 性能问题:使用Selenium会显著降低爬虫的性能,因为每个请求都需要启动浏览器并等待页面加载完成。考虑使用Scrapy的Splash或Puppeteer等替代方案,它们提供了更轻量级的JavaScript渲染支持。
  • 资源管理:确保在爬虫结束时正确关闭浏览器和WebDriver,避免资源泄漏。
  • 异常处理:在Selenium Middleware中添加异常处理逻辑,以处理如页面加载超时、WebDriver异常等问题。
  • 等待策略:避免使用简单的time.sleep()来等待页面加载完成,考虑使用Selenium的WebDriverWait和expected_conditions来智能等待页面元素的出现。
  • 环境配置:确保Selenium WebDriver与浏览器版本兼容,并根据需要配置无头模式、代理等。

通过以上步骤,你可以将Scrapy与Selenium有效结合,实现对复杂动态网页的高效抓取。这种结合方式虽然在一定程度上牺牲了性能,但为爬虫开发者提供了一种强大的工具来应对日益复杂的Web环境。


该分类下的相关小册推荐: