🎯 一款可定制、具备反检测功能的云浏览器,由自主研发的 Chromium驱动,专为网页爬虫AI 代理设计。👉立即试用
返回博客

用Perl进行网页抓取:2026年分步指南

Emily Chen
Emily Chen

Advanced Data Extraction Specialist

31-Jul-2026

TL;DR:

  • Perl 在网页抓取方面依然实用,当它已经是数据或操作栈的一部分时。使用 HTTP::Tiny 进行小请求,使用 LWP::UserAgent 进行更丰富的 HTTP 控制,使用 HTML::TreeBuilder 进行 DOM 解析,使用 WWW::Mechanize 处理 cookies、链接和表单。
  • 不要使用正则表达式解析任意 HTML。将文档解析为树状结构,通过属性选择元素,验证必填字段,并使用真实的 CSV 或 JSON 编码器写入 UTF-8 输出。
  • 普通的 Perl HTTP 客户端不执行 JavaScript。在添加浏览器或管理渲染层之前,确认所需数据是否存在于初始响应中。
  • 代理解决路由和位置需求;它们并不能修复选择器或渲染页面。对于动态或高度保护的公共页面,管理 API 可以将 HTML 返回给相同的 Perl 解析器。
  • 本教程的主要抓取程序在 Perl 5.34.1、libwww-perl 6.83、HTML-Tree 5.07、Text-CSV 2.06 和 WWW::Mechanize 2.22 的测试中,针对安全实践网站 Books to Scrape。

当一个组织已经拥有 Perl 脚本、CPAN 部署和文本处理专长时,使用 Perl 进行网页抓取是一个明智的选择。一个简短的 Perl 程序可以获取页面,遍历 HTML,验证记录,并发出干净的 CSV 或 JSON,而无需引入新的运行时。

限制同样重要。静态 HTTP 客户端查看服务器响应,而不是浏览器渲染的 DOM。现代网站可能在页面启动后加载数据,需要会话或应用流量控制。正确的设计保持在 Perl 中解析,同时仅在目标需要时更改获取层。

本指南逐步构建该设计。

何时应使用 Perl 进行网页抓取?

在以下情况下使用 Perl:

  • Perl 已经在生产环境中安装并得到支持;
  • 源返回有用的 HTML 或 JSON,而无需浏览器执行;
  • 任务依赖成熟的文本处理和文件转换;
  • 抓取程序必须与现有的 Perl ETL 或报告作业集成;
  • 团队更重视小型、可审核的脚本,而不是浏览器自动化栈。

当源严重依赖客户端 JavaScript、交互式浏览器状态、多媒体 API 或在 Perl 以外具有更好支持的框架时,可以考虑其他运行时或管理 API。重点不是让 Perl 模仿每个浏览器的功能,而是让 Perl 自主处理其擅长的部分:HTTP 编排、解析、验证和输出。

官方 CPAN 文档 解释了 Perl 如何解析、构建、测试和安装模块。在你的环境中验证版本后,在部署清单或镜像中固定版本。

选择合适的 Perl 抓取模块

模块 最佳使用 JavaScript 会话支持 安装来源
HTTP::Tiny 小型 GET/POST 客户端,依赖最少 手动 许多安装中的 Perl 核心
LWP::UserAgent 头部、cookies、代理、超时、重定向 是,带 cookie jar LWP::UserAgent
HTML::TreeBuilder 将 HTML 解析为可遍历树 不适用 HTML::TreeBuilder
WWW::Mechanize 跟踪链接、提交表单、保留 cookies WWW::Mechanize
Text::CSV 遵循标准的 CSV 输出 不适用 不适用 Text::CSV
JSON::PP 无需编译扩展即可编码或解码 JSON 不适用 不适用 与 Perl 一起包含

当前的 LWP::UserAgent 文档 涵盖重定向、超时、cookies、身份验证和代理方法。WWW::Mechanize 文档 明确指出该模块不执行 JavaScript。

设置可重复的 Perl 项目

先决条件:

  • Perl 5;
  • cpancpanm
  • 一个可写的项目目录;
  • 外向 HTTPS 访问;
  • 收集目标公共数据的权限。

创建项目并安装完整示例使用的确切模块:

bash Copy
mkdir perl-books-scraper
cd perl-books-scraper
cpanm LWP@6.83 HTML::Tree@5.07 Text::CSV@2.06 WWW::Mechanize@2.22
perl -MLWP -MHTML::TreeBuilder -MText::CSV -MWWW::Mechanize -e 'print "modules ready\n"'

在起草时,这些版本已根据其包注册表进行了验证。如果操作系统的包管理器提供了较旧的模块,请使用项目本地库,而不是替换系统 Perl 组件。

本教程的文件为:

Copy
perl-books-scraper/
├── scrape_books.pl
├── books.csv
└── books.json

使用 HTTP::Tiny 发出小请求

当任务是“发送一个请求并检查响应”时,HTTP::Tiny 就足够了。它返回一个包含 statusreasonheaderscontent 的哈希。

perl Copy
use strict;
use warnings;
use HTTP::Tiny;

my $url = 'https://books.toscrape.com/';

我的$response = HTTP::Tiny->new(
agent => 'PublicCatalogResearch/1.0',
timeout => 20,
)->get($url);

die "请求失败: $response->{status} $response->{reason}\n"
unless $response->{success};

print "接收到 " . length($response->{content}) . " 字节\n";

这对于端点检查和JSON馈送非常有用。对于Cookie、代理凭据、详细响应对象或更丰富的重定向处理,请使用LWP::UserAgent

构建完整的LWP和HTML::TreeBuilder爬虫

承载示例收集了“Books to Scrape”主页上的可见产品卡,验证每条记录,并同时写入CSV和JSON。

该代码块需要访问 books.toscrape.com 和上面列出的四个已安装模块。它不需要凭据。

perl Copy
use strict;
use warnings;
use utf8;

use HTML::TreeBuilder;
use JSON::PP qw(encode_json);
use LWP::UserAgent;
use Text::CSV;

binmode STDOUT, ':encoding(UTF-8)';
binmode STDERR, ':encoding(UTF-8)';

my $url = $ENV{TARGET_URL} || 'https://books.toscrape.com/';
my $ua = LWP::UserAgent->new(
    agent      => 'PublicCatalogResearch/1.0',
    timeout    => 20,
    max_size   => 2_000_000,
);
$ua->protocols_allowed(['https']);

my $response = $ua->get($url);
die "HTTP失败: " . $response->status_line . "\n"
    unless $response->is_success;

my $content_type = $response->header('Content-Type') || '';
die "期望HTML,接收到 $content_type\n"
    unless $content_type =~ m{text/html}i;

my $tree = HTML::TreeBuilder->new;
$tree->ignore_unknown(0);
$tree->parse_content($response->decoded_content);

my @records;
for my $card ($tree->look_down(_tag => 'article', class => qr/\bproduct_pod\b/)) {
    my $link = $card->look_down(_tag => 'h3')->look_down(_tag => 'a');
    my $price = $card->look_down(_tag => 'p', class => qr/\bprice_color\b/);
    my $stock = $card->look_down(_tag => 'p', class => qr/\binstock\b/);

    next unless $link && $price && $stock;
    my $title = $link->attr('title') || $link->as_trimmed_text;
    my $href = $link->attr('href') || '';

    push @records, {
        title => $title,
        price => $price->as_trimmed_text,
        stock => $stock->as_trimmed_text,
        url   => $href,
    };
}
$tree->delete;

die "接受检查失败: 没有完整的产品记录\n" unless @records;

my $csv = Text::CSV->new({ binary => 1, eol => "\n" })
    or die "无法初始化CSV编码器\n";
open my $csv_fh, '>:encoding(UTF-8)', 'books.csv'
    or die "无法写入 books.csv: $!\n";
$csv->print($csv_fh, [qw(title price stock url)]);
for my $record (@records) {
    $csv->print($csv_fh, [@{$record}{qw(title price stock url)}]);
}
close $csv_fh;

open my $json_fh, '>:encoding(UTF-8)', 'books.json'
    or die "无法写入 books.json: $!\n";
print {$json_fh} JSON::PP->new->utf8(0)->canonical->pretty->encode(\@records);
close $json_fh;

print "接受了 " . scalar(@records) . " 条产品记录\n";

运行它:

bash Copy
perl scrape_books.pl
head -n 4 books.csv
perl -MJSON::PP -0777 -e 'decode_json(<STDIN>); print "JSON有效\n"' < books.json

接受检查很重要。HTTP 200 页面仍然可能是登录页面、挑战页面、维护消息或更改的模板。生产收集器应验证预期字段,并将意外输出隔离,而不是将其写入主数据集中。

为什么不使用正则表达式解析HTML?

Perl有一个出色的正则表达式引擎,但HTML是一个具有嵌套元素、可选属性、字符实体、脚本、注释和格式错误的标记。一个在某个快照上有效的模式,通常会在空格或属性顺序更改时失效。

在选择后对值使用正则表达式——例如,规范化价格字符串。使用HTML解析器来定位元素。

HTML::TreeBuilder参考 文档包括look_down、元素属性、文本提取和显式树清理。调用 $tree->delete 在解析后释放循环引用。

选择器的鲁棒性来自语义锚点:

  • 稳定的元素类型和类标记;
  • data-*属性用于应用状态;
  • 具有模式标记的字段;
  • 近值的标签;
  • 对必需字段的接受检查。

避免诸如“第三个div包含价格”等位置假设。

使用WWW::Mechanize维护会话并提交表单

WWW::Mechanize 扩展了 LWP::UserAgent,提供链接、表单、Cookie和历史记录助手。对于授权的表单工作流和当网站返回正常HTML时的应用测试非常有用。

此示例使用先决条件URL和表单字段名称,因为表单因网站而异。仅在您拥有或被授权测试的应用程序上使用。

perl Copy
use strict;
use warnings;
use WWW::Mechanize;

my $mech = WWW::Mechanize->new(
    agent      => 'AuthorizedFormTest/1.0',
    autocheck  => 1,
    timeout    => 20,
);
```plaintext
$mech->get($ENV{AUTHORIZED_FORM_URL});
$mech->submit_form(
    form_name => 'search',
    fields    => { query => '文档' },
);

print $mech->title . "\n";

请勿在发布的脚本中放置用户名或密码。请从受保护的环境或秘密管理器中读取机密,并在日志中保持响应体的隐私,避免泄露账号数据。

在 LWP::UserAgent 中配置代理

当需要选择区域、稳定的批准出口或在收集作业之间进行隔离时,使用代理是合适的。这并不使未授权的目标变得可接受。

以下代码块是一个配置示例,需要读者拥有代理凭证:

perl Copy
use strict;
use warnings;
use LWP::UserAgent;

for my $name (qw(PROXY_HOST PROXY_PORT PROXY_USER PROXY_PASSWORD)) {
    die "请设置 $name\n" unless defined $ENV{$name} && length $ENV{$name};
}

my $proxy = sprintf(
    '%s://%s:%s@%s:%s',
    'http',
    $ENV{PROXY_USER},
    $ENV{PROXY_PASSWORD},
    $ENV{PROXY_HOST},
    $ENV{PROXY_PORT},
);

my $ua = LWP::UserAgent->new(timeout => 20);
$ua->proxy([qw(http https)], $proxy);

my $response = $ua->get('https://example.com/');
die $response->status_line unless $response->is_success;
print $response->decoded_content;

将代理凭证保存在环境中,并在异常报告中屏蔽它们。Scrapeless Proxy Solutions 页面帮助将住宅、数据中心、静态 ISP 和 IPv6 选项映射到流量需求。

处理故障而不损坏数据

健壮性始于有限、明确的结果:

  • 拒绝非成功的 HTTP 状态代码;
  • 限制响应大小和请求时间;
  • 确认 Content-Type
  • 验证预期页面的身份;
  • 要求定义完整记录的字段;
  • 将新输出写入临时路径,仅在验证后重命名;
  • 将意外页面发送到安全的元数据隔离目录;
  • 发出结构化日志,不包含凭证或响应体。

403429 和意外的 HTML 视为停止并调查策略、节奏、目标变化或获取适配的信号。请勿创建无限失效循环。

同时遵守 爬虫排除协议、目标条款、隐私法律和特定来源的请求预算。对于较大的抓取,存储规范化 URL 和内容哈希,以便同一页面不会被处理两次。

了解页面何时需要 JavaScript

在假设需要浏览器渲染之前检查原始响应:

  1. 打开浏览器开发工具。
  2. 重新加载页面并识别包含所需数据的网络响应。
  3. 将该响应与 LWP::UserAgent 输出进行比较。
  4. 在 HTML 中搜索已知的产品名称或记录标识符。
  5. 如果数据来源于公共 JSON 端点,请直接使用该授权端点。
  6. 如果数据仅在浏览器执行后存在,请将获取移动到渲染层。

WWW::Mechanize 不是 JavaScript 引擎。存在驱动浏览器的 Perl 模块,但它们增加了浏览器二进制文件、驱动程序兼容性、进程隔离和更大的资源需求。这对于小型内部系统可能是合理的;但对于静态抓取器而言,这通常不是一个直接的升级。

当浏览器操作成为主要项目时,通过 Universal Scraping API 测试一个代表性的 URL 并比较接受的输出、延迟和操作工作量。

从 Perl 调用 Universal Scraping API

管理的路径将下游解析器保持在 Perl 中。API 执行页面获取并返回结果;Perl 验证并转换它。

这个前提缺口块需要 SCRAPELESS_API_KEY 和授权的 TARGET_URL。在生产使用之前,请在 Universal Scraping API 快速入门中确认当前请求字段。

perl Copy
use strict;
use warnings;
use HTTP::Tiny;
use JSON::PP qw(encode_json decode_json);

my $token = $ENV{SCRAPELESS_API_KEY} or die "设置 SCRAPELESS_API_KEY\n";
my $target = $ENV{TARGET_URL} or die "设置 TARGET_URL\n";

my $response = HTTP::Tiny->new(timeout => 60)->post(
    'https://api.scrapeless.com/api/v1/scraper/request',
    {
        headers => {
            'Content-Type' => 'application/json',
            'x-api-token'  => $token,
        },
        content => encode_json({
            actor => 'unlocker.webunlocker',
            input => { url => $target },
        }),
    },
);

die "API 失败: $response->{status} $response->{reason}\n"

除非$response->{success};

我的$payload = decode_json($response->{content});
如果不存在 $payload->{data},则终止"API响应不包含数据\n";
打印 JSON::PP->new->canonical->pretty->encode($payload->{data});

这个边界故意设计得很简单:

  • Scrapeless负责页面获取和网络操作;
  • Perl负责目标范围、响应验证、解析和存储;
  • 组织负责授权、保留和数据使用。

导出干净的CSV和JSON

CSV和JSON服务于不同的下游系统。

当结果是平坦的并且将要导入Excel、数据库或分析工具时选择CSV。使用Text::CSV;它正确地引用逗号、换行符和引号。

当记录包含数组、嵌套对象或元数据(例如源URL、捕获时间和验证状态)时,选择JSON。JSON::PP可移植,并生成符合标准的JSON。

有关将网络数据提供给分析师的工作流程,请参阅使用Power Query和API在Excel中进行网页抓取。有关API输出选择,请阅读Scrapeless响应格式指南

每条记录应包含足够的来源信息以供审计:

  • 源URL;
  • 捕获时间;
  • 解析器或架构版本;
  • 当相关时,地区或区域;
  • 内容哈希;
  • 接受状态。

Perl抓取器的生产检查清单

在安排脚本之前:

  • 源和字段已获得授权。
  • 目标的robots指令和条款已审核。
  • 模块版本已固定和测试。
  • 秘密来自受保护的环境变量。
  • 解析器使用结构选择器,而不是任意HTML正则表达式。
  • 响应大小、时间、内容类型和必需字段是有界的。
  • 意外页面进入隔离。
  • 日志排除凭据和敏感内容。
  • CSV和JSON输出是编码安全的。
  • 动态页面需求有明确的获取决策。
  • 指标统计接受的记录,而不仅仅是请求。

使Perl专注于数据契约

当抓取器有明确的契约时,Perl最强大:获取授权源,解析已知结构,验证完整记录,并写入确定性输出。这在HTML直接来自LWP::UserAgent、通过代理或来自托管渲染API时仍然有用。

从最小的工作方法开始。如果页面是动态或高度保护的,请保持已验证的Perl解析器,仅替换获取。比较当前Scrapeless定价选项,然后创建Scrapeless帐户,并在承诺更大迁移之前测试一个代表性的公共URL。

常见问题解答

Perl在2026年仍然适合网页抓取吗?

是的,尤其是对于已经有Perl环境和返回有用HTML或JSON的目标的团队。Perl有成熟的HTTP、HTML解析、会话、CSV和JSON模块。涉及浏览器密集工作的任务可能会更容易通过托管获取层或其他支持的自动化堆栈完成。

哪个Perl模块最适合网页抓取?

使用HTTP::Tiny作为最小客户端,LWP::UserAgent获取更丰富的请求控制,HTML::TreeBuilder进行HTML解析,WWW::Mechanize处理链接、表单、cookies和会话历史。大多数真实项目将HTTP客户端与解析器和输出编码器结合在一起。

LWP::UserAgent能执行JavaScript吗?

不能。它检索HTTP响应但不运行页面JavaScript。如果所需的数据在响应中缺失,请使用授权的JSON端点、一个驱动浏览器的工具或一个托管的渲染API。

应该使用Perl正则表达式解析HTML吗?

不,不能用于文档结构。使用HTML解析器定位元素,然后根据需要使用正则表达式规范化选定的文本。

如何在Perl中使用代理?

创建一个LWP::UserAgent并调用其proxy方法以支持所需的协议。从受保护的环境变量中读取代理主机、端口、用户名和密码。

网页抓取合法吗?

合法性取决于管辖权、访问方法、目标条款、数据类型和预期用途。限制收集在授权的公共数据,尊重robots指令和源限制,尽量减少个人数据,并为高风险程序获取法律建议。

在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。

最受欢迎的文章

目录