Phantomjs:进程通信方式

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

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

1. 命令行方式

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

2. 驱动方式

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

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/

时间: 2024-11-06 09:44:50

Phantomjs:进程通信方式的相关文章

Linux下的进程通信方式(IPC)——管道通信

Unix IPC: 管道.命名管道(FIFO)      管道 1.概念 管道是单向的(半双工).先进先出.无结构的字节流,它把一个进程的输出和另一个进程的输入连接在一起. 写进程在管道的尾端写入数据,读进程在管道的首端读出数据.数据读出后将从管道中移走,其它读进程都不能再读到这些数据. 管道提供了简单的流控制机制.进程试图读一个空管道时,在数据写入管道前,进程将一直阻塞.同样,管道已经满时,进程再试图写管道,在其它进程从管道中读走数据之前,写进程将一直阻塞. 2.管道的特点 (1)单向数据通信

进程通信方式-线程通信方式

进程间:管道,消息队列,信号,共享内存,套接字 进程间通信的方法主要有以下几种:  (1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信.  (2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关 系 进程间的通信.命名管道在文件系统中有对应的文件名.命名管道通过命令mkfifo或系统调用mkfifo来创建.  (3)信号(Signal):信号是比较复杂的通信方式,用于

linux下进程通信方式--共享内存

1.概念 不同进程看到共同区域 2.特点 (1)是进程间通信最快的方式,对不同内存的映射(少了两次拷贝) (2)不提供任何同步互斥机制,也不自己维护 (3)接口简单 3.通信方式 由文件系统提供--管道 由system V提供--消息队列.信号量.共享内存 共享内存与信号量搭配使用 4.实现shmat.shmdt at:挂接   dt:去挂接 0 1 2 1 0 创建 挂接 被另一进程看到 退出 再退出 代码实现: shm.h文件: #ifndef __SHM__#define __SHM__

【网络编程基础】Linux下进程通信方式(共享内存,管道,消息队列,Socket)

在网络课程中,有讲到Socket编程,对于tcp讲解的环节,为了加深理解,自己写了Linux下进程Socket通信,在学习的过程中,又接触到了其它的几种方式.记录一下. 管道通信(匿名,有名) 管道通信,在一个进程之中,只能单一的对其写或者是读,而不可以及执行写操作又执行读操作.这一点,我们可以将其想象成我们的水管,分别连着不同的两端,在有水流的时候,一端只能进行输入,另一端只能进行输出,而不可以同时输入和输出. 管道又分为有名管道和匿名管道,两者的区别在于对于匿名管道,其只能在具有亲缘关系的父

进程通信方式

进程间通讯的方式: 管道中还有命名管道和非命名管道之分,非命名管道只能用于父子进程通讯,命名管道可用于非父子进程,命名管道就是FIFO,管道是先进先出的通讯方式.FIFO是一种先进先出的队列.它类似于一个管道,只允许数据的单向流动.每个FIFO都有一个名字,允许不相关的进程访问同一个FIFO,因此也成为命名管. 消息队列:是用于两个进程之间的通讯,首先在一个进程中创建一个消息队列,然后再往消息队列中写数据,而另一个进程则从那个消息队列中取数据.需要注意的是,消息队列是用创建文件的方式建立的,如果

进程通信方式-管道pipe

管道是两个进程间进行单向通信的机制.因为管道传递数据的单向性,管道又称之为半双工管道. 1.数据只能从一个进程流向另一个进程(其中一个写管道,另一个读管道):如果要进行全双工通信,需要建立两个管道. 2.管道只能用于父子进程或者兄弟进程间的通信,也就是说管道只能用于具有亲缘关系的进程间的通信,无亲缘关系的进程不能使用管道. 除了以上局限性,管道还有其他一些不足,如管道没有名字,管道的缓冲区大小是受限制的,管道所传送的是无格式的字节流. 这就要求管道的输入方和输出方事先约定好数据的格式. 虽然有这

Android中跨进程通信方式之使用Messenger

服务端MessengerService的process属性指定它的进程跟MainActivity不在一个进程 <service android:name="com.example.activity.MessengerService" android:process="com.example.activity.remote" > </service> 在MessengerService创建一个Messenger,以它底层的binder作为绑定服

Android中跨进程通信方式之使用文件共享

一.使用Serializable序列化对象 import java.io.Serializable; public class User implements Serializable{ private static final long serialVersionUID = 1L; private String username; private String password; public String getUsername() { return username; } public v

[OS] Linux进程、线程通信方式总结

转自:http://blog.sina.com.cn/s/blog_64b9c6850100ub80.html Linux系统中的进程通信方式主要以下几种: 同一主机上的进程通信方式 * UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal) * System V进程通信方式:包括信号量(Semaphore), 消息队列(Message Queue), 和共享内存(Shared Memory) 网络主机间的进程通信方式 * RPC: Remote Pro