做网页抓取信息,其实非常非常麻烦,但是不难,最开始我也想自己弄个 gem,用 machanize,可是发现这东西真的不是特别方便,js 过多的网页要想抓取就要分析 js 文件,尤其是编译过的 js,解压后找到 ajax 请求的地址,说实话,人家主站小改以下整个程序就有可能跑不通了。
已京东的手机搜索为例子做个小程序爬了一下,分析才发现,一个页面 60 个商品,前 30 个是跟着页面过来的,后 30 个是去另外的地址通过 ajax 拿到的,而且每个商品的价格也是从另外的地方 ajax 得到的,不能说这么设计页面不好,能想到当初设计师的设计思路大概是,为了加快页面加载速度只加载一半的商品,等用户浏览到底部的时候再载入剩下的商品,价格从 ajax 请求得到,可以随时做活动打折而不用修改当前展示页面的代码。可是这可苦了作爬虫的人了。
最后发现了一个东西 casperjs,这个东西是基于 phantomjs 的,作用是用 js 代码不通过浏览器而模拟用户行为,主要用于前端自动化脚本测试(初始目的?)。用了这个其实就用不上 ruby 了。不过有了这东西就不会出现上述的情况了,拿来一个网站,不用分析 js,分析页面加载顺序。直接操作就行了,然后把操作的动作翻译成 js。希望多一些人研究这个东西,说实话,现在我有问题了都不知道问谁,大家一起进步把。。。。
正如 #13 楼 @lanyatou 所说的,这就是我后来不用 mechanize 的原因,如我最开始所说,要爬的网站一变,就要重新分析网页,重写代码。。。。实在是个噩梦,尤其是要同时爬很多网站的这种情况,永远在改以前的代码。另外关于 js 速度没有 mechanize 速度快的问题,我们可以有很多办法解决这个问题,但是程序员的时间才是最宝贵的而不是机器的时间。。。
结果我发现我错了,还是 mechanize 好。。。。。。。。。。。。
说到底还是在遇到 js 的时候跑一个本地解释器,然后抓数据,我用 watir+phantomjs+nokogiri 试过,速度简直不要太慢
除非是遇到了数据非抓不可的情况,不然真没必要
ruby 的相关 gem 有 curb(curl) 适合抓取 json 通信类数据,mechanize 适合抓取少 js 和 iframe 类页面,watir 貌似只能用来做测试,直接模拟浏览器,但是速度可想而知。phantomjs 据说很不错,没用过。
phantomjs 速度慢的离谱跟浏览一样,哪里有 machanize 速度快。爬虫当然是 machanize,模拟登陆抓图什么的,可以考虑 phantomjs
@wudixiaotie 如果爬虫的目标网站需要登录,并且是那种 js popup 那种,而且在 input 筐中的 password 是经过客户端 js 加密处理的,这种情况是不是也可以用 casperjs 解决,因为发现只要目标网站的加密规则一变,自己就得重新处理解密的代码,所以,我想,能不能直接利用 casperjs 来跑目标网站的加密 js,不知道你有遇到过这种情况没有
@wudixiaotie ,有时间的话,可以看下百度指数的页面:http://index.baidu.com
这里的登录用的是非对称加密,我试着登录,发现总是会收到用户名或密码错误的提示,觉得应该是客户端加密的代码没有在 casperjs 里面执行导致的,所以不知道 casperjs 能否直接执行这些加密代码,如果能的话,就没必要自己破解了。还有个问题就是,如果我用代理的话,只要是境外的代理,必然让我输入验证码,这个验证码不知道楼主是怎么解决的,难不成每次都要请求下验证码的地址,拿到图片后人工识别并手动输入?
var casper = require('casper').create({
verbose: true,
logLevel: "debug",
clientScripts: ["includes/jquery-2.1.1.min.js"]
})
.userAgent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36");
baidu_index_url = 'http://index.baidu.com/'
casper.start(baidu_index_url, function() {
this.evaluate(function() {
BID.popLogin();
});
});
casper.then(function() {
this.evaluate(function() {
$('input[name="userName"]').val("xxx");
$('input[name="password"]').val("xxx");
$('input#TANGRAM_18__submit').click();
});
});
casper.waitForSelector("#ubarUname", function() {
this.capture('current_page_capture.png');
}, 10000);
casper.run();
改了一下 还是不行 但是确实是登录了 跳转到一个 url 就不通了。。。。
@wudixiaotie 你在
BID.popLogin();
的后面加上
this.__utils__.echo('-----------------------')
this.__utils__.echo(this.__utils__.visible('input#TANGRAM_18__userName'))
this.__utils__.echo(this.__utils__.visible('input#TANGRAM_18__password'))
this.__utils__.echo('-----------------------')
试试,我用你的代码测试了下,发现也没面上的这连个 input 并没有显示出来。另外,这个是我的代码;
var utils = require('utils');
var casper = require('casper').create({
verbose: true,
logLevel: "debug"
});
casper.userAgent('Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:18.0) Gecko/20130119 Firefox/18.0')
var usr = casper.cli.get(0);
var passwd = casper.cli.get(1);
var index_url = 'http://index.baidu.com/';
casper.start(index_url);
casper.wait(1000, function(){
this.click('li.navli a[href="javascript:BID.popLogin();"]');
this.wait(1000,function(){
this.thenEvaluate(function(usr,passwd){
this.__utils__.echo('-----------------------')
this.__utils__.echo(this.__utils__.visible('input#TANGRAM_18__userName'))
this.__utils__.echo(this.__utils__.visible('input#TANGRAM_18__password'))
this.__utils__.echo('-----------------------')
document.querySelector('#TANGRAM_18__userName').setAttribute('value', usr);
document.querySelector('#TANGRAM_18__password').setAttribute('value', passwd);
this.wait(1000,function(){
this.click('#TANGRAM_18__submit');
});
},{ 'usr' : usr, 'passwd' : passwd });
this.wait(1000,function(){
this.evaluate(function(){
if(this.__utils__.exists('#ubarUname')){
this.__utils__.log('-----------login success')
}else{
this.__utils__.log('-----------login faild')
if(this.__utils__.visible('#TANGRAM_18__error')){
this.__utils__.log('password error ------------')
}else{
this.__utils__.log('password not error ------------')
}
if(this.__utils__.visible('#TANGRAM_18__verifyCodeImgWrapper')){
this.__utils__.log('need verify code ------------')
}else{
this.__utils__.log('need not verify code ------------')
}
}
})
})
});
});
casper.run();
打印出来是
[debug] [remote] -----------login faild
[debug] [remote] password error ------------
[debug] [remote] need not verify code ------------
从打印出来的结果看,是登陆失败的,失败的原因是密码错误。所以我想,应该是没有调用 js 的加密函数,这也就是我想问的,怎样才能执行这个加密函数,因为我不像费劲的去破解它的加密规则,想让 phantomjs 模拟 webkit 去自动执行。
#21 楼 @lanyatou 我改了一下 你再看一下,但是还是不通。。。(在原来的楼层改的)
[debug] [phantom] Navigation requested: url=https://passport.baidu.com/v2/api/?login, type=FormSubmitted, lock=true, isMainFrame=false
[debug] [phantom] Navigation requested: url=http://index.baidu.com/static/v3Jump.htm?err_no=257&callback=parent.bd__pcbs__88fo40&codeString=captchaservice326564385963637844715a746b666a38763834672f4b703573355445436d57782b335074715053416b7157644b5855425134615863686f396775542f394667564242657251796b42335839302b6b4e326e50346a67472f716d624d534c513277564f614a35686e52672f4362312f305564696a53646f525a737155314230455565786c76396d544942723946466f34642b46782b61544d6c6673786f32417a4246736d3851714a54484b516669474a6b6475776538757a594533765633337538494769384d573879694b7a696a39486d6e714e716c63776f746e7635566d3634534d75585a7a6d4569337350754e4134494c6b6d41325169496e4931587a6b6f796a3065756754784d2b59576150396d797670414f6b6c626239574f6b4b4f6c73334f637338546132506276672b704f3968705273734d524374506e3977&userName=wudixiaotie&phoneNumber=&mail=&hao123Param=&u=http://index.baidu.com/&tpl=&secstate=&gotourl=&authtoken=&loginproxy=&resetpwd=&vcodetype=e61fcWmwhK7ZyIfWHlY+iHSmF7wcgyMjJBa+ZAChAh+2PAinQDnh3OrZ7OyH6WRIJGFdHbfaE5WylnEvy5P8hJowSJs&lstr=<oken=&bckv=&bcsync=&bcchecksum=&bctime=&accounts=, type=Other, lock=true, isMainFrame=false
@wudixiaotie ,你抓包看一下,在密码框的 keydown 或者 keyup 事件上肯定是绑了事件的,这个事件就是 ajax 请求拿到服务器的 public_key,然后再发送登录请求的时候肯定是把用户输入的密码进行 rsa 加密的了,所以我觉得我们只是利用 setAttribute('value', usr) 或者 attr('val') 肯定是不会触发这个事件的,所以在登录请求的时候肯定是密码错误的
require ‘mechanize’
baidu_agent = Mechanize.new
baidu_agent.cookie_jar.load_cookiestxt("cookies/baidu.cookies")
url = 'http://www.baidu.com'
home_page = baidu_agent.get(url)
结果还不是登录的状态,你帮我看看。
@wudixiaotie Mechanize 能够自动管理 cookie,不需要你 load 一个文件的,之所以用 casperjs 是因为它能够抓取动态内容,mechanize 在这方面还没法做到