Mechanize 是一个用于在 Ruby 脚本里将与 Web 页面的交互工作自动化的库。它会自动处理 Cookie 、重定向、Referer 之类的东西,使用起来非常方便。我就用我今天写的一个小脚本作为例子来介绍一下 Mechanize 吧!
其实我是在寻找 Ruby 里面处理 Cookie 的库的时候找到它的。学校的网络以前是要先通过一个 Web 页面登录才能上网的(可恶的电信!),早就有前辈们写了上网登录脚本,方便使用(事实上在 Linux 下想要使用学校提供的 201+ 卡方式访问校外网的话,只有用登陆脚本了,电信提供的 IE 插件根本没法用)。可是脚本是用 perl 写的,我倒是不想为了上网自动登录大费周章地去装一个 Perl (在 Windows 下),不过倒是装了 Ruby ,便想自己写一个吧。
看了那个 Perl 脚本,又抓了写包看了看,好像挺麻烦的,有重定向、还要处理 Cookie ,Ruby 标准库 Net::HTTP 似乎没有提供 Cookie 的解决方案,也不想手工处理,便去网上搜索,一下子就找到了 Mechanize 这个库,这难道不正是我要找的那个东西吗?!
粗略地扫描了下他的 README ,便开始在 IRB 里做实验,先 new 一个 agent 出来:
require 'rubygems'
require 'mechanize'
agent = WWW::Mechanize.new
打开登录页面:
page = agent.get('http://61.175.164.14:8880/webLogin.jsp')
可是出错了:
WWW::Mechanize::ResponseCodeError: 403 => Net::HTTPForbidden
唉!这都是那可恶的电信,居然还判断 User-Agent 的,用 Firefox 都还好,用 Opera 就不能登录,也不知道他们是怎么想的。且不管那么多,既然有限制,我们就欺骗一下好了。Mechanize 有许多内置的 User-Agent 可以供我们选(参见 WWW::Mechanize::AGENT_ALIASES ):
agent.user_agent_alias = 'Windows Mozilla'
page = agent.get('http://61.175.164.14:8880/webLogin.jsp')
这下页面获取到手了,从 IRB 的输出就可以看到里面有一个简单的 form ,只有一个字段 LocalIP ,不管三七二十一,用本地 ip 填上这个字段,然后提交回去:
form = page.form('mainform')
form.LocalIP = get_local_ip
page = agent.submit(form)
这下得到了一个好长的页面,里面有一个超级大的 form (还是叫做 mainform ),其实仔细看看里面只有 username 和 password 两项是需要填的,其他都有预设的值,在浏览器里也正是看到了这两个输入框。那么就按照平时在输入框里输入的内容填进去吧:
form = page.form('mainform')
form.username = '800387278211@zheda'
form.password = 'my password'
page = agent.submit(form)
再返回的页面里面已经没有 form 了,出问题了?不是!打开浏览器,发现已经可以上网了! :D
Mechanize 确实很好用啊,比传统的用于处理 web 页面的脚本抽象了更多的东西,让我们可以更加专注于需要关注的东西,而不用去处理细枝末节了。下面分别是用 Mechanize 做的 Ruby 脚本和原本的 Perl 脚本,从长度就可以看出来差别了,而且 Ruby 脚本应该是更易懂的 :) :
require 'rubygems'
require 'mechanize'
def get_local_ip
IO.popen("ipconfig").read =~ /IP Address[. ]+: ([0-9.]+)/
return $1
end
agent = WWW::Mechanize.new
agent.user_agent_alias = 'Windows Mozilla'
page = agent.get('http://61.175.164.14:8880/webLogin.jsp')
exit if page.forms.empty? # already logged in
form = page.form('mainform')
form.LocalIP = get_local_ip
page = agent.submit(form)
form = page.form('mainform')
form.username = '800387278211@zheda'
form.password = 'my password'
page = agent.submit(form)
然后是 Perl 版的脚本:
#!/usr/bin/perl
use strict;
use warnings;
use LWP::UserAgent;
my $cmd = "/sbin/ifconfig | sed -ne \'s/.*inet addr:\\([^ ]*\\).*/\\1/p\'";
chomp(my @myaddr = `$cmd`);
my $ip = $myaddr[0];
my $url='http://61.175.164.14:8880/webLogin.jsp';
my $agent=new LWP::UserAgent(
keep_alive => 1,
timeout => 30,
agent => 'mozilla/4.0',
cookie_jar => {file => "lwpcookies.txt", autosave => 1}
);
my $request=new HTTP::Request('GET' => $url);
my $response=$agent->request($request);
$response->is_success() or die "Maybe dx server down\n";
print "Got key success, processing...\n";
my %form1 = ('LocalIP'=>$ip);
$response=$agent->post($url, \%form1);
$response->is_success() or die "Something I have not encounter happened :(\n";
print "Get main page success, processing...\n";
my %form2 = (
'username' => "$ARGV[0]\@zheda",
'password' => "$ARGV[1]",
'cookiedate' => '3650',
'consumeright' => '0',
'separatecard' => '0',
'localIP' => $ip,
'needActiveX' => '1',
'isPNP' => '0',
'httpIP' => $ip,
'isProxy' => '0',
'clienttype' => '1',
'ip' => $ip,
'id' => '',
'languagetype' => '0',
'operatingSystem' => '1',
'httpIp' => $ip,
'servicetype' => 'portallocal'
);
$url='http://61.175.164.14:8880/secu/webLogin.jsp';
$response=$agent->post($url, \%form2);
$response->is_success() or die "Something I have not encounter happened :(\n";
print "Send Card infomation success, processing...\n";
$response->content() =~ m/key=(\d*)/;
$url='http://61.175.164.14:8880/myportal/loginsetparameter.jsp?issecu=1&key='.$1;
$response=$agent->get($url);
$response->is_success() or die "Get Parameter failed\n";
$response->is_success() or die "Something I have not encounter happened :(\n";
print "Send Card infomation success, processing...\n";
$response->content() =~ m/key=(\d*)/;
$url='http://61.175.164.14:8880/myportal/loginsetparameter.jsp?issecu=1&key='.$1
;
$response=$agent->get($url);
$response->is_success() or die "Get Parameter failed\n";
print "done\n";
可以看到用传统的方法的话,需要抓包分析,而且许多事情需要手工来做,比如一个 form 的内容通常需要全部填写(看 Perl 脚本里面的那个巨大的 form )。如果是使用浏览器的话,许多工作就没有了,浏览器会自动跟踪重定向,而且隐藏的 form 字段也不会显示出来让你全部填写,而 Mechanize 的工作方式更像浏览器一点:打开登录页面、填上用户名和密码、提交!当然会方便许多了! :)
Comments (6)
我觉得还好啊,因为用 hpricot 分析一下 HTML 花费的时间毕竟不是瓶颈,这个年头在这个地界网速才是问题呢。 :(
这个之类的东西能处理javascript的onclick事件吗?
如果不能,对我来说就没有意义
请楼主尽快回我,谢谢
不能。当然,要看你怎么定义“处理 onClick 事件”了。如果你想让他执行 javascript 代码的话,我想现在还是办不到的。