perl & LWP学习笔记

perl & LWP学习笔记

use LWP::UserAgent; 通过浏览器对象发出GET, HEAD, 和 POST请求

use HTTP::Response; 返回应答结果

这两个语句可以简单的用use LWP;代替。

首先通过类,创建一个浏览器对象,该对象在整个程序中都可使用,因此只需定义一次,用该对象发出HTTP请求。浏览器对象可以使用网络代理

use LWP::UserAgent;

my $browser = LWP::UserAgent->new( );

$browser->env_proxy( ); #proxy (a server that fetches web pages for you, such as a firewall, or a web cache such as Squid).

设置这些后就可以使用get( ), head( ), 或者post( )了

$url = 'http://www.guardian.co.uk/';

my $response = $browser->get($url);

die "Hmm, error \"", $response->status_line( ),"\" when getting $url" unless $response->is_success( );

my $content_type = $response->content_type( );

die "Hm, unexpected content type $content_type from $url" unless $content_type eq 'text/html';

my $content = $response->content( );

die "Odd, the content from $url is awfully short!" if length($content) < 3000;

if($content =~ m/Madonna|Arkansas/i) {

print "<!-- The news today is IMPORTANT -->\n",

$content;

} else {

print "$url has no news of ANY CONCEIVABLE IMPORTANCE!\n";

}

在使用do_POST()和do_GET()函数时,先加载模块use LWP::UserAgent和use HTTP::Response,然后声明变量$browser用来创建LWP::UserAgent实例,由于$browser是在do_POST()和 do_GET()函数的外面创建的,所以它本质上是一个静态变量,在程序和函数中都保持其值不变,例如,如果你打开了HTTP cookies选项,那么在一次调用中由服务器设置的cookies在随后多次调用中都将不变。

详解LWP::UserAgent 和 HTTP::Response类

LWP::UserAgent

用于管理HTTP connections,并为你发出请求。用new( )创建它的一个对象

$browser = LWP::UserAgent->new(%options);

options以及他们的缺省值列于下表

Key Default

agent "libwww-perl/#.###"

conn_cache undef

cookie_jar undef

from undef

max_size undef

parse_head 1

protocols_allowed undef

protocols_forbidden undef

requests_redirectable ['GET', 'HEAD']

timeout 180

如果你需要浏览器的副本,那么使用clone()方法,如$copy = $browser->clone( );

连接参数

timeout()

设置LWP等待服务器响应的最长时间,用法:

$browser->timeout(newval);

$oldval = $browser->timeout( );

$browser->timeout(10);

print "Changed timeout from $oldval to 10\n";

max_size( )

设置user agent从HTTP Response中读取的字节数,用法:$size = $browser->max_size([bytes]),如果Response超过了maximum size,那么客户端会有一个Client-Aborted的header

$response = $browser->request($req);

if ($response->header("Client-Aborted")) {

warn "Response exceeded maximum size."

}

为了让你的浏览器对象支持HTTP Keep-Alive,调用conn_cache( )方法(属于LWP::ConnCache类)即可。使用方法:

use LWP::ConnCache;

$cache = $browser->conn_cache(LWP::ConnCache->new( ));

新创建的connection cache对象一次只能cache一个连接,为了能cache更多,设置它的total_capacity属性。

$browser->conn_cache->total_capacity(10);

如果要cache 所有的连接,$browser->conn_cache->total_capacity(undef);

请求参数

agent()属性设置和返回发送的User-Agent信息。一些网站通过User-Agent字符串来鉴别客户端的浏览器。实例:

print "My user agent name is ", $browser->agent( ), ".\n";

$browser->agent("Mozilla/4.76 [en] (Windows NT 5.0; U)");

print "And now I'm calling myself ", $browser->agent( ),"!\n";

My user agent name is libwww-perl/5.60.

And now I'm calling myself Mozilla/4.76 [en] (Windows NT 5.0;U)!

from()属性控制From信息,包括发起请求的用户的email地址。

$old_address = $browser->from([email_address]);

缺省值是undef。

cookie_jar()方法:管理发送和收到的cookie。

$old_cj_obj = $browser->cookie_jar([cj_obj])

该语句读取或设置该浏览器所有cookie的HTTP::Cookies对象,缺省是无cookie jar,也就是客户端忽略cookie。

如果只想创建一个user agent效用期间的临时cookie jar,用这样的语句:

$browser->cookie_jar(HTTP::Cookies->new);

如果想创建永久性的cookie,应该用文件,如:

my $some_file = '/home/mojojojo/cookies.lwp';

$browser->cookie_jar(HTTP::Cookies->new('file' => $some_file, 'autosave' => 1));

协议

protocols_allowed( )和protocols_forbidden( )方法用来设置允许和禁用的协议。

$aref_maybe = $browser->protocols_allowed([\@protocols]);

$aref_maybe = $browser->protocols_forbidden([\@protocols]);

如果无参数调用上述两个方法时,返回允许和禁用协议的数组的一个引用。

实例:

$browser->protocols_forbidden(["ftp"]);

$browser->protocols_allowed(["http", "gopher"]);

如果想知道LWP是否支持某个协议,用is_protocol_supported( ),如:

$boolean = $browser->is_protocol_supported(scheme);

例如:

unless ($browser->is_protocol_supported("https")) {

warn "Cannot process https:// URLs.\n";

}

重定向

默认状态下LWP::UserAgent是开启了重定向的

requests_redirectable( )属性控制客户端自动重定向的请求方法的列表。

$aref = $browser->requests_redirectable([\@methods]);

如果要禁用重定向,给上述方法传递一个空列表的引用:

$browser->requests_redirectable([]);

要把POST方法加到重定向方法的列表中,可push @{$browser->requests_redirectable}, 'POST';

redirect_ok( )方法来判断请求方法是否支持重定向,支持返回真。如:

$boolean = $browser->redirect_ok(request); #request是方法名,如"POST"

验证

credentials( )用来设置一个用户名,密码来访问一个网站上的特定域

$browser->credentials(host_port, realm, uname, pass);

realm参数为一个字符串,指定服务器上限制访问的区域。在交互式浏览中,realm是弹窗上字符串的一部分。例如,如果弹窗上显示“Enter username for Unicode-MailList-Archives at www.unicode.org”,那么realm字符串是Unicode-MailList-Archives。host_port是www.unicode.org:80,注意浏览器上是不会显示HTTP的默认端口80,以及HTTPS的默认端口443.

假设一个限制页面的地址是http://www.unicode.org/mail-arch/unicode-ml/y2002-m08/0067.html

如果你在浏览器里请求这个地址,一个新的窗口跳出,显示Enter username and password for "Unicode-MailList-Archives" at server "www.unicode.org",请求你输入用户名和密码。单纯的用LWP请求这个页面

use LWP 5.64;

my $browser = LWP::UserAgent->new;

my $url ='http://www.unicode.org/mail-arch/unicode-ml/y2002-m08/0067.html';

my $response = $browser->get($url);

die "Error: ", $response->header('WWW-Authenticate') ||'Error accessing',#('WWW-Authenticate' is the realm-name)"\n ", $response->status_line, "\n at $url\n Aborting" unless $response->is_success;

你将得到以下错误:

Error: Basic realm="Unicode-MailList-Archives"

401 Authorization Required at http://www.unicode.org/mail-arch/unicode-ml/y2002-m08/0067.html

Aborting at auth1.pl line 9. [or wherever]

这是因为 LWP 不知道 host www.unicode.org 里的 “Unicode-MailList-Archives” 区的用户名和地址。解决这个问题最简单的方法是使用 credentials 方法来提供用户名和密码:

credentials 函数要在发请求之前调用.比如:

$browser->credentials( # add this to our $browser 's "key ring"

'www.unicode.org:80',

'Unicode-MailList-Archives',

'unicode-ml' => 'unicode');

代理

env_proxy()方法从环境变量http_proxy或gopher_proxy或no_proxy读取设置。

proxy()可以设置或返回从指定scheme的代理:

$browser->proxy(scheme, proxy);

$browser->proxy(\@schemes, proxy);

$proxy = $browser->proxy(scheme);

前两个语句都是给scheme设置代理,第三个是读取scheme的代理。

如:

$p = $browser->proxy("ftp");

$browser->proxy("ftp", "http://firewall:8001/");

print "Changed proxy from $p to our firewall.\n";

no_proxy()设置对特定的domain禁用代理:

$browser->no_proxy([ domain, ... ]);

$browser->no_proxy("c64.example.int", "localhost", "server");

无参数的no_proxy()表示对任何网站都禁用代理。$browser->no_proxy( ); # no exceptions to proxying

请求方法 三个

$resp = $browser->get(url);

$resp = $browser->head(url);

$resp = $browser->post(url, \@form_data);

更复杂点的:

$resp = $browser->get(url, Header1 => Value1, Header2 =>Value2, ...);

$resp = $browser->head(url, Header1 => Value1, Header2 =>Value2, ...);

$resp = $browser->post(url, \@form_data,Header1 => Value1, Header2 => Value2,...);

实例:

$resp = $browser->get("http://www.nato.int",'Accept-Language' => 'en-US','Accept-Charset' => 'iso-8859-1,*,utf-8','Accept-Encoding' => 'gzip',

'Accept' =>"image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,image/png, */*",);

将应答结果保存到文件

默认将应答结果保存到$response->content( )。这对小文件来说还比较好办。但是对MB级别的文件就就应该把他们保存到文件中,方法:

$resp = $browser->get(url, ':content_file' => filename, ...);

$resp = $browser->head(url, ':content_file' => filename, ...);

$resp = $browser->post(url, \@form_data,':content_file' => filename, ...);

这样就可以把应答结果写入指定文件名的文件。实例:

my $out = 'weather_satellite.jpg';

my $resp = $browser->get('http://weathersys.int/',':content_file' => $out,);

die "Couldn't get the weather picture: ", $response->status_line unless $response->is_success;

如果在header中加入:content_cb和一个subroutine的引用,那么应答请求不会保存在文件中和内存中。

$resp = $browser->get(url, ':content_cb' => \&mysub, ...);

$resp = $browser->head(url, ':content_cb' => \&mysub, ...);

$resp = $browser->post(url, \@form_data,':content_cb' => \&mysub, ...);

你定义的subroutine应该有两个参数,第一个是从应答中获得的数据块,第二个是最终获取的HTTP::Response对象。所以你的subroutine应该类似于:

sub callbackname {

my($data, $response) = @_;

...

实例:

my $resp = $browser->get('http://www.perl.com'':content_cb' => \&hexy,);

sub hexy {

my($data, $resp) = @_;

print length($data), " bytes:\n";

print ' ', unpack('H*', substr($data,0,16,'')), "\n"

while length $data;

return;

}

保存应答结果到文件的另一种方法

mirror()函数

$response = $browser->mirror(url_to_get, filename)

该方法会在header中加If-Modified-Since信息,用来判断本地filename的文件和远程服务器上的文件是否有更新,如果有更新就下载,如果没有就不下载。该方法返回一个HTTP::Response对象,但是没有content属性。所以应该检查$response->is_error( ),实例:

$response = $browser->mirror("http://www.cpan.org/","cpan_home.html");

if( $response->is_error( ) ){

die "Couldn't access the CPAN home page: " .$response->status_line;

}

高级方法

如果Response对象是HTML对象,那么它的<head>会被解析,部分head信息会被加入到HTTP::Response对象的headers中。如:

<html>

<head><title>Kiki's Pie Page</title>

<base href="http://cakecity.int/">

<meta name="Notes" content="I like pie!">

<meta http-equiv="Description" content="PIE RECIPES FROM

KIKI">

</head>

如果你请求该页面,并print $response->headers_as_string

返回结果:

Date: Fri, 05 Apr 2002 11:19:51 GMT

Accept-Ranges: bytes

Server: Apache/1.3.23

Content-Base: http://cakecity.int/

Content-Length: 204

Content-Type: text/html

Last-Modified: Fri, 05 Apr 2002 11:19:38 GMT

Client-Date: Fri, 05 Apr 2002 11:19:51 GMT

Description: PIE RECIPES FROM KIKI

Title: Kiki's Pie Page

X-Meta-Notes: I like pie!

你就可以单独访问那些headers了,方法:

$response->header('Content-Base')

$response->header('Description')

$response->header('Title')

$response->header('X-Meta-Notes')

HTML::HeadParser模块详细介绍了这部分内容。

HTTP::Response对象

code( )返回HTTP::Response的状态码。

status_line( )返回整个HTTP的状态行。eg:

$resp = $browser->get("http://www.cpan.org/nonesuch");

print $response->status_line( );

404 Not Found

如果只需知道状态码,用code()函数

$code = $response->code( );

如果要知道状态行的解释消息,用message( )

$msg = $response->message( );

实例:

$resp = $browser->get("http://www.cpan.org/nonesuch");

print $response->code(), " (that means ", $response->message(), " )\n";

404 (that means Not Found)

有四个函数来检测状态行的类型:is_error( ), is_success( ),is_redirect( ), 和is_info( )

$boolean = $response->is_error( );

$boolean = $response->is_success( );

$boolean = $response->is_redirect( );

$boolean = $response->is_info( );

内容

$the_file_data = $response->content( );

headers

$value = $response->header(header_name);

如果你想访问header中的Description信息,$response->header('Description')

$type = $response->content_type( ); 返回MIME类型。如果是HTML返回"text/html",如果是JPEG文件,返回"image/jpeg"

$length = $response->content_length( ); 返回body的bytes。

$lm = $response->last_modified( ); 返回最后更新时间,有时候取不到值

$encoding = response->content_encoding( );返回网页的编码,最常见的是iso-8859-1,还有utf-8。值得注意的是有时候这些信息并不准确。比如说某个网页返回的编码格式是iso- 8859-1,但实际是big5.

如果你想把所有的header信息都合并到一个字符串,并返回,调用$response->headers_as_string

过期时间

大多数服务器都会发送Date的head信息

$age = $response->current_age( ); 用来返回服务器发送该文档到现在的秒数。实例:

$age = $response->current_age( );

$days = int($age/86400); $age -= $days * 86400;

$hours = int($age/3600); $age -= $hours * 3600;

$mins = int($age/60); $age -= $minutes * 60;

$secs = $age;

print "The document is $days days, $hours hours, $mins minutes, and $secs

seconds old.\n";

The document is 0 days, 0 hours, 5 minutes, and 33seconds old.

$lifetime = $response->freshness_lifetime( ); 用来返回到过期时间还剩的秒数。例如:

$time = $response->freshness_lifetime( );

$days = int($time/86400); $time -= $days * 86400;

$hours = int($time/3600); $time -= $hours * 3600;

$mins = int($time/60); $time -= $mins * 60;

$secs = int($time);

print "The document expires in $days days, $hours hours, $mins minutes, and $secs seconds.\n";

The document expires in 0 days, 23 hours, 6 minutes, and 15 seconds.

$boolean = $response->is_fresh( );用来返回文档是否过期

$expires = $response->fresh_until( );用来返回过期时间

例如

$expires = $response->fresh_until( );

print "This document is good until ", scalar(localtime($expires)), "\n";

This document is good until Tue Feb 26 07:36:08 2004

相对路径

LWP可以将相对路径转化成绝对路径。前提是你的知道URL的base。可用base()函数

$url = $response->base( );