关闭

07 java.awt.Robot的一些使用

标签: java自动化robot
826人阅读 评论(0) 收藏 举报
分类:

前言

今天, 说一下java.awt.Robot 的用法, 此类用于为测试自动化、自运行演示程序和其他需要控制鼠标和键盘的应用程序生成本机系统输入事件。Robot 的主要目的是便于 Java 平台实现自动测试。

这个家伙可厉害了, 可以模拟键盘操作, 鼠标操作, 以及截图
其实 我接触这个家伙, 也是在一个比较偶然的情况下, 一个家伙在贴吧问如何实现透明的界面, 然后司马大水神貌似是贴了一下Robot截图的时候, 然后 后来搜了一下api文档, 看了一下
从此 一些相关的问题, 我通常会向Robot考虑一下的

注意

请确保你在发消息的过程中不会有其他的弹窗之类的出现, 如果有的话, 那么会对该窗口产生相应的按键的效果的 !
所以 此类一定要慎用, 确保两批模拟按键之间的间隔时间内你能够关掉程序, 否则 是很危险的 !

试想 如下场景, 如果你启动了一个程序, 将鼠标每次移动到(100, 100)的位置, 并且占用了键盘[用户不能操作键盘了], 而每两次模拟鼠标移动的时间间隔为100毫秒, 那么 只要程序启动起来了, 你的机器是不是就只能看着他在这里干耗电 或者强制关机了!

又或者 你在模拟点击的时候恰好一个弹窗出来, 然后程序点击了他, 给你造成了经济 或者其他的损失, 那是不是非常不划算 !

所以 再次提醒, 请慎用 !

01 qq消息刷屏

不知道各位快要毕业的同学是否收到过这样的消息, 卧槽 隔三差五的给你发这么一条消息, 还叫你不要方案, 是不是不能忍….
那么 我经常干的事情就是, 给他发个几十条废话发送过去, 虽然 这样没有什么用, 但是 至少能够满足满足我们也骚扰一下他们的心里呗
这里写图片描述

思路 : 将给定的字符串复制到剪切板, 然后等待prepareMillus毫秒, 给用户准备的时间 [用户需要做的准备工作就是获取聊天对象的窗口], 然后就是一个循环发送消息[模拟”ctrl+v” 将剪切板的消息粘贴到聊天窗口, 然后再”alt+enter“发送消息]

当然 这也依赖于qq聊天的一个特性发送消息过后, 焦点会继续回到当前聊天窗口

效果如下图 :
这里写图片描述

参考代码如下 :
当然 一下代码是scala代码, 将他改写成java代码就好了, 替换一下属性, 方法的表示, 以及循环的表示

1 Test02SendMsgOnClipBoardForQQ : qq发送剪切板的信息

package com.hx.test01

/**
 * fileName : Test02SendMsgOnClipBoardForQQ 
 * Created by 970655147 on 2015-10-18 21:55.
 */
object Test02SendMsgOnClipBoardForQQ {

  // 可配置对象
  // 两个按键
  // 发送消息的次数, 发送消息之前让用户准备的时间
  // 发送消息的间隔, 打印日志的间隔
  val robot = new Robot()
  val ctrlV = Array[Int](KeyEvent.VK_CONTROL, KeyEvent.VK_V)
  val ctrlEnter = Array[Int](KeyEvent.VK_CONTROL, KeyEvent.VK_ENTER)
  val prepareMillus = 5 * 1000
  val chatInterval = 1 * 100
  val sleepPerLog = 5 * 100

  // 消息, 消息内容的长度阈值, 获取消息内容 [超长, 用".."表示]
  val msg = "宝宝小逗比 !"
  val sendMsgTimes = 10
  val msgContentThreshold = 20
  val leaveOut = "..."
  val msgContent = if(msg.length > msgContentThreshold) {
    msg.substring(0, msgContentThreshold - leaveOut.length) + leaveOut
  } else {
    msg
  }

  // qq聊天 "ctrl+v" + "ctrl+enter"
  // 发送给逗逗
  def main(args :Array[String]) = {

    Tools.copyDataToClipBoard(msg)
    await(prepareMillus)
    for(i <- 0 until sendMsgTimes) {

      pressBts(ctrlV)
      pressBts(ctrlEnter)
      println("send the msg '" + msgContent + "' at : " + i + " times ... ")
      Tools.sleep(chatInterval)

    }

  }

  // 等待之前的打印日志
  def await(sleepMillus :Int) = {
    var remaining = sleepMillus
    while(remaining > 0) {
      println("before send msg : " + remaining + " ms")
      Tools.sleep(sleepPerLog)
      remaining -= sleepPerLog
    }
  }

  // 模拟按给定的按键序列, 并松开
  // 这里发现了一个问题 scala能够使用 "1 to 10", 表示"1, 2, ..., 10", 但是不能使用"1 to 10 2", 也就是说不能带步长
  // 带步长的使用方式 "1 to (10, 2)"
  def pressBts(keyCodes :Array[Int]) :Unit = {
    for (i <- 0 until keyCodes.length) {
      robot.keyPress(keyCodes(i))
    }

//    for (i <- Range.inclusive(keyCodes.length-1, 0, -1)) {
    for (i <- (keyCodes.length-1) to (0, -1) ) {
      robot.keyRelease(keyCodes(i))
    }
  }

}

2 Tools. copyDataToClipBoard(Object obj) : 将给定的字符串复制到剪切板

    // 将数据复制到剪切板
    public static void copyDataToClipBoard(Object obj) {
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        StringSelection ss = new StringSelection(obj.toString() );
        clipboard.setContents(ss, null);
    }

02 游聚刷新闻

。。这个小应用是关于一个游戏平台玩游戏的时候, 刷刷刷屏是否是感觉非常有逼格, 在你玩游戏的时候, 居然还能够发出消息,,,会让别人有一种神奇的感脚的[你的 手速这么快么, 还能边玩游戏, 边刷屏…]!

思路 : 将获取游聚游戏面板的焦点, 然后模拟”enter”获取聊天窗口的焦点, 然后模拟”ctrl+v”将数据粘贴到聊天窗口, 然后在模拟”enter”发送消息
这个过程中 需要改变的就是消息的内容啦, 至于消息的内容嘛, 可以从其他的输入源获取
我这里 是从百度上面抓取的新闻
当然, 这也依赖于游聚聊天的一个特性, 当获取游戏面板的焦点后, 按下enter键可以获取到聊天输入框的焦点

当然 这里还可以导出一个刷屏的方式, f1-f8为游聚的刷屏的快捷键, 冷却[硬直]时间间隔为5 sec, 所以 只要隔5 sec模拟一次”fn”的按键即可

因为gif太大了, 所以我将其分成了两部分
这里写图片描述


这里写图片描述

参考代码如下 :
1 Test03CrawlForBaiduNews : 爬取baidu新闻 [依赖于jsoup]

/**
 * file name : Test03CrawlForBaiduNews.java
 * created at : 8:54:12 PM Jun 26, 2015
 * created by 970655147
 */

package com.hx.crawler;

public class Test03CrawlForBaiduNews {

    // 测试站点 : http://news.baidu.com/
    // 爬取百度新闻的, 所有主题
    public static void main(String []args) {

        String url = "http://news.baidu.com/";

        try {

            List<String> news = getAllNewsTitle(url);
            Log.log(news.iterator() );
            Log.log("get " + news.size() + " news !");

        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    // 爬取所有的新闻标题
    public static List<String> getAllNewsTitle(String url) throws IOException {
        List<String> res = new LinkedList<>();
        Document doc = Jsoup.connect(url).get();
        Elements body = doc.select("div#body");

        // 1. 解析热点要闻
        Elements allKindOfNews = doc.select("div#pane-news");
        for(Element ele01 : allKindOfNews) {
            logTitle(ele01.parent().previousElementSibling(), "li.active");

            logNews(ele01, "ul", "li a", res);
            Log.enter(2);
        }


        // 2. 解析地方新闻, 以及其他 等等
        Elements withIdsDivs = body.select("div[id] div[class=column-title-home]");
        Set<Element> otherNews = new HashSet<>();
        for(Element ele01 : withIdsDivs) {
//          newsIds.add(ele.parent().attr("id"));
            Elements newss = ele01.parent().select("div ul");
            if(newss.size() == 0) {
                otherNews.add(ele01);
            }

            logTitle(ele01, "h2 a");

            logNews(newss, "li a", res);
            Log.enter(2);
        }

        // 解析特殊的新闻, 图片新闻
        for(Element otherNew : otherNews) {
            logTitle(otherNew, "h2 a");

            Elements news = otherNew.parent().select("a.txt");
            for(Element ele03 : news) {
                Log.log(ele03.text());
                res.add(ele03.text());
            }
        }

        return res;
    }

    // 打印标题
    private static void logTitle(Element ele, String selectStr) {
        Element title = ele.select(selectStr).get(0);
        Log.log(title.text());
        Log.horizon();
    }

    // 打印一个主题的新闻
    private static void logNews(Element ele, String selectStr01, String selectStr02, List<String> res) {
        Elements newss = ele.select(selectStr01);
        for(Element ele02 : newss) {
            Elements news = ele02.select(selectStr02);
            for(Element ele03 : news) {
//              Log.log(ele03.text());
                res.add(ele03.text());
            }
            Log.enter();
        }
    }

    // 重载, 相对于上面的方法, 这里直接提供了上面的newss
    private static void logNews(Elements newss, String selectStr01, List<String> res) {
        for(Element ele02 : newss) {
            Elements news = ele02.select(selectStr01);
            for(Element ele03 : news) {
//              Log.log(ele03.text());
                res.add(ele03.text());
            }
            Log.enter();
        }
    }

}

2 Test04GetAllNewsToChat : 模拟发送游聚消息

/**
 * file name : Test04GetAllNewsToChat.java
 * created at : 8:11:31 PM Jun 27, 2015
 * created by 970655147
 */

package com.hx.crawler;

public class Test04GetAllNewsToChat {

    // 将新闻内容 复制到游聚平台 聊天
    public static void main(String []args) {

        String url = "http://news.baidu.com/";
        int interval = 5000;

        List<String> news = null;
        try {
            news = Test03CrawlForBaiduNews.getAllNewsTitle(url);
        } catch (IOException e) {
            e.printStackTrace();
        }

//      news  = new HashSet<>();
//      news.add("ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd");

        int start = 20;
        sendNews(news, interval, start);

        Log.log("end ... ");
    }

    // 发送所有的新闻
    public static void sendNews(List<String> newss, int interval, int start) {
        int idxForNews = start;

        Iterator<String> it = newss.listIterator(idxForNews);
        while(it.hasNext() ) {
            String news = it.next();
            Tools.sleep(interval);
            Log.log((idxForNews++ ) + " : " + news);
            sendNews0(news);
        }
    }

    // 模拟操作的robot
    private static Robot robot;
    private static int PAUSE_INTERVAL;
    static {
        try {
            PAUSE_INTERVAL = 10;
            robot = new Robot();
        } catch (AWTException e) {
            e.printStackTrace();
        }
    }

    // 按住enter的序列, 和按住 (ctrl + v) 的序列
    static List<Integer> enterSeq;
    static List<Integer> ctrlVSeq;
    static {
        enterSeq = new ArrayList<>();
        ctrlVSeq = new ArrayList<>();

        enterSeq.add(KeyEvent.VK_ENTER);
        ctrlVSeq.add(KeyEvent.VK_CONTROL);
        ctrlVSeq.add(KeyEvent.VK_V);
    }

    // 发送消息[复制信息到剪切板 + enter + (ctrl + v) + enter]
    public static void sendNews0(String news) {
        copyDataToClipBoard(news);

        pressVK(enterSeq);
        pressVK(ctrlVSeq);
        pressVK(enterSeq);
    }

    // 按住keyCodes, 并松开
    public static void pressVK(List<Integer> keyCodes) {
        for(int i=0; i<keyCodes.size(); i++) {
            robot.keyPress(keyCodes.get(i));
        }
//      Tools.sleep(PAUSE_INTERVAL);
        for(int i=keyCodes.size()-1; i>=0; i--) {
            robot.keyRelease(keyCodes.get(i));
        }
    }

    // 将数据复制到剪切板
    private static void copyDataToClipBoard(Object obj) {
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        StringSelection ss = new StringSelection(obj.toString() );
        clipboard.setContents(ss, null);
    }

}

03 windows常亮

这是一个 由于以前遇到的一个问题, 因为 我的机器有时候睡眠之后, 在输入密码的话 网络就会断掉, 然后 再去点击链接无线的时候, 就会发现, 一个无线热点都不见了, 这时候 就只能重启电脑了,,,
还有一个场景就是玩游戏的有事的时候, 需要离开一会儿, 之后 回来的时候, 你会发现电脑休眠了, 然后重新登录时候, 就会发现游戏已经断开连接了..
所以 为了这几个问题, 我思考了一会儿 使用Robot定时模拟按键来维护windows的常亮

这里写图片描述

参考代码如下

/**
 * file name : Test06WindowsAlwaysOn.java
 * created at : 9:42:46 PM Aug 22, 2015
 * created by 970655147
 */

package com.hx.test01;

public class Test06WindowsAlwaysOn {

    // 是系统常亮[不会进入睡眠状态]
    public static void main(String []args) {

        alwaysOn();

    }

    // robot 模拟用户操作
    static Robot robot;
    static int SLEEP_INTERVAL = 20 * 1000;
    static Point pos;
    static {
        try {
            robot = new Robot();
            pos = new Point(100, 100);
        } catch (AWTException e) {
            e.printStackTrace();
        }
    }

    // 是系统常亮[不会进入睡眠状态]
    public static void alwaysOn() {
        while(true ) {
            Log.log("program run now : " + getCurrentTime() );
            Point pos = getNextPoint();
            robot.mouseMove(pos.x, pos.y);
            sleep(SLEEP_INTERVAL);
        }
    }

    // 获取下一个鼠标的位置
    private static Point getNextPoint() {
        return pos;
    }

    // 获取当前时间的字符串表示
    private static String getCurrentTime() {
        return new Date().toString();
    }

    // 是当前线程睡眠sleep秒
    public static void sleep(int sleep) {
        try {
            Thread.sleep(sleep);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

end

总结 : 还是那句话, 请慎用 !

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:71933次
    • 积分:2370
    • 等级:
    • 排名:第15759名
    • 原创:162篇
    • 转载:2篇
    • 译文:0篇
    • 评论:13条
    一名路过的黑客 [refer:chszs]
    大家好,我是黑客,专门盗账号的。现在这个人的帐号被我盗了,但看这个人平时的博客空间,一直过着艰苦努力、持之以恒的技术研究生活,勤奋刻苦,积极分享,无私奉献,我被深深的感动了,这是一个纯粹的人,人品这样的高尚,希望大家看到我这条消息后,可以私聊他,多鼓励他,不缺钱的就多给他一些经济上的资助,让他再接再厉!就这样吧,我下线了,眼框湿湿的难受。