zoukankan      html  css  js  c++  java
  • 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/

  • 相关阅读:
    火狐插件火狐黑客插件将Firefox变成黑客工具的七个插件
    memcache安装环境:WINDOWS 7
    PHP正则表达式
    968. 监控二叉树 力扣(困难) dfs 官方说DP
    375. 猜数字大小 II 力扣(中等) 区间动态规划、记忆化搜索
    629. K个逆序对数组 力扣(困难) 区间动态规划
    剑指 Offer 51. 数组中的逆序对 力扣(困难) 巧用归并排序算法
    488. 祖玛游戏 力扣(困难) dfs
    16. 最接近的三数之和 力扣(中等) 双指针
    319. 灯泡开关 力扣(中等) 数论
  • 原文地址:https://www.cnblogs.com/kavmors/p/4731883.html
Copyright © 2011-2022 走看看