WWW::Mechanize无法登录DeviantArt非JS表单问题排查
使用WWW::Mechanize登录DeviantArt的问题与修复
问题描述
我正在用Perl的WWW::Mechanize模块尝试登录DeviantArt,已添加POST请求所需的字段和必要请求头。在Firefox禁用JavaScript后可以手动完成登录,但脚本在密码页无法找到表单(DeviantArt登录分两步:用户名页和密码页,跳转地址分别为deviantart.com/_sisu/do/step2和deviantart.com/_sisu/do/signin)。尝试直接调用$mech->post方法也会跳回登录页。
核心疑问
- 禁用JS能手动登录,是否意味着一定能用
WWW::Mechanize这类模块登录? - 取消注释
strict_forms选项后,多个字段(如referer_type)报“readonly”错误,但浏览器检查元素时看不到readonly属性,原因是什么?
现有代码
use strict; use warnings; use WWW::Mechanize; my $mech = WWW::Mechanize->new(); print "Make sure to enter a user/pass\n"; $mech->get('https://www.deviantart.com/users/login'); print "Loaded https://deviantart.com/users/login\n"; my $username = ''; # 请填入有效的用户名 my $password = ''; # 请填入有效的密码 my $csrf; my $lu_token; # 从请求页面获取 my $lu_token2; # DeviantArt登录分两步:先用户名页,再密码页,跳转地址分别为_sisu/do/step2和_sisu/do/signin # 第一步:用户名页 # 获取lu_token、csrf_token等参数 my $form = $mech->forms(); # 该页面只有一个表单 $csrf = $form->[0]->param("csrf_token"); $lu_token = $form->[0]->param("lu_token"); print "提交第一步前的URL: " . $mech->base() . "\n"; # 调试信息 $mech->add_header( 'Connection' => "keep-alive" ); $mech->add_header( 'Content-Type' => "application/x-www-form-urlencoded" ); # $form->[0]->find_input('csrf_token')->{readonly} = undef; # 注释状态,开启strict_forms时会报错 $mech->submit_form( # 完全复制站点的请求字段 form_number => 1, fields => { referer => "https://www.deviantart.com", referer_type => "", csrf_token => "$csrf", challenge => "0", lu_token => $lu_token, username => $username, remember => "on", }, # strict_forms => 1, ); print "提交第一步后的URL: " . $mech->base() . "\n"; if($mech->uri =~ /_sisu/){ print "已跳转到用户名验证后的页面\n"; } # 第二步:密码页 # 获取csrf_token等参数 $form = $mech->forms(); $csrf = $form->[0]->param("csrf_token") . "\n"; # 调试:print "找到csrf $csrf\n"; $lu_token = $form->[0]->param("lu_token"); # 调试:print "找到lutoken $lu_token\n"; $lu_token2 = $form->[0]->param("lu_token2"); # 调试:print "找到lutoken2 $lu_token2\n"; print("提交第二步前的URL: " . $mech->base(), "\n"); $mech->add_header( 'Connection' => "keep-alive" ); $mech->add_header( 'Content-Type' => "application/x-www-form-urlencoded" ); $form->[0]->action("https://www.deviantart.com/_sisu/do/step2"); $mech->submit_form( form_number => 1, fields => { referer => "https://www.deviantart.com/_sisu/do/signin", referer_type => "", csrf_token => $csrf, challenge => "0", lu_token => $lu_token, lu_token2 => $lu_token2, username => "", password => $password, remember => "on", }, # strict_forms => 1, ); # 预期跳转到_sisu/do/signin print("提交第二步后的URL: " . $mech->base(), "\n"); # 调试信息 if ($mech->content() =~ /\Q$username\E/) { print "登录成功!\n\n"; }
脚本修复建议
- 移除多余的换行符:第二步中
$csrf = $form->[0]->param("csrf_token") . "\n";的\n会导致CSRF令牌验证失败,直接改为$csrf = $form->[0]->param("csrf_token");。 - 不要手动修改表单action:注释掉
$form->[0]->action("https://www.deviantart.com/_sisu/do/step2");,使用页面返回的原始action,避免破坏站点的验证逻辑。 - 移除手动添加的Content-Type头:
WWW::Mechanize在submit_form时会自动设置正确的Content-Type,手动添加可能覆盖默认值导致请求被拒绝。 - 验证第一步跳转结果:在第一步提交后,增加判断逻辑,如果未跳转到包含
_sisu的页面,打印页面内容排查问题,比如:unless($mech->uri =~ /_sisu/){ print "第一步提交失败,页面内容:\n" . $mech->content() . "\n"; exit; } - 处理readonly字段(若开启strict_forms):如果需要开启
strict_forms => 1,提前将报错的字段readonly属性设为undef,比如:foreach my $field_name (qw(referer_type csrf_token)) { if (my $input = $form->[0]->find_input($field_name)) { $input->{readonly} = undef; } }
疑问解答
禁用JS可手动登录≠一定能用WWW::Mechanize登录
浏览器禁用JS后仍会自动处理诸多细节:自动维护Cookie、处理跳转时的Referer头、添加浏览器特有的User-Agent等请求头、自动填充表单隐藏字段的默认值。而WWW::Mechanize是纯HTTP请求模拟,需要完全复现浏览器的所有请求细节(包括请求头、Cookie、字段值的精确匹配),任何一个细节不符都可能被站点的反爬或验证机制拦截。strict_forms报readonly错误的原因
- 部分字段可能是HTML中的
type="hidden"隐藏字段,WWW::Mechanize的strict_forms逻辑会将隐藏字段视为不可修改(类似readonly); - 浏览器检查工具显示的是DOM渲染后的结果,而
WWW::Mechanize解析的是原始HTML源码,源码中可能存在readonly属性但被JS动态移除了,你可以通过print $mech->content()查看原始HTML确认; - 开启
strict_forms后,模块会严格校验提交的字段是否属于表单的可修改字段列表,若提交的字段不在列表中就会报错。
- 部分字段可能是HTML中的
内容的提问来源于stack exchange,提问作者dummy097
相关产品推荐
相关产品推荐

