将curl命令改写为Perl代码的正确方式及LWP::UserAgent问题解决
我之前也碰到过类似的迁移坑——把依赖curl的功能转到Perl内置模块时,decoded_content()偶尔会返回空,但明明能拿到完整的响应对象甚至打印出部分内容。大概率是编码自动检测或响应头处理的问题,下面给你几个实用的排查和解决方向:
1. 强制指定编码,绕过自动检测的盲区
LWP的decoded_content()会依赖响应头Content-Type里的charset字段自动解码,但很多服务器要么没返回这个字段,要么返回的编码和实际内容不匹配,直接导致解码失败返回空。
你可以手动指定编码来解决这个问题:
use LWP::UserAgent; my $ua = LWP::UserAgent->new; my $response = $ua->get('https://example.com'); if ($response->is_success) { # 手动指定编码(比如UTF-8,根据目标网站实际编码调整) my $content = $response->decoded_content(charset => 'UTF-8'); # 如果不确定编码,也可以先拿原始内容再自行解码 # my $raw_content = $response->content; # my $content = Encode::decode('UTF-8', $raw_content); print $content if $content; } else { die $response->status_line; }
2. 先确认响应状态与原始内容
有时候你拿到的响应可能不是200成功状态(比如3xx重定向、4xx/5xx错误),这类响应的内容本身就很少甚至为空,但很容易被误以为是解码问题。先加上状态检查:
if ($response->is_success) { # 处理内容逻辑 } else { print "响应状态异常: " . $response->status_line . "\n"; # 打印响应头辅助排查 print "响应头详情:\n" . $response->headers_as_string; }
另外,也可以先打印原始content()的长度,确认内容是否真的为空:
my $raw_content = $response->content; print "原始内容字节长度: " . length($raw_content) . "\n";
如果原始内容不为空,那100%是解码环节的问题,直接用Encode模块手动解码会更靠谱。
3. 检查内容压缩的处理情况
如果服务器返回的是gzip或deflate压缩的内容,LWP默认会自动解压,但偶尔也会出现解压失败的情况。你可以先检查响应头里的Content-Encoding字段:
my $encoding = $response->header('Content-Encoding'); print "内容压缩类型: $encoding\n" if $encoding;
如果确实是压缩内容但自动解压失败,你可以手动用IO::Uncompress::Gunzip处理原始内容:
use IO::Uncompress::Gunzip qw(gunzip); my $raw_content = $response->content; my $uncompressed; gunzip \$raw_content => \$uncompressed; my $content = Encode::decode('UTF-8', $uncompressed);
4. 完整的跨平台实现示例
这里给你一个更健壮的跨平台代码,同时实现响应头和网页源码的获取:
use strict; use warnings; use LWP::UserAgent; use Encode; sub get_web_info { my ($url) = @_; my $ua = LWP::UserAgent->new( agent => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', timeout => 10, ssl_opts => { verify_hostname => 0 }, # SSL证书问题时可临时关闭,生产环境不建议 ); my $response = $ua->get($url); my %result = ( headers => $response->headers_as_string, content => '', status => $response->status_line, ); if ($response->is_success) { # 优先尝试自动解码,失败则降级为手动指定UTF-8解码 my $content = eval { $response->decoded_content }; if ($@ || !$content) { $content = Encode::decode('UTF-8', $response->content); } $result{content} = $content; } return \%result; } # 使用示例 my $info = get_web_info('https://example.com'); print "响应状态: " . $info->{status} . "\n"; print "响应头:\n" . $info->{headers} . "\n"; print "网页内容:\n" . $info->{content} . "\n";
这个代码兼顾了自动解码的便利性和手动解码的可靠性,跨平台运行完全没问题(只要Perl环境安装了LWP::UserAgent和Encode模块,Windows/Linux/macOS都支持)。
内容的提问来源于stack exchange,提问作者TheAdam122

