标签归档:PhantomJS

PhantomJS 模拟登录二

以下的模拟登录程序,流程跟直接操作浏览器一致,不相同的地方是一个是程序开做,一个是人来做。

"use strict";
var page = require('webpage').create();

page.onResourceRequested = function (req) {
    //console.log('requested: ' + JSON.stringify(req, undefined, 4));
};
page.onResourceReceived = function (res) {
    //console.log('received: ' + JSON.stringify(res, undefined, 4));
};
page.onConsoleMessage = function(message) {
    console.log(message);
};


// 访问需要登录的页面
var url = 'http://my.sandbox.ebay.com/ws/eBayISAPI.dll?MyEbay&gbh=1&CurrentPage=MyeBayAllSelling&ssPageName=STRK:ME:LNLK:MESX';

page.open(url, function (status) {  
    if (status === 'success') {
        page.includeJs("http://xxx:8888/js/jquery.min.js", function() {
           var res = page.evaluate(function() {
                var str = '';
                $("table.my_itl-iT tr.my_itl-itR td a.g-asm").each(function(){
                    str += $(this).text()+"\n";
                });
                return str;
           });
           
           console.log(res);
            
           phantom.exit();
        });
    } else {
        phantom.exit();
    }
});

/*
page.open('https://signin.sandbox.ebay.com/ws/eBayISAPI.dll', function (status) {
    if (status === 'success') {
        page.includeJs("http:/xxx:8888/js/jquery.min.js", function() {
            // evaluate里面是一个沙箱,主要的DOM操作地
            page.evaluate(function() {
                var form = $("#SignInForm");
                form.find("input[name=userid]").val("testuser_xxx");
                form.find("input[name=pass]").val("xxxx");
                
                form.trigger("submit");
            });
            // 等待5秒后收集结果
            window.setTimeout(function() {
                //console.log(page.content);
                
                var cookies = page.cookies;
                var saveData = '';
                for(var i in cookies) {
                    console.log(cookies[i].name + '=+' + cookies[i].value);
                    saveData += cookies[i].name+"="+cookies[i].value+"&";
                }
    
                // 1 
                // 如果供外部程序使用,这里把cookie送出
                var save = webpage.create();
                save.open('http://xxx:7070/i.php', 'POST', saveData, function (status) {
                    if (status === "success") {
                        console.log(save.content);
                    }
                    phantom.exit();
                });
                // 2 
                // 如果不需要供外部使用,成功登录后直接退出记录
                // Cookie记录到文件,这样仅需要在Cookie失效时重新登录即可
                // phantom.exit();
            }, 5000);
        });
    } else {
        phantom.exit();
    }
});
*/

登录成功后,需要访问登录保护的页,可以把Cookie发出出去,然后外部程序来完成,也可以继续让PhantomJs去访问,如果抓取内容不连续,那么就类似于用完后,关闭浏览器,下次用再次打开浏览器,载入浏览器是低效的,而且它会载入URL相关的所有页,所以应该使用外部程序完成抓取,程序一旦检测到Cookie失效(比如检测到了跳到登录页),那么马上启动浏览器,重新自动登录,然后取到最新Cookie,这个类似于数据库链接里面的断线重连,如果断线重连无法链接,那么就需要终止了。 需要注意的是PhantomJs并不会把会话Cookie写入到你指定的cookie文件,会话cookie仅在phantom存在时有效,这个跟真实浏览器一致,浏览器关闭,所有会话Cookie会被删除,因为会话cookie的过期时间为0,意味关闭则失效,所以如果这些会话Cookie是维持登录状态的,就需要主动发送出去或记录,而不能依赖cookie文件。

用一个真实的浏览器去登录,再牛逼的防机器登录机制都无效(不使用验证码的情况,防程序登录的机制无非是用JS产生Cookie, 在HTML渲染后根据环境动态添加表单域,这些在一般模拟登录中,几乎可以通杀,因为你不可能得到HTML渲染后的值,也取不到JS在客户端产生的Cookie)。

PhantomJS 模拟登录一

"use strict";

var webpage = require('webpage');
var page = webpage.create();

/// 登录页
page.open('https://signin.sandbox.ebay.com/ws/eBayISAPI.dll', function (status) {
	if (status === "success") {
		var cookies = page.cookies;
		for(var i in cookies) {
			console.log(cookies[i].name + '=' + cookies[i].value);
		}
		//console.log(page.content);
		page.close();

		/// 登录
                // 需要提取其它的表单字段
		var postData = 'userid=testuser_xxx&pass=xxx';

		var login = webpage.create();
		login.open('https://signin.sandbox.ebay.com/ws/eBayISAPI.dll?co_partnerId=2&siteid=0&UsingSSL=1', 'POST', postData, function (status) {
		  if (status === "success") {

			var cookies = login.cookies;
			var saveData = '';
			for(var i in cookies) {
				console.log(cookies[i].name + '=+' + cookies[i].value);
				saveData += cookies[i].name+"="+cookies[i].value+"&";
			}

			//console.log(login.content);
			login.close();

			///	成功登录后发送数据
			var save = webpage.create();
			save.open('http://120.24.42.192:7070/i.php', 'POST', saveData, function (status) {
			  if (status === "success") {

				console.log(save.content);
				save.close();

				///
			  }
			  phantom.exit();
			});
		  } else {
			 console.log("Login Failed...");
			 phantom.exit();
		  }
		  
		});
	} else {
		phantom.exit();
	}
});

这里的phantom可以看做是浏览器进程,调用exit()方法表示直接关闭浏览器。page相当于浏览器中的Tab(开一个tab,跟建一个page概念相同),Tab或叫page打开以后,需要给一个URL,然后把这些个内容加载回来,然后在浏览器中就可以点击了,而在PhantomJs中,需要通过接口去模拟点击,要操作这个page,可以玩的花样很多,比如往这个page中注入一段JS,包含一个JS文件,修改DOM的内容,提交表单等等。page的evaluate方法一定程度上,可以看做是

可编程浏览器 – PhantomJS

可编程浏览器,目前主要有两个
1 PhantomJs,打包Webkit,主要提供JS编程接口
2 SlimerJs,需要独立安装Firefox,依赖这个独立安装的Firefox内核,提供JS编程接口

这两个工具提供的JS编程接口较原始,可以使用CasperJS(http://casperjs.org/)来操作(Node.js模块)。

>>>开始
1 下载
http://phantomjs.org/download.html,直接下载二进制包。
2 构建(Build)
建议直接使用二进制包。
3 最新发布
http://phantomjs.org/releases.html
4 Release Names
5 REPL
PhantomJS 1.5起,提供一个交互模式(在命令行里面直接输入代码运行)

phantomjs.exe	//进入交互模式
phantomjs> phantom.version
{
   "major": 2,
   "minor": 1,
   "patch": 1
}
phantomjs> console.log("phantomjs");
phantomjs
undefined

phantomjs> window.navigator
{
   "appCodeName": "Mozilla",
   "appName": "Netscape",
   "appVersion": "5.0 (Windows NT 6.1; WOW64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1",
   "cookieEnabled": true,
   "language": "zh-CN",
   "mimeTypes": {
      "length": 0
   },
   "onLine": true,
   "platform": "Win32",
   "plugins": {
      "length": 0
   },
   "product": "Gecko",
   "productSub": "20030107",
   "userAgent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1",
   "vendor": "Apple Computer, Inc.",
   "vendorSub": ""
}

CTRL+C或CTRL+D或输入phantom.exit()退出交互模式。支持自动完成和历史记录。

>>>学习
1 开速开始

console.log('Hello, world!');
phantom.exit();

//截屏
var page = require('webpage').create();
page.open('http://example.com', function(status) {
	console.log("Status:" + status);
	if(status === "success") {
		page.render('example.png');
	}
	phantom.exit();
});

//测试加载时间
//phantomjs.ext loadspeed.js http://blog.ifeeline.com
var page = require('webpage').create(),
  system = require('system'),
  t, address;

if (system.args.length === 1) {
  console.log('Usage: loadspeed.js <some URL>');
  phantom.exit();
}

t = Date.now();
address = system.args[1];
page.open(address, function(status) {
  if (status !== 'success') {
    console.log('FAIL to load the address');
  } else {
    t = Date.now() - t;
    console.log('Loading ' + system.args[1]);
    console.log('Loading time ' + t + ' msec');
  }
  phantom.exit();
});

// evaluate()方法,只能返回简单对象,不能包含函数
// 并且evaluate()不能访问page只能的内容
var page = require('webpage').create();
page.open(url, function(status) {
  var title = page.evaluate(function() {
    return document.title;
  });
  console.log('Page title is ' + title);
  phantom.exit();
});

// 在evaluate()方法内部的console信息是不能显示的
// 不过可以在page的onConsoleMessage来实现
// 同时也是一个提取信息的有用办法
var page = require('webpage').create();
page.onConsoleMessage = function(msg) {
  console.log('Page title is ' + msg);
};
page.open(url, function(status) {
  page.evaluate(function() {
    console.log(document.title);
  });
  phantom.exit();
});

//以下实例展示了跟踪请求 和 响应
var page = require('webpage').create();
page.onResourceRequested = function(request) {
  console.log('Request ' + JSON.stringify(request, undefined, 4));
};
page.onResourceReceived = function(response) {
  console.log('Receive ' + JSON.stringify(response, undefined, 4));
};
page.open(url);

2 自动化测试
3 截屏

// 支持png jpg  gif  pdf
var page = require('webpage').create();
page.open('http://github.com/', function() {
  page.render('github.pdf');
  phantom.exit();
});

4 网络监控
5 页面自动化

// 设置用户代理
var page = require('webpage').create();
console.log('The default user agent is ' + page.settings.userAgent);
page.settings.userAgent = 'SpecialAgent';
page.open('http://www.httpuseragent.org', function(status) {
  if (status !== 'success') {
    console.log('Unable to access network');
  } else {
    var ua = page.evaluate(function() {
      return document.getElementById('myagent').textContent;
    });
    console.log(ua);
  }
  phantom.exit();
});
输出:
The default user agent is Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1
Your Http User Agent string is: SpecialAgent

// 使用jQuery来操作页面
// 在page.includeJs中必须使用 phantom.exit()来退出
var page = require('webpage').create();
page.open('http://www.sample.com', function() {
  page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
    page.evaluate(function() {
      $("button").click();
    });
    phantom.exit()
  });
});

// 页面的内容
page.content
page.plainText
// 页面的cookies(包括页面在渲染是JS产生的cookie)
page.cookies
// 页面设置,当前只有一个值userAgent
page.settings.userAgent = ‘SpecialAgent’;
// 页面URL
page.url
// 页面视口大小(浏览器尺寸)
page.viewportSize
{
	width:1024,
	height:768
}
////////////////////////////////////
// 函数列表

    page.childFramesCount
    page.childFramesName
    page.close
    page.currentFrameName
    page.deleteLater
    page.destroyed
    page.evaluate
    page.initialized
    page.injectJs
    page.javaScriptAlertSent
    page.javaScriptConsoleMessageSent
    page.loadFinished
    page.loadStarted
    page.openUrl
    page.release
    page.render
    page.resourceError
    page.resourceReceived
    page.resourceRequested
    page.uploadFile
    page.sendEvent
    page.setContent
    page.switchToChildFrame
    page.switchToMainFrame
    page.switchToParentFrame
    page.addCookie
    page.deleteCookie
    page.clearCookies

// 回调列表

    onInitialized
    onLoadStarted
    onLoadFinished
    onUrlChanged
    onNavigationRequested
    onRepaintRequested
    onResourceRequested
    onResourceReceived
    onResourceError
    onResourceTimeout
    onAlert
    onConsoleMessage
    onClosing

6 进程间通信
写入到文件
对外HTTP请求(GET或POST数据,在页面中通过XMLHttpRequest),跨域是禁止的
来自外部的HTTP请求(提供WebServer模块)
路由PhantomJS的流量通过一个代理(比如fiddler)
7 命令行工具
phantomjs [options] somescript.js [arg1 [arg2 […]]]

// 最为有用的是指定cookies保存的路径
–cookies-file=/path/to/cookies.txt specifies the file name to store the persistent Cookies.
–local-to-remote-url-access=[true|false] allows local content to access remote URL (default is false). Also accepted: [yes|no].

Phantomjs 1.3后,可以通过–config=/config.json来指定配置:

config.json
{
	"cookiesFile": "cookies.text"
}

phantomjs.exe --cookies-file=cookies.txt cookie.js
phantomjs.exe --config=config.json cookie.js

>>> 探索
1 实例
2 最佳实践
推荐使用自动化测试框架,高层封装CasperJS
3 提示和技巧
4 Supported Web Standards
5 Buzz
6 Who’s using PhantomJS?
7 Related Projects
http://phantomjs.org/related-projects.html