在本指南中,我将带你了解如何使用 Elixir 进行网页抓取。我们会介绍基础内容------设置环境、必备工具和最佳实践。我还会讲解从分页页面和 JavaScript 渲染页面中抓取数据的技巧。让我们看看为什么 Elixir 正在成为网页抓取的热门选择!
为什么选择 Elixir 进行网页抓取?
Elixir 的优势在于其可扩展性和并发能力。Elixir 构建在 Erlang VM (BEAM) 之上,可以处理数百万个轻量级进程,每个进程的开销都很小。这使得 Elixir 非常适合处理需要并行处理多个请求的任务,例如抓取包含分页内容的网站。Elixir 的语法和工具生态系统也让它更易上手,即使是刚接触函数式编程的开发者也能使用。
考虑使用 Elixir 进行网页抓取的关键原因包括:
- 并发性与可扩展性:Elixir 可以同时处理多个请求,同时不牺牲速度或内存效率,因此非常适合抓取多个页面或多个网站。
- 可靠性:BEAM 的"让它崩溃"理念支持优雅的故障处理,这意味着如果某个进程崩溃,你的爬虫工具可以自我恢复。
- 容错能力:Elixir 内置的监督树和弹性架构意味着应用程序可以从意外问题中恢复,而不会中断整个抓取操作。
Elixir 的最佳自动化替代方案
如果你完全不想自己抓取,只想获取数据,可以看看这些数据集网站:
- Bright Data --- 覆盖各行业的可定制和预构建数据集。
- Statista --- 面向商业和研究的广泛统计数据与报告。
- Datarade --- 来自不同提供商的优质数据产品市场。
- AWS Data Exchange --- 与 AWS 服务集成的第三方数据集。
- Zyte --- 面向业务需求定制的网页抓取和自定义数据集。
- Data & Sons --- 用于买卖多样化数据集的开放市场。
- Coresignal --- 提供广泛职位相关数据的人力分析服务。
- Oxylabs --- 专业的公司数据和网页抓取服务。
- Bloomberg Enterprise Data Catalog --- 面向企业使用的金融数据。
- Kaggle --- 面向数据科学的免费公共数据集和工具。
其中有些是免费的,有些不是,有些提供免费样本。选择适合你需求的即可。我与它们中的任何一家都没有关联。
使用 Elixir 实现网页抓取
设置你的 Elixir 网页抓取环境
要开始在 Elixir 中进行网页抓取,你需要设置一个 Elixir 项目,并添加两个主要库:Crawly 和 Floki。
- Crawly 是一个强大的爬取框架,模仿了 Scrapy 的结构和功能,而 Scrapy 是 Python 最流行的网页抓取库之一。它提供了必要的抓取组件,包括 spider、pipeline 和 middleware。
- Floki 是一个面向 Elixir 的简单 HTML 解析器。它允许你使用 CSS 选择器从 HTML 文档中定位并提取特定元素。
第 1 步:设置 Elixir 项目
首先,安装 Elixir(如果你使用 Windows,还需要安装 Erlang),然后创建一个新的 Elixir 项目:
mix new elixir_scraper - sup
此命令会初始化一个名为 elixir_scraper 的新受监督项目。
第 2 步:添加依赖项
在你的 mix.exs 文件中,将 Crawly 和 Floki 添加为依赖项:
defp deps do
{:crawly, "\~\> 0.16.0"}, {:floki, "\~\> 0.33.0"}
end
Then, install the libraries:
mix deps.get
你的 Elixir 项目现在已经可以用于网页抓取了。
使用 Crawly 构建一个简单的 Elixir Spider
Crawly spider 本质上是一个 Elixir 模块,用于定义如何从目标网站获取和解析数据。在这个示例中,我们将创建一个 spider,用于从 ScrapingCourse.com 这个模拟电商网站抓取数据。
第 1 步:创建 Spider
使用以下命令生成一个 Crawly spider:
mix crawly.gen.spider - filepath ./lib/scrapingcourse_spider.ex - spidername ScrapingcourseSpider
这会在 lib 目录中创建一个名为 scrapingcourse_spider.ex 的文件。初始代码应如下所示:
defmodule ScrapingcourseSpider do
use Crawly.Spider
@impl Crawly.Spider
def base_url(), do: "https://www.scrapingcourse.com/ecommerce/"
@impl Crawly.Spider
def init() do
start_urls: \["https://www.scrapingcourse.com/ecommerce/"\]
end
@impl Crawly.Spider
def parse_item(response) do
{:ok, document} = Floki.parse_document(response.body)
product_items =
document
|> Floki.find("li.product")
|> Enum.map(fn x ->
%{
url: Floki.find(x, "a.woocommerce-LoopProduct-link") |> Floki.attribute("href") |> Floki.text(),
name: Floki.find(x, "h2.woocommerce-loop-product__title") |> Floki.text(),
image: Floki.find(x, "img.attachment-woocommerce_thumbnail") |> Floki.attribute("src") |> Floki.text(),
price: Floki.find(x, "span.price") |> Floki.text()
}
end)
%Crawly.ParsedItem{items: product_items}
end
end
这段代码定义了一个 ScrapingcourseSpider 模块,它会:
- 以 ScrapingCourse.com 网站为目标。
- 使用 CSS 选择器提取产品名称、价格、图片和 URL 等数据。
第 2 步:运行 Spider
要执行 spider,请运行以下命令:
iex -S mix run -e "Crawly.Engine.start_spider(ScrapingcourseSpider)"
你的爬虫工具应该会记录每个提取到的项目,为你理解目标网站的数据结构提供有价值的洞察。
将抓取的数据导出为 CSV
要将抓取的数据存储到 CSV 文件中,请配置 pipeline 以包含 Crawly 的 CSVEncoder:
import Config
config :crawly,
middlewares: \[\],
pipelines: [
{Crawly.Pipelines.CSVEncoder, fields: :url, :name, :image, :price},
{Crawly.Pipelines.WriteToFile, extension: "csv", folder: "output"}
]
再次运行 spider,Crawly 会在 output 文件夹中输出一个包含所需数据字段的 CSV 文件。
处理分页页面
许多网站会将数据分布在多个页面上。要处理分页,请检查目标网站的导航元素,并配置你的 spider 以跟随其他页面。
例如,要抓取分页的产品列表:
识别 CSS 选择器,用于分页链接(例如 a.page-numbers)。
使用 Floki 提取每个页面 URL,并将其添加到 Crawly 的 next_requests 数组中。
def parse_item(response) do
{:ok, document} = Floki.parse_document(response.body)
product_items = # parse products...
next_requests =
document
|> Floki.find("a.page-numbers")
|> Floki.attribute("href")
|> Enum.map(&Crawly.Utils.request_from_url/1)
%Crawly.ParsedItem{items: product_items, requests: next_requests}
end
此设置使 Crawly 能够跟随每个分页链接,确保所有产品都被抓取。
处理 JavaScript 渲染内容
与许多语言一样,Elixir 无法在 HTTP 响应中原生执行 JavaScript。不过,你可以集成 Splash(一个无头浏览器)来处理 JavaScript 渲染页面。Splash 会在服务器端渲染内容,为 Elixir 提供完整的 HTML 文档,包括由 JavaScript 生成的元素。
第 1 步:设置 Splash
Splash 可以作为 Docker 容器运行。拉取并运行 Splash 镜像:
docker pull scrapinghub/splash
docker run -it -p 8050:8050 - rm scrapinghub/splash
第 2 步:配置 Crawly 使用 Splash
在你的 config.exs 文件中,配置 Crawly 通过 Splash 获取页面:
import Config
config :crawly,
fetcher: {Crawly.Fetchers.Splash, base_url: "http://localhost:8050/render.html", wait: 3}
此配置确保 Crawly 通过 Splash 获取数据,使其能够处理 JavaScript 渲染内容。现在,当你的 spider 发起请求时,Splash 会处理所有 JavaScript,并将渲染后的 HTML 发送给 Elixir。
使用代理和 User-Agent 避免封锁
许多网站会实施反机器人措施,可能会封锁来自单个 IP 的重复请求。你可以通过轮换 User-Agent 并使用代理服务器来缓解这些封锁。
轮换 User-Agent:添加 Crawly.Middlewares.UserAgent middleware 来轮换 User-Agent:
config :crawly,
middlewares: [
{Crawly.Middlewares.UserAgent, user_agents: [
"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/100.0.4896.127 Safari/537.36",
]}
]
使用代理服务器:使用代理隐藏你的 IP,增强你的抓取工具抵御封禁的能力。
config :crawly,
middlewares: [
{Crawly.Middlewares.RequestOptions, proxy: {"http://proxyaddress.com", 8000}}
]
高级 Elixir 网页抓取技巧
借助 Elixir,你可以让抓取更进一步,通过额外配置和高级技巧提升性能。
并行请求:Crawly 支持并发请求,从而更快地收集数据。在 config.exs 中将 concurrent_requests_per_domain 设置为更高的值。
config :crawly,
concurrent_requests_per_domain: 4
错误处理:Crawly 的错误处理能力允许在抓取遇到问题时优雅恢复,确保以最少的中断实现最大化的数据提取。
结论
Elixir 是网页抓取的强大选择,主要因为它可以并发处理任务,并在高负载下保持韧性。Crawly 和 Floki 等库让上手变得轻松,提供了强大的工具,可用于从简单设置到更复杂任务的一切场景,例如处理分页、抓取 JavaScript 渲染页面以及避免封锁。
网页抓取正在向更分布式、更持久的架构发展,而 Elixir 的函数式风格和强大的处理能力与之非常契合。如果你正在探索更大规模的数据提取,Elixir 是一个可靠的选择,可以满足现代抓取需求。
对其他网页抓取指南感兴趣?
- 使用 Scrapy 进行网页抓取
- 使用 Selenium 进行网页抓取
- JavaScript 与 Python 在网页抓取中的对比
- 使用 Python lxml 进行网页抓取
- 使用 Excel 进行网页抓取
- 使用 Python 进行网页抓取
- 使用 C# 进行网页抓取
- 抓取亚马逊畅销商品
- 使用 Google Sheets 进行网页抓取
- 为网页抓取绕过 Cloudflare
- 请求限速指南
如需阅读其他精彩文章,请访问我的个人主页 --- Data Journal ❤️