You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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";
}

脚本修复建议

  1. 移除多余的换行符:第二步中$csrf = $form->[0]->param("csrf_token") . "\n";的\n会导致CSRF令牌验证失败,直接改为$csrf = $form->[0]->param("csrf_token");。
  2. 不要手动修改表单action:注释掉$form->[0]->action("https://www.deviantart.com/_sisu/do/step2");,使用页面返回的原始action,避免破坏站点的验证逻辑。
  3. 移除手动添加的Content-Type头:WWW::Mechanize在submit_form时会自动设置正确的Content-Type,手动添加可能覆盖默认值导致请求被拒绝。
  4. 验证第一步跳转结果:在第一步提交后,增加判断逻辑,如果未跳转到包含_sisu的页面,打印页面内容排查问题,比如:
    unless($mech->uri =~ /_sisu/){
        print "第一步提交失败,页面内容:\n" . $mech->content() . "\n";
        exit;
    }
    
  5. 处理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;
        }
    }
    

疑问解答

  1. 禁用JS可手动登录≠一定能用WWW::Mechanize登录
    浏览器禁用JS后仍会自动处理诸多细节:自动维护Cookie、处理跳转时的Referer头、添加浏览器特有的User-Agent等请求头、自动填充表单隐藏字段的默认值。而WWW::Mechanize是纯HTTP请求模拟,需要完全复现浏览器的所有请求细节(包括请求头、Cookie、字段值的精确匹配),任何一个细节不符都可能被站点的反爬或验证机制拦截。

  2. strict_forms报readonly错误的原因

    • 部分字段可能是HTML中的type="hidden"隐藏字段,WWW::Mechanize的strict_forms逻辑会将隐藏字段视为不可修改(类似readonly);
    • 浏览器检查工具显示的是DOM渲染后的结果,而WWW::Mechanize解析的是原始HTML源码,源码中可能存在readonly属性但被JS动态移除了,你可以通过print $mech->content()查看原始HTML确认;
    • 开启strict_forms后,模块会严格校验提交的字段是否属于表单的可修改字段列表,若提交的字段不在列表中就会报错。

内容的提问来源于stack exchange,提问作者dummy097

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 16:34:55