Phantomjs 进程通讯方式

Phantomjs[1]是一款无界面Webkit浏览器,可用于网页自动化测试。最近一个项目涉及到Phantomjs与其余进程间的通讯,如下介绍其余进程中如何调用Phantomjs做数据接口。java

 

目的:其余程序调用Phantomjs,以Java为例node

 

1. 命令行方式git

经过命令行能够启动Phantomjs进程,在Java中能够用Runtime.getRuntime.exec(String cmd)的方式。这种方式网上不少例子,这里不详细说。在这种方式下,每次调用Phantomjs都须要启动一个进程,调用完退出,而开启一次Phantomjs进程比较费时,因此这种方式不适合于生产环境。github

 

2. 驱动方式web

Selenium提供PhantomjsDriver,提供了在Java直接调用Phantomjs的一系列方法。这种方式下只能调用驱动提供的方法,不能直接调用js文件,不够灵活,所以此次项目中没有用这个方式。有须要能够查阅PhantomjsDriver[2]有关文档。api

 

3. Webserver方式浏览器

Phantomjs提供了Webserver[3]模块,能够用该模块来搭建http服务器。经过Webserver监听端口,Java发起Http请求,就能够实现二者通讯的目的。服务器

Phantomjs充当服务端,解析一个URL对应网站的title,而后把title返回。多线程

 1 var webserver = require('webserver').create();  2 var page = require('webpage').create();  3 var system = require('system');  4 
 5 var port = system.args[1];    //取第二个参数为端口号
 6 
 7 webserver.listen(system.args[1], function(request, response) {  8     var url = request.postRaw;        //接收post数据为url
 9     page.open(url, function(status) { 10         var title = page.evaluate(function() { 11             return document.title; 12  }); 13  response.write(title); 14  response.close(); 15  }); 16 });

Java充当客户端,根据URL列表查询URL的title信息并发

 1 public class Demo {  2      public static void main(String[] arg) {  3          //要查询的URL地址列表
 4          String[] urls = new String[]{  5              "http://www.baidu.com/",  6              "http://www.cnblogs.com/",  7              "http://www.w3school.com.cn/"
 8  };  9          for (int i=0; i<urls.length; i++) { 10              //Http类的详细代码不提供,可用HttpURLConnection或HttpClient封装
11              Http http = new Http("http://127.0.0.1:9999");    //Phantomjs开放的端口
12              http.setParam(urls[i]);        //设置Post参数(URL地址)
13              http.post();                  //发起Post请求
14  System.out.println(http.getResponse()); 15  } 16  } 17  }

把Phantomjs保存为D:/script.js,用Phantomjs加载(phantomjs D:/script.js 9999)。Java发起请求后,Phantomjs接收Request的Post参数做为要查询的URL地址,获取该网站的title后经过Response返回。Java收到Response后,把title打印到console。

在现阶段最新版本中,Webserver模块并未开发得很完善,尤为是在并发方面。因此不建议将这种方式用于大并发的状况下。

 

4. std方式

进程间最基本的通讯方式,相比起Webserver,稳定性更好一些。但一样,不适合用于大并发的状况下。

先看Java端,PhantomjsConnector用于维护Java和Phantomjs之间的std流。

 1 public class PhantomjsConnector {  2     private String pid;        //进程PID
 3     private OutputStream out;  4     private PrintWriter writer;  5     private InputStream in;  6     private InputStreamReader inReader;  7     private BufferedReader reader;  8     
 9     public PhantomjsConnector() { 10         try { 11             Process process = Runtime.getRuntime().exec("phantomjs D:/script.js");    //经过命令行启动phantomjs 12             //初始化IO流
13             in = process.getInputStream(); 14             inReader = new InputStreamReader(in, "utf-8"); 15             reader = new BufferedReader(inReader); 16             pid = reader.readLine();        //从phantomjs脚本中获取本进程的PID
17             out = process.getOutputStream(); 18             writer = new PrintWriter(out); 19         } catch (Exception e) { 20  close(); 21  e.printStackTrace(); 22  } 23  } 24     
25     //结束当前维护的进程
26     public void kill() { 27         try { 28             close();    //先关闭IO流
29             Runtime.getRuntime().exec("taskkill /F /PID " + pid);    //Windows下清除进程的命令,Linux则为kill -9 pid
30         } catch (Exception e) { 31  e.printStackTrace(); 32  } 33  } 34     
35     //执行查询
36     public String exec(String url) throws IOException { 37         writer.println(url);          //把url输出到phantomjs
38         writer.flush();                //当即输出
39         return reader.readLine();     //读取phantomjs的输出
40  } 41     
42     //关闭IO
43     private void close() { 44         try { 45             if (in!=null) in.close(); 46             if (inReader!=null) inReader.close(); 47             if (reader!=null) reader.close(); 48             if (out!=null) out.close(); 49             if (writer!=null) writer.close(); 50         } catch (IOException e) { 51  e.printStackTrace(); 52  } 53  } 54 }

当实例化时,java经过命令行启动Phantomjs进程并保持IO流的链接。执行查询时,向流输出字符(url),而后从流中读取内容(Phantomjs返回的title)。程序完成后可根据pid结束Phantomjs进程。

主类中,只须要循环执行PhantomjsConnector的exec方法。

 1 public class Demo {  2     public static void main(String[] arg) throws IOException {  3         //要查询的URL地址列表
 4         String[] urls = new String[]{  5             "http://www.baidu.com/",  6             "http://www.cnblogs.com/",  7             "http://www.w3school.com.cn/"
 8  };  9         PhantomjsConnector connector = new PhantomjsConnector(); 10         
11         for (int i=0; i<urls.length; i++) { 12             String title = connector.exec(urls[i]); 13  System.out.println(title); 14  } 15         
16         connector.kill();    //最后结束该进程
17  } 18 }

再看Phantomjs端,在js脚本中首先返回本次进程的pid,而后循环监听std输入的内容。

 1 var system = require("system");  2 console.log(system.pid);    //本次进程pid
 3 
 4 //监听std输入
 5 var listen = function() {  6     var url = system.stdin.readLine();    //接收std内容为url
 7     var page = require('webpage').create();  8     page.open(url, function(status) {  9         var title = page.evaluate(function() { 10             return document.title; 11  }); 12         system.stdout.writeLine(title);    //再经过stdout输出
13         system.stdout.flush();            //当即输出
14 
15         //稍做延迟再开始下一次监听
16         setTimeout(function() { 17  listen(); 18         }, 100); 19  }); 20 }; 21 
22 listen();

在只启动一个进程的状况下,Phantomjs只能同时间执行一个查询操做,一次查询结束后才能监听下一个url。在多线程场景下,能够在Java端启动多个Phantomjs的进程,对应多个PhantomjsConnector实例,根据需求把各个类动态分给不一样的线程(BlockingQueue可实现),具体不做陈述。

 

除此之外,Phantomjs与其余语言还有一些集成化驱动,好比与nodejs的phantomjs-node模块之类。以上的只是基本的几种方式,具体选用什么方式通讯,仍是要根据业务需求决定。

 

参考资料及引用:

[1] Phantomjs:Phantomjs官网.
http://phantomjs.org/

[2] PhantomjsDriver:Github. GhostDriver.
https://github.com/detro/ghostdriver

[3] Webserver模块:Phantomjs官网. Phantomjs Api.
http://phantomjs.org/api/webserver/

相关文章
相关标签/搜索