HOOOS

Scrapy 遇上 Selenium:解锁动态网页抓取新姿势

0 8 爬虫小王子 ScrapySelenium动态网页爬虫
Apple

在网络爬虫的世界里,Scrapy 框架以其高效、灵活的特点,赢得了众多开发者的青睐。然而,当面对那些需要 JavaScript 渲染才能完整呈现内容的动态网页时,Scrapy 往往显得有些力不从心。这时候,将 Scrapy 与 Selenium 这两大神器结合起来,就能发挥出 1+1>2 的威力,轻松应对各种复杂的动态网页抓取场景。

为什么 Scrapy 需要 Selenium?

Scrapy 本身是一个强大的爬虫框架,它通过发送 HTTP 请求并解析 HTML 响应来抓取网页内容。但是,对于那些 heavily 依赖 JavaScript 渲染的网页,Scrapy 只能获取到未经渲染的 HTML 源代码,而无法直接抓取到 JavaScript 动态生成的内容。这就像你试图通过一张蓝图来了解一座建筑的内部结构,但蓝图上并没有标注那些后来添加的家具和装饰。

Selenium 则是一个自动化测试工具,它可以模拟用户在浏览器中的各种操作,包括点击、滚动、输入等。最重要的是,Selenium 可以执行 JavaScript 代码,并获取渲染后的网页内容。因此,通过 Selenium,我们可以让浏览器先将网页渲染完毕,然后再将渲染后的 HTML 源代码交给 Scrapy 进行解析和提取。

Scrapy + Selenium:工作原理

Scrapy 与 Selenium 结合的工作流程大致如下:

  1. Scrapy 发起请求: Scrapy 像往常一样,根据 Spider 中定义的规则,向目标网站发起 HTTP 请求。
  2. 下载器中间件介入: 当 Scrapy 的下载器中间件接收到请求时,它会判断该请求是否需要使用 Selenium 进行处理。通常,我们会根据 URL 或其他条件来判断。
  3. Selenium 驱动浏览器: 如果需要使用 Selenium,下载器中间件会启动一个浏览器实例(例如 Chrome 或 Firefox),并使用 Selenium 提供的 API 将请求的 URL 加载到浏览器中。
  4. 等待页面渲染完成: Selenium 会等待页面上的 JavaScript 代码执行完毕,确保所有动态内容都已渲染完成。我们可以设置一个显式等待,等待特定的元素出现,或者简单地设置一个固定时间的等待。
  5. 获取渲染后的 HTML: 一旦页面渲染完成,Selenium 就可以获取到渲染后的 HTML 源代码。
  6. 将 HTML 返回给 Scrapy: 下载器中间件将 Selenium 获取到的 HTML 源代码作为响应返回给 Scrapy。
  7. Scrapy 解析 HTML: Scrapy 像处理静态网页一样,使用 Spider 中定义的规则来解析 HTML 响应,提取需要的数据。

实战演练:以抓取某电商平台商品信息为例

假设我们需要抓取某电商平台上的商品信息,但是该平台上的商品价格和库存信息是通过 JavaScript 动态加载的。为了解决这个问题,我们可以使用 Scrapy 和 Selenium 结合的方式来实现。

1. 安装必要的库

首先,我们需要安装 Scrapy 和 Selenium 这两个库。可以使用 pip 命令来安装:

pip install scrapy selenium

除了 Selenium 库之外,还需要安装一个浏览器驱动。这里以 Chrome 为例,你需要下载 ChromeDriver,并将其添加到系统的 PATH 环境变量中。ChromeDriver 的下载地址为:https://chromedriver.chromium.org/downloads

2. 创建 Scrapy 项目

使用 Scrapy 提供的命令行工具创建一个新的 Scrapy 项目:

scrapy startproject ecom_spider
cd ecom_spider

3. 定义 Spider

spiders 目录下创建一个名为 ecom_spider.py 的 Spider 文件,并定义 Spider 的规则:

import scrapy
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from scrapy.selector import Selector

class EcomSpider(scrapy.Spider):
 name = 'ecom'
 allowed_domains = ['example.com'] # 替换为实际域名
 start_urls = ['https://example.com/products'] # 替换为实际起始 URL

 def __init__(self):
 chrome_options = Options()
 chrome_options.add_argument("--headless") # 使用无头模式,不在前台显示浏览器
 self.driver = webdriver.Chrome(options=chrome_options)
 super(EcomSpider, self).__init__()

 def closed(self, reason):
 self.driver.close()

 def parse(self, response):
 self.driver.get(response.url)
 # 等待页面加载完成,可以根据实际情况调整等待时间
 self.driver.implicitly_wait(10)

 html = self.driver.page_source
 sel = Selector(text=html)

 # 提取商品信息
 products = sel.xpath('//div[@class="product"]') # 替换为实际的商品元素 XPath
 for product in products:
 item = {}
 item['title'] = product.xpath('.//h2[@class="product-title"]/text()').get() # 替换为实际的商品标题 XPath
 item['price'] = product.xpath('.//span[@class="product-price"]/text()').get() # 替换为实际的商品价格 XPath
 item['stock'] = product.xpath('.//span[@class="product-stock"]/text()').get() # 替换为实际的商品库存 XPath
 yield item

代码解释:

  • __init__ 方法:初始化 Selenium 的 WebDriver 实例。这里使用了 Chrome 的无头模式,即不在前台显示浏览器窗口。这样可以节省资源,提高爬取效率。
  • closed 方法:在 Spider 关闭时,关闭 WebDriver 实例,释放资源。
  • parse 方法:这是 Spider 的核心方法,用于处理每一个响应。在这个方法中,我们首先使用 Selenium 加载网页,然后等待页面渲染完成。之后,我们使用 scrapy.selector.Selector 将渲染后的 HTML 源代码转换为一个 Selector 对象,就可以像处理静态网页一样,使用 XPath 或 CSS 选择器来提取需要的数据了。

4. 配置 Scrapy

settings.py 文件中,我们需要配置 Scrapy,启用 Selenium 下载器中间件。首先,定义一个下载器中间件:

class SeleniumDownloaderMiddleware:
 def __init__(self):
 chrome_options = Options()
 chrome_options.add_argument("--headless")
 self.driver = webdriver.Chrome(options=chrome_options)

 def __del__(self):
 self.driver.quit()

 def process_request(self, request, spider):
 if 'selenium' in request.meta:
 self.driver.get(request.url)
 # 等待页面加载完成,可以根据实际情况调整等待时间
 self.driver.implicitly_wait(10)
 html = self.driver.page_source
 return scrapy.http.HtmlResponse(url=request.url, body=html.encode('utf-8'), encoding='utf-8', request=request)
 return None

然后在 settings.py 中启用这个中间件,并设置优先级:

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

5. 运行 Spider

使用 Scrapy 提供的命令行工具运行 Spider:

scrapy crawl ecom

优化技巧:

  • 使用无头浏览器: 使用无头浏览器(例如 Chrome 的 headless 模式)可以节省资源,提高爬取效率。
  • 显式等待: 使用显式等待可以确保页面上的 JavaScript 代码执行完毕,避免因页面未完全加载而导致数据提取失败。Selenium 提供了多种显式等待的方法,例如 wait.until(EC.presence_of_element_located((By.ID, 'element_id')))
  • 使用连接池: 如果需要抓取大量的网页,可以使用连接池来复用 WebDriver 实例,减少创建和销毁 WebDriver 实例的开销。
  • 使用代理: 为了避免被目标网站封禁 IP,可以使用代理 IP 来隐藏你的真实 IP 地址。

总结

通过 Scrapy 和 Selenium 的结合,我们可以轻松应对各种复杂的动态网页抓取场景。希望本文能够帮助你更好地理解和使用 Scrapy 和 Selenium,提升你的爬虫技能。记住,实践是检验真理的唯一标准,多动手尝试,你才能真正掌握这些技术。

点评评价

captcha
健康