Polarisbancage

Navigation

82days since
最爱的banban生日

173days since
Me

Perl&Ruby‎ > ‎

为你学perl转的文章(1)关于merchanzie

Automate interaction with websites using Mechanize

January 9, 2008 – 4:44 pm

rubyMechanize 是一个用于在 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 ),其实仔细看看里面只有 usernamepassword 两项是需要填的,其他都有预设的值,在浏览器里也正是看到了这两个输入框。那么就按照平时在输入框里输入的内容填进去吧:

form = page.form('mainform')
form.username = '800387278211@zheda'
form.password = 'my password'
page = agent.submit(form)

再返回的页面里面已经没有 form 了,出问题了?不是!打开浏览器,发现已经可以上网了! :D

Mechanize 确实很好用啊,比传统的用于处理 web 页面的脚本抽象了更多的东西,让我们可以更加专注于需要关注的东西,而不用去处理细枝末节了。下面分别是用 Mechanize 做的 Ruby 脚本和原本的 Perl 脚本,从长度就可以看出来差别了,而且 Ruby 脚本应该是更易懂的 :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 版的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/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 的工作方式更像浏览器一点:打开登录页面、填上用户名和密码、提交!当然会方便许多了! :)