あなたの天然記念物
ホーム更新雑談Perl鉄ゲタランドナーコースガイド自転車Linuxリンク経歴連絡先

LWPのHTML::Formにinputを追加してPOSTする方法 (2015.12.14)

背景

サイトに全自動でアクセスするプログラムをPerlで書く中で、サイトはJavaScriptを使いフォームにinputタグを動的に追加している場所があります。 モジュールHTML::Formはサイトのフォーム処理に便利で使っていますがプログラム側からinputを追加できないので何とかしたい訳です。

コード

#  uri_escape_utf8を使います
use URI::Escape;

# $formが対象のフォーム
# 送信ボタンをクリック、ただし、そのリクエストは後で
my $req = $form->click();

# フォームにないタグ設定のinputをリクエストに追加
my $add_content;
# @append_fieldがフォームにないinput名一覧
# プログラムが設定したいinput/値のセット $spec{input名}=値
for (@append_field) {
	my $v = uri_escape_utf8($spec{$_});
	$add_content .= "&$_=$v";
}
$req->add_content($add_content);

# バイト数変更をヘッダに反映
$req->header(Content_Length => length $req->content);

# リクエスト
$res = $self->ua->request($req);

解説

クリックしてリクエストを作成、加工、サイトへリクエストの流れです。 この2番目にある加工が今回の肝になります。 作成したリクエストはHTML::Messageを継承しているのでそちらの参照/変更関数を使って操作できるようになっています。 GETリクエストのURLでよく見かける「&input名=値」という形式でPOSTのデータ(URLじゃないよ)に追加できます。 URLで%を前置するエスケープがあるのでそれをuri_escape_utf8で変換します。 Perlコード、値の両者とも文字コードはUTF-8です。 全部のinputを連結してからリクエストに追加します。 これでサイトへリクエストすると動作します。 ただしヘッダのContent_Lengthが追加前のままですから、送信時に「ヘッダ(Content_Length)と送信するデータサイズが違うからヘッダを修正するね(意訳)」と警告がエラー出力されます。 そこで送信データのバイト数でヘッダを更新して矛盾を解消します。 最後にサイトへリクエストして完了です。

おまけ

このページを書くにあたり改めて関連リンク先を読むと、
$req->header(Content_Length => length $req->content);
は、
$req->content_length(length $req->content);
で良いことに気付いた。もうアフターカーニバル(笑)。

関連リンク

LWP::UserAgent - Web ユーザエージェントクラス - perldoc.jp
HTTP::Response - HTTP 形式のレスポンスメッセージ - perldoc.jp
HTTP::Request - HTTP 形式のリクエストメッセージ - perldoc.jp
HTTP::Headers - HTTPメッセージヘッダをカプセル化するクラス - perldoc.jp
HTTP::Message - HTTP 形式のメッセージ (基底クラス) - perldoc.jp
HTML::Form - HTML フォーム要素を表現するクラス - perldoc.jp
URI::Escape - 安全でない文字のエスケープとアンエスケープ - perldoc.jp