啰嗦一下,我使用这个的原因,我的房子到期了,想在官网上租赁一套公租房,因为房源紧张所以要不停的刷页面,而且还有极验的验证码,搞得很烦,效率也低,之后我就想写一段程序,用来自动刷新页面,如果有房子就给我发邮件。
我遇到的第一个难题,就是页面有极验验证,所以我就搜索了几个破解极验验证的代码,自己尝试。
原文链接:https://blog.csdn.net/qq_28379809/article/details/81210761
首先,要想运行程序,需要一个chromedriver.exe文件,有需要可以从我的百度云盘上下载
链接:https://pan.baidu.com/s/1scnppvHSjd02VrJkDQdNNQ
提取码:ewyx
其次:我再部署maven项目的时候遇到org.mail不能使用,之后我是下载了jar包构建了才可以的,这里附上jar包
链接:https://pan.baidu.com/s/1Mi9oNNRD_2tW-F47KZGqXg
提取码:58jp
最后我们可以开始讲解代码了
想了解selenium是什么的朋友,可以请转百度。
第一步
导入Maven依赖,javax.mail可以在上文直接下载jar包
<dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-server</artifactId> <version>3.0.1</version> </dependency> <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup --> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.7.2</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency>
第二步:
声明常量,包括url,图片名称,北京图片名称,和图片存储路径等,并且使用静态块,获取谷歌驱动
private static String basePath = "src/main/resources/"; private static String FULL_IMAGE_NAME = "full-image"; private static String BG_IMAGE_NAME = "bg-image"; private static int[][] moveArray;// = new int[52][2]; private static boolean moveArrayInit = false; private static int pieceNumber = 0; // 小图片数量 private static String INDEX_URL = "http://117.71.57.99:9080/online/roomResource.xp?action=showResource";//测试网站登录验证码 private static WebDriver driver;
static {
System.setProperty("webdriver.chrome.driver", "D://git//selenium-geetest-crack//src//file//chromedriver.exe"); // chromedriver.exe的存放路径
driver = new ChromeDriver();
}
第三步:
先写个main函数,在其中调用iinvoke()方法
注意 selenum有多种定位元素位置的方法,具体可参考以下两篇文章:https://www.cnblogs.com/csj2018/p/9194618.html && https://www.cnblogs.com/111testing/p/8100289.html
private static void invoke() throws IOException, RuntimeException, InterruptedException, StaleElementReferenceException{ //设置input参数 driver.get(INDEX_URL); Thread.sleep(3000);// 为防止页面加载验证码缓慢,获取不到验证按钮,特地增加,如果浏览器加载速度较快,可以删除 By moveBtn = By.cssSelector(".gt_slider_knob.gt_show");//根据class获取滑动验证按钮 waitForLoad(driver, moveBtn);//等待加载元素 WebElement moveElemet = driver.findElement(moveBtn);//定位元素位置 int i = 0; while (i++ < 8) { int distance = getMoveDistance(driver);// 获取图片偏移量 move(driver, moveElemet, distance - 6);// 移动按钮 // 移动之后,无论结果如何,会出现下面两个class为gt_info_type和gt_info_content的标签,注意:如果没有移动是获取不到的 By gtTypeBy = By.cssSelector(".gt_info_type");//验证结果类型 By gtInfoBy = By.cssSelector(".gt_info_content");//验证结果内容 waitForLoad(driver, gtTypeBy); String gtType = driver.findElement(gtTypeBy).getText(); waitForLoad(driver, gtInfoBy); String gtInfo = driver.findElement(gtInfoBy).getText();//StaleElementReferenceException //System.out.println(gtType + "---" + gtInfo); if(gtType.contains("验证通过")){ // 获取页面源代码 String source = driver.getPageSource(); // 原页面返回roomColor在后面拼接状态,状态为04则表示没有房子,状态为02则表示有房子 if (source.contains("class=\"roomColor02\"")){ isRoomText = "有房子啦"; SendEmail.sendTextEmail(); // 发送邮件,有需要的话,可以将得到的信息解析出来,一并发送出去 }else{ isRoomText = "还没有房子"; } System.out.println(isRoomText); } /** * 再来一次: * 验证失败: */ if (!gtType.equals("再来一次:") && !gtType.equals("验证失败:")) { Thread.sleep(2000); //System.out.println(driver); break; } Thread.sleep(2000); } }
第四步:
计算出具体的偏移量
/** * 计算需要平移的距离 * * @param driver * @return * @throws IOException */ public static int getMoveDistance(WebDriver driver) throws IOException , RuntimeException{ String pageSource = driver.getPageSource(); // 获取网页源代码 // 获取元素图片路径 以及 获取原始带背景图片路径 String fullImageUrl = getFullImageUrl(pageSource); String getBgImageUrl = getBgImageUrl(pageSource); // 将两张图片拷贝到本机地址 FileUtils.copyURLToFile(new URL(fullImageUrl), new File(basePath + FULL_IMAGE_NAME + ".jpg")); FileUtils.copyURLToFile(new URL(getBgImageUrl), new File(basePath + BG_IMAGE_NAME + ".jpg")); // 获取已经错位的图片地址 initMoveArray(driver); // 拼接融合图片 restoreImage(FULL_IMAGE_NAME); restoreImage(BG_IMAGE_NAME); // 根据两张图片计算出偏移的位置 BufferedImage fullBI = ImageIO.read(new File(basePath + "result/" + FULL_IMAGE_NAME + "result3.jpg")); BufferedImage bgBI = ImageIO.read(new File(basePath + "result/" + BG_IMAGE_NAME + "result3.jpg")); for (int i = 0; i < bgBI.getWidth(); i++) { for (int j = 0; j < bgBI.getHeight(); j++) { int[] fullRgb = new int[3]; fullRgb[0] = (fullBI.getRGB(i, j) & 0xff0000) >> 16; fullRgb[1] = (fullBI.getRGB(i, j) & 0xff00) >> 8; fullRgb[2] = (fullBI.getRGB(i, j) & 0xff); int[] bgRgb = new int[3]; bgRgb[0] = (bgBI.getRGB(i, j) & 0xff0000) >> 16; bgRgb[1] = (bgBI.getRGB(i, j) & 0xff00) >> 8; bgRgb[2] = (bgBI.getRGB(i, j) & 0xff); if (difference(fullRgb, bgRgb) > 255) { return i; } } } throw new RuntimeException("未找到需要平移的位置"); } /** * 获取原始图url * * @param pageSource * @return */ private static String getFullImageUrl(String pageSource) { String url = null; Document document = Jsoup.parse(pageSource); String style = document.select("[class=gt_cut_fullbg_slice]").first().attr("style"); Pattern pattern = Pattern.compile("url\\(\"(.*)\"\\)"); Matcher matcher = pattern.matcher(style); if (matcher.find()) { url = matcher.group(1); } url = url.replace(".webp", ".jpg"); //System.out.println(url); return url; } /** * 获取带背景的url * * @param pageSource * @return */ private static String getBgImageUrl(String pageSource) { String url = null; Document document = Jsoup.parse(pageSource); String style = document.select(".gt_cut_bg_slice").first().attr("style"); Pattern pattern = Pattern.compile("url\\(\"(.*)\"\\)"); Matcher matcher = pattern.matcher(style); if (matcher.find()) { url = matcher.group(1); } url = url.replace(".webp", ".jpg"); //System.out.println(url); return url; } /** * 获取move数组 * * @param driver */ private static void initMoveArray(WebDriver driver) { if (moveArrayInit) { return; } Document document = Jsoup.parse(driver.getPageSource()); Elements elements = document.select("[class=gt_cut_bg gt_show]").first().children(); // 获取底图错位后的图片元素们 int i = 0; pieceNumber = elements.size(); moveArray = new int[pieceNumber][2]; for (Element element : elements) { Pattern pattern = Pattern.compile(".*background-position: (.*?)px (.*?)px.*"); Matcher matcher = pattern.matcher(element.toString()); if (matcher.find()) { String width = matcher.group(1); String height = matcher.group(2); moveArray[i][0] = Integer.parseInt(width); moveArray[i++][1] = Integer.parseInt(height); } else { throw new RuntimeException("解析异常"); } } moveArrayInit = true; } /** * 还原图片 * * @param type */ private static void restoreImage(String type) throws IOException { //把图片裁剪为2 * 26份 for (int i = 0; i < pieceNumber; i++) { cutPic(basePath + type + ".jpg" , basePath + "result/" + type + i + ".jpg", -moveArray[i][0], -moveArray[i][1], 10, 58); } //拼接图片 String[] b = new String[(int)pieceNumber/2]; for (int i = 0; i < (int)pieceNumber/2; i++) { b[i] = String.format(basePath + "result/" + type + "%d.jpg", i); } mergeImage(b, 1, basePath + "result/" + type + "result1.jpg"); //拼接图片 String[] c = new String[(int)pieceNumber/2]; for (int i = 0; i < (int)pieceNumber/2; i++) { c[i] = String.format(basePath + "result/" + type + "%d.jpg", i + (int)pieceNumber/2); } mergeImage(c, 1, basePath + "result/" + type + "result2.jpg"); mergeImage(new String[]{basePath + "result/" + type + "result1.jpg", basePath + "result/" + type + "result2.jpg"}, 2, basePath + "result/" + type + "result3.jpg"); //删除产生的中间图片 for (int i = 0; i < pieceNumber; i++) { new File(basePath + "result/" + type + i + ".jpg").deleteOnExit(); } new File(basePath + "result/" + type + "result1.jpg").deleteOnExit(); new File(basePath + "result/" + type + "result2.jpg").deleteOnExit(); } private static int difference(int[] a, int[] b) { return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]) + Math.abs(a[2] - b[2]); }
第五步;
拖住验证按钮,并根据偏移量移动位置,此处因为原移动公式,对我的验证码成功率不高,所以我自己改进了一下
此处可根据自己要操作的验证码界面自己调试
该函数中,包含selenium对鼠标的操作,详情可参考:https://www.cnblogs.com/lingling99/p/5750266.html
/** * 移动 * * @param driver * @param element * @param distance * @throws InterruptedException */ public static void move(WebDriver driver, WebElement element, int distance) throws InterruptedException { int xDis = distance;// 偏移量 int moveX = new Random().nextInt(5) - 2;//生成随机数 int moveY = 1; Actions actions = new Actions(driver); new Actions(driver).clickAndHold(element).perform();//单击拖住按钮 Thread.sleep(1000);//slow down // 偏移量 int offset = (xDis+moveX)/2; actions.moveByOffset(offset,moveY).perform();// 第一次偏移 Thread.sleep((int)(Math.random()*1000)); actions.moveByOffset(distance-offset-moveX,moveY).perform();//第二次偏移 Thread.sleep(500); actions.release(element).perform();// 释放按钮 }
最后,发送邮件那块,可以参考我之前写的文章进行操作,用不着ical4j可以删除,下方是连接,该篇文章底部有java email发送邮件时出现的问题总结,希望对大家有所帮助
https://www.cnblogs.com/fuhui-study-footprint/p/8464968.html
此处用于笔记,第一次使用selenium有不足之处,请大神多多指教,谢谢