Java Selenium简单浏览器模拟
模拟登陆QQ邮箱为例
Maven中pom.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.wu</groupId>
<artifactId>Edge</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
</dependencies>
</project>
主要代码:
package com.wu;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.edge.EdgeDriver;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
public class Edge {
public static void main(String[] args){
boolean flag = true;
System.setProperty("webdriver.edge.driver","E:driver\\msedgedriver.exe");
WebDriver driver = new EdgeDriver();
driver.get("https://mail.qq.com/");
try{
driver.switchTo().frame(driver.findElement(By.id("login_frame"))); // 切换到相应的frame标签下
driver.findElement(By.id("switcher_plogin")).click();
}catch(Exception e){
// e.printStackTrace();
}
while(flag){
try{
WebElement input = driver.findElement(By.id("u"));
input.sendKeys("这里是你的账号");
WebElement password = driver.findElement(By.id("p"));
password.sendKeys("这里是你的密码");
driver.findElement(By.id("login_button")).click();
driver.switchTo().defaultContent(); // 由于前面已经位于子frame中,故当前需要切换到顶级frame中
Thread.sleep(500);
driver.switchTo().frame(driver.findElement(By.id("login_frame"))); // 切换到一级frame
driver.switchTo().frame(driver.findElement(By.id("tcaptcha_iframe"))); // 切换到二级frame
driver.findElement(By.id("tcaptcha_drag_thumb")).click();
mouseAction(840,570); // 滑动验证(不保证100%成功)
driver.findElement(By.id("tcaptcha_drag_thumb")).click();
flag = false;
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
* 鼠标移动事件
* @param x 横坐标
* @param y 总坐标
* @throws AWTException,InterruptedException
*/
private static void mouseAction(int x, int y) throws AWTException,InterruptedException{
Robot robot = new Robot();
robot.mouseMove(x,y); // 鼠标移动到当前位置
/**
以下仅供扩展学习
**/
// 简单模拟单击鼠标左右键
robot.mouseMove(x+400,y+200);
{
robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); // 按住鼠标右键
robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); // 释放鼠标右键
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); // 按住鼠标左键
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); // 释放鼠标左键
}
// 实现通过Utools打开百度首页
{
// 唤醒Utools
robot.keyPress(KeyEvent.VK_ALT);
robot.keyPress(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_ALT);
// 拼音百度
Thread.sleep(200);
robot.keyPress(KeyEvent.VK_B);
robot.keyRelease(KeyEvent.VK_B);
Thread.sleep(100);
robot.keyPress(KeyEvent.VK_A);
robot.keyRelease(KeyEvent.VK_A);
Thread.sleep(100);
robot.keyPress(KeyEvent.VK_I);
robot.keyRelease(KeyEvent.VK_I);
Thread.sleep(100);
robot.keyPress(KeyEvent.VK_D);
robot.keyRelease(KeyEvent.VK_D);
Thread.sleep(100);
robot.keyPress(KeyEvent.VK_U);
robot.keyRelease(KeyEvent.VK_U);
Thread.sleep(100);
robot.keyPress(KeyEvent.VK_1);
robot.keyRelease(KeyEvent.VK_1);
Thread.sleep(100);
robot.keyPress(KeyEvent.VK_ENTER);
robot.keyRelease(KeyEvent.VK_ENTER);
Thread.sleep(100);
robot.keyPress(KeyEvent.VK_ENTER);
robot.keyRelease(KeyEvent.VK_ENTER);
}
}
}
改进策略后…
package com.wu;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.edge.EdgeDriver;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
public class Edge {
private static WebDriver driver = null;
public static void main(String[] args){
System.setProperty("webdriver.edge.driver","E:driver\\msedgedriver.exe");
driver = new EdgeDriver();
driver.get("https://mail.qq.com/");
try{
driver.switchTo().frame(driver.findElement(By.id("login_frame"))); // 切换到相应的frame标签下
driver.findElement(By.id("switcher_plogin")).click();
}catch(Exception e){
// e.printStackTrace();
}
// 登录
{
WebElement input = driver.findElement(By.id("u"));
input.sendKeys("你的账号");
WebElement password = driver.findElement(By.id("p"));
password.sendKeys("你的密码");
driver.findElement(By.id("login_button")).click();
}
while(true){
try{
Thread.sleep(200);
driver.switchTo().defaultContent(); // 由于前面已经位于子frame中,故当前需要切换到顶级frame中
driver.switchTo().frame(driver.findElement(By.id("login_frame"))); // 切换到一级frame
driver.switchTo().frame(driver.findElement(By.id("tcaptcha_iframe"))); // 切换到二级frame
driver.findElement(By.id("tcaptcha_drag_thumb")).click();
Thread.sleep(200);
mouseAction();
driver.findElement(By.id("tcaptcha_drag_thumb")).click();
Thread.sleep(500);
// 直到成功为止或者失败
try{
WebElement reload = driver.findElement(By.id("e_reload"));
reload.click();
}catch(Exception e){
break;
}
}catch(Exception e){
// e.printStackTrace();
}
}
}
/**
* 鼠标移动事件
* @throws Exception
*/
private static void mouseAction() throws Exception{
Robot robot = new Robot();
int x,y = 570;
x = imgHandle();
robot.mouseMove(x,y); // 鼠标移动到当前位置
// System.out.println("横坐标为:"+x+",纵坐标为:"+y);
}
/**
* 网络获取照片
* @return 移动的横坐标
* @throws Exception
*/
private static int imgHandle() throws Exception{
WebElement img = driver.findElement(By.id("slideBg"));
URL url = new URL(img.getAttribute("src"));
int result = 0;
try{
DataInputStream dataInputStream = new DataInputStream(url.openStream());
/**
DataInputStream tempInputStream = new DataInputStream(url.openStream());
String image = "D:\\test.jpg";
FileOutputStream fileOutputStream = new FileOutputStream(new File(image));
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
// dataInputStream.read(byte[] a); 该方法返回写入字符数组返回buffer末端位置
while((length = tempInputStream.read(buffer)) > 0){
output.write(buffer,0,length);
}
byte[] context = output.toByteArray();
// handleBytes(context);
fileOutputStream.write(output.toByteArray()); // 将字节流写入.jpg文件中
fileOutputStream.close();
tempInputStream.close();
**/
result = bytesHandle(dataInputStream);
dataInputStream.close();
}catch(Exception e){
e.printStackTrace();
}
return result;
}
/**
* 处理边界像素
* @param inputStream
* @return 移动的横坐标
* @throws Exception
*/
private static int bytesHandle(InputStream inputStream) throws Exception{
BufferedImage bufferedImage = ImageIO.read(inputStream);
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
int minx = bufferedImage.getMinX();
int miny = bufferedImage.getMinY();
int[][][] rgb = new int[width][height][3];
int[] temp = new int[width+1]; // 用于计数同一横坐标的个数
for(int i = minx;i < width ; i++){
for(int j = miny ; j < height ; j++){
int pixel = bufferedImage.getRGB(i, j); // 下面三行代码将一个数字转换为RGB数字
rgb[i][j][0] = (pixel & 0xff0000) >> 16;
rgb[i][j][1] = (pixel & 0xff00) >> 8;
rgb[i][j][2] = (pixel & 0xff);
// 查找右边界策略
if((rgb[i][j][0] >= 250 && rgb[i][j][1] >= 250 && rgb[i][j][2] >= 224) ||
(rgb[i][j][0] >= 250 && rgb[i][j][2] >= 250 && rgb[i][j][1] >= 224) ||
(rgb[i][j][1] >= 250 && rgb[i][j][2] >= 250 && rgb[i][j][0] >= 224)
){
temp[i+1]++;
}
}
}
int rightMargin = temp[1];
int result = 0;
for(int i = 1 ; i < width ; i++){
if(rightMargin < temp[i]){
rightMargin = temp[i];
result = i;
}
}
// 最终计算需要移动横坐标所需要的像素
System.out.println("照片坐标系右边界的横坐标为:"+result);
return 680+result/(680/283)-75; // 需要找到固定的初始位置,即这里需要自定义微调
}
}
简单分析一下,这里如果出现类似这种滑动验证码,由于边界大致是基于白色的,所以一般很难寻找到边界,因此程序会执行‘刷新’…
但是类似一下这种就一般可以直接命中…
总而言之,是否容易命中取决于概率加上边界寻找策略。读者可以自己尝试以及修改代码,来实现自己理想的效果。