• 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/

  • 相关阅读:
    【转载】Select2插件 IE下 autofocus bug的解决方法
    修改iphone自带的按钮样式
    使用swiper制作微场景遇到的问题
    使用Excel的VLOOKUP函数合并不同Sheet页两个表格中的相同项
    【转】判断浏览器版本是否是ie8以下浏览器,如果是调到提示换更高级版本浏览器页面
    跨域上传文件
    WEB前端学习中遇到的概念
    Rails Gem notification_exception
    Html5 drag and drop
    KeyCode
  • 原文地址:https://www.cnblogs.com/kavmors/p/4731883.html
Copyright © 2020-2023  润新知