perl から Web ページを読み込む

←戻る
概要 | 準備するもの | スクリプト | 使用方法(基本) | 使用方法(応用) | 免責




















概要

このページでは、perl プログラムから Web ページを読み込む方法を説明します。perl プログラムの文法や、書き方、実行の方法は、既に知っている事を前提にしています。

ファイルハンドルを使ってファイルを読み込むのと同じ感覚で、Web 上のコンテンツ、つまり HTML や画像データを perl の変数にセットする事ができます。

この方法を使って、例えば自作 CGI に、次のような演出を加えられます。

ローカルで走るプログラムであれば、[Internet Explorer のお気に入り]や[Cookie]、[レジストリ]などにもアクセスできる為、

と、夢が広がります。

準備するもの

次の準備が必要です。

  1. perl 5 が実行できる
  2. インターネットに接続されている( HTTP プロクシ越しでも O.K. )
  3. perl に SOCKET 関数が組み込まれている
  4. CGI から動かす場合、CGI からの SOCKET 使用が許可されている
  5. 以下 3 つの perl モジュールが使用できる
    • MIME::Base64
    • Socket
    • FileHandle

ローカルで実行する場合はあまり問題になりませんが、プロバイダのサーバで動かす場合に注意が必要です。

注意点

使用に際し、次の点に注意して下さい。

知的所有権を遵守する

他の人が作成したページは、その人が著作権を有しています。

複写や二次使用をするには、許可や契約が必要となります。

著作権法は、個人利用の範囲であれば、他人の著作物であっても複製してヨシ、と定めていますが、CGI での利用は、「個人利用の範囲」を逸脱しています。普通 CGI は、誰でも読めるようになっているからです。

著作者に無断でページを加工して自分の CGI で表示したり、内容に基づいてCGI の処理を分岐させたりすると、著作者から訴えられる可能性があります。

利用させてもらう前に、必ず著作者の方に確認を取るようにして下さい。

呼び出しすぎない

ネットワーク通信は、コンピュータにとって重い作業です。

この技を CGI で使用すると、一回実行するだけでも、通常の(ネットワーク通信を行わない)CGI と比べ、サーバーに大きな負荷がかかります。

同時にいくつも実行すると、ページがなかなか表示されなくなったり、他の CGI の処理速度が低下したりと、自分だけでなく、他の人の迷惑にもなります。

あまり度を越すと、サーバーの管理者から「迷惑行為」とみなされアカウントを剥奪されかねません。

また、接続先のサーバ(読み込み先)の負担も重要です。

これは、CGI だけでなく、ローカルからの使う場合であっても、同様に注意を払わねばなりません。

ブラウザからの読み込みは、人間の指の速さに比例した負荷しか与えませんが、この技を短い周期で自動ループさせたりすると、桁違いの負荷を与えてしまいます。向こうにしてみれば、いわゆる DoS 攻撃を受けているのに近く、迷惑な事おびただしい筈です。ここまでくると、営業妨害で訴えられる可能性もあります。

くれぐれも、常識はずれの負荷は掛けないようにして下さい。

待ち時間を考慮する

相手や、相手までの伝達経路の混雑具合によって、読み込みにかかる時間はまちまちです。

ひょっとすると長時間、応答が返らないかもしれません。

この間、呼び出し元のプログラムは待ち続ける事になります。しかし CGI プログラムの場合、それは単に、「なかなかページが表示されない」事としてしか反映されません。

インターネットはハードディスクなどと違い、転送経路間に不安定材料が多く、時間も掛かるのが普通です。

CGI の場合、一体何が原因で遅くなっているのか見えないので、ユーザーは、ディズニーのクソネズミ園に並ぶ以上に、ストレスを感じるでしょう。遅い原因は、あなたの技術力不足と判断されるかもしれません。

せっかく来て頂いたお客さんに不愉快な思いをさせるのは残念ですし、自分の預かり知らぬ事が原因で酷評の矢面に立たされるのも、気持ちがいいものではありません。

不確定な要素を含んでいる旨。時間が掛かるかもしれない旨など、予め明記しておくとユーザーフレンドリーでしょう。

スクリプト

更新履歴

2004年 3月 5日
  • プロクシポートとして設定した値を正しく処理できない、という問題を修正しました。
  • かつ、大幅にリファクタリングしました。(インターフェイスに変更はございません)
一点目は、Y様(o******@***.n**.jp)よりご指摘頂きました。ありがとうございます。
2003年 2月10日
  • Base64モジュールを呼び出す部分を、大文字(MimeMIME)に変更。(UNIX 環境では、大文字でないと当該モジュールが読み込まれませんでした)
  • 送られるデータを読み込む部分の、chomp の位置を、while の条件判断部からループ内に変更。(while 条件部に書いた場合、最後の行が改行コードを含まないとき、その行を読み込めませんでした)
以上2点、東京都のY.S.様よりご指摘頂きました。ありがとうございます。
2002年 1月12日未明
  • サブルーチン名を変更(getHTMLgetHTTP)
  • 戻り値内の \r\n\n に置換しないようにした(すみません。これってバイナリを読む時ヤバかったです)
2002年 1月 6日
  • 初稿
###
### Web 上のコンテンツを読む perl スクリプト
### A perl script to retrieve contents on the internet via http.

### (C) 2001-2004 笠井 崇文  最終更新日: 2004年 3月 5日
### (C) 2001-2004 Takafumi Kasai           Last modified: 5 May, 2004

### 本スクリプトは、自由に転載・引用・配布・使用・改変していただいて結構です。
### 但し、その結果何らかの損害を被った場合も、責任を負いかねます。
### あらかじめご了承ください。

### と言いつつ虫の好い話ですが、バグや無駄な部分等を発見されましたら、上記
### アドレスまでご一報頂ければ幸いです。

### You can quote, distribute, use, and alter this script without 
###  any permission. But I never bear responsibility for any possible
###  damage by this script.  However, I hope to receive reports of
###  bugs or wasteful part in this script from you.

# ==============================================================

#  要件 / Requirements
#
# ・perl 5 以降 / perl 5 or later
# ・以下の perl モジュール / perl modules follows:
#      Socket
#      FileHandle
#      MIME::Base64
# (ActivePerl 等に付属のものや、CPAN からダウンロードしたものを別途ご用意下さい)
# (You can get them from ActiveStates or CPAN)

#  使用方法 / Usage
#
# (1) このスクリプト(getHTTP)を、@INC にパスの通った場所に置く。
# (2) 呼び出し元のプログラムで、
#       require('getHTTP');
#     と書く。
# (3) 
#       ($http_response, $errorMessage) = &getHTTP('http://www.kasai.fm/');
#     のように、Web 上のコンテンツを読みこめる。

#     $http_response に、読み込んだコンテンツが入る。
#     もしエラーが起ると、$errorMessage にエラー理由が入る。

# (1) Put this script where @INC can reach.
# (2) In the caller script, put this code:
#     require('getHTTP');
# (3) 
#     ($http_response, $errorMessage) = &getHTTP('http://www.kasai.fm/');
#    will retrieve contents on the internet.

#  認証の必要なページを見る場合 / In case authorization required

# ($http_response, $errorMessage) = &getHTTP('URL'=>'http://www.kasai.fm/',
#  'UserID'=>'Mihata', 'Password'=>'Tatenashi');

#  プロクシを使う場合 / In case you use a proxy 

# ($http_response, $errorMessage) = &getHTTP('URL'=>'http://www.kasai.fm/', 'Proxy'=>'someproxy.com:8080');

#  認証の必要なプロクシを使う場合 / In case you use a proxy which requires authorization

# ($http_response, $errorMessage) = &getHTTP('URL'=>'http://www.kasai.fm/', 'Proxy'=>'someproxy.com:8080',
#  'ProxyUserID'=>'Alladin', 'ProxyPassword'=>'open_sesami');

# ==============================================================

use Socket;
use FileHandle;
#use MIME::Base64;
require('BASE64');

sub getHTTP{
	local $\ = "";

	my %arg = ( @_ );
	my $data;
	my ( $uri, $proxy_address, $proxy_port, $http_version, $user_id, $password,
		$proxy_user_id, $proxy_password, $referrer, $referer, $agent ) =
	(
		$arg{URL}           || $_[0],
		$arg{Proxy}         || "",
		$arg{ProxyPort}     || "8080",
		$arg{HTTP}          || "1.1",
		$arg{UserID}        || "",
		$arg{Password}      || "",
		$arg{ProxyUserID}   || "",
		$arg{ProxyPassword} || "",
		$arg{Referrer}      || "",
		$arg{Referer}       || "",
		$arg{UserAgent}     || "HTTP client version 1.02 (www.kasai.fm)"
	);

	my ( $scheme, $domain, $server_address, $server_port, $path );
	my ( $target_address, $target_port, $target_path, $target_ip );


######### URI parsing

	### retrieve scheme ( "http://" )
	
	if ( $uri =~ s!^(\w+?)://!! ){
		$scheme = $1;
		
		return ("",
			"Can't handle '$scheme'. Only 'http' is possible"
		) if ( $scheme !~ /^http$/i );
	}
	else{
		$scheme = 'http';
	}


	### retrieve domain, port and path ( "hogehoge:8080/foo/bar.html" )
	
	( $domain, $path ) = split( /\//, $uri, 2 );
	( $server_address, $server_port ) = split( /:/, $domain, 2 );
	
	$server_address ||= "localhost";
	$server_port    ||= getservbyname( $scheme, "tcp" );


	### complete info about your proxy server

	if ( $proxy_address and !$proxy_port ){
		( $proxy_port ) = $proxy_address =~ /:(\d+)/;
		
		return ("",
			"Proxy port is undefined.".
			"Specify 'ProxyPort'=>8080 or 'Proxy'=>'${proxy_address}:8080'"
		) unless ( $proxy_port );
	}


	### switch arguments according to if you use a proxy server
	
	( $target_address, $target_port, $target_path ) = $proxy_address ?
		(
			$proxy_address,
			$proxy_port,
			"${scheme}://${server_address}:${server_port}/${path}"
		) :
		(
			$server_address,
			$server_port,
			"/$path"
		);


 ######### SOCKET (create)
 
	$target_ip    = inet_aton( $target_address ) || return ("", "Can't connect to $target_address" );
	$sock_address = pack_sockaddr_in( $target_port, $target_ip );
	
	socket(SOCKET, PF_INET, SOCK_STREAM, 0) || return ("", "Can't create socket on $target_address");


 ######### SOCKET (connect)
 
	connect(SOCKET, $sock_address) or return ("", "Can't connect socket on $sock_address");
	autoflush SOCKET (1);


 ######### Send HTTP GET request
 
	if ( $http_version eq "1.1" ) {
		print SOCKET "GET $target_path HTTP/1.1\n";
		print SOCKET "Host: $target_address\n";
		print SOCKET "Connection: close\n";
	}
	else {
		print SOCKET "GET $target_path HTTP/1.0\n";
	}

	if ( $user_id ){
		print SOCKET "Authorization: Basic\n ";
		print SOCKET &base64'b64encode("${user_id}:${password}")."\n";
	}

	if ( $proxy_user_id ){
		print SOCKET "Proxy-Authorization: Basic\n ";
		print SOCKET &base64'b64encode("${proxy_user_id}:${proxy_password}")."\n";
	}

	print SOCKET "User-Agent: $agent\n"  if ( $agent    );
	print SOCKET "Referrer: $referrer\n" if ( $referrer );
	print SOCKET "Referer: $referer\n"   if ( $referer  );

	print SOCKET "Accept: text/html; */*\n";
	print SOCKET "\n";


	######### Receive HTTP response via SOCKET

	while ( <SOCKET> ) {
		chomp;
		$data .= "$_\n";
	}


	######### SOCKET (close); take down the session
	
	close(SOCKET);


	######### return

	return ($data, "");
}

1;

使用方法(基本)

使用する手順は次の通りです。

  1. 上記のスクリプトをコピー&ペースト、またはダウンロードし、getHTTPというファイル名で保存します。
  2. この機能を使いたいプログラム(呼び出し元プログラム)をgetHTTPと同じフォルダに置きます。
  3. 呼び出し元プログラム中に、
    	require('getHTTP');
    	
    と書きます。プログラムのどこに書いても OK です。
  4. Web ページを読み込む時は、
    	($http_response, $errorMessage) = &getHTTP('http://www.kasai.fm/');
    	
    のようにします。&getHTTP('xxxxx') の、xxxxx の部分に、読み込みたいページのアドレス(URL)を書く訳です。
  5. 読み込みに成功すると、$http_responseに、サーバーからの応答が入ります。
  6. 読み込みに失敗すると、$errorMessageにエラー理由が入ります。この時$http_responseには空文字列が返ります。
使用方法(応用)
  1. 読み込んだ直後の$http_responseは、そのままではホームページとして表示することができません。$http_responseには、通信上必要となる様々な情報も含まれているからです。
    これらを解釈する方法は、HTTP レスポンスを解析するを参照して下さい。
  2. 認証の必要なページを見る場合、
    ($http_response, $errorMessage) = &getHTTP('URL'=>'http://www.kasai.fm/', 
     'UserID'=>'Mihata', 'Password'=>'Tatenashi');
    	
    のようにします。
  3. プロクシを使う場合、
    ($http_response, $errorMessage) = &getHTTP('URL'=>'http://www.kasai.fm/', 'Proxy'=>'someproxy.com:8080');
    	
    のようにします。
  4. 認証の必要なプロクシを使う場合、
    ($http_response, $errorMessage) = &getHTTP('URL'=>'http://www.kasai.fm/', 'Proxy'=>'someproxy.com:8080',
     'ProxyUserID'=>'Alladin', 'ProxyPassword'=>'open_sesami');
    	
    のようにします。
免責

本プログラムは、自由に配布、使用、改変して頂いて結構です。転載の許可も必要ありません。

但し本プログラムを使用した結果、何らかの損害を被ったとしても、当方では責任を負いかねます。ご了承ください。

と言いつつ虫の好い話ですが、バグや無駄な部分等を発見されましたら、下記アドレスまでご一報頂ければ幸いです。


←戻る