前言
今天, 说一下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();
}
}
}
======================= add at 2020.02.24 =======================
java – Robot.mouseMove在Mac OS X中根本不起作用
http://www.chinaoc.com.cn/p/1086831.html
在“系统偏好设置”(“齿轮”图标)中,在“安全”和“安全”下.隐私,单击顶部的“隐私”选项卡,然后选择左侧的“辅助功能”.这列出了可以“控制您的计算机”的所有程序.我正在使用STS,而不是IntelliJ.我看到STS与BetterSnapTool和KeyCastr一起列出.检查BetterSnapTool和KeyCastr.未检查STS.而且,所有这些都是灰色的,所以我不能改变任何检查.窗口左下角有一个Lock图标.我点击了图标,出现了一个提示,要求输入我的密码(我有一定级别的系统管理员权限).我输入了密码,现在我可以“检查”STS.我“检查”了STS(你将“检查”IntelliJ).在“检查”STS之后,我再次单击左下角的“锁定”图标.这个“关闭”了锁.现在,当我运行程序时,机器人命令会移动我的鼠标.
以上内容引用自上面的这个链接
end
总结 : 还是那句话, 请慎用 !