囉嗦一下,我使用這個的緣由,個人房子到期了,想在官網上租賃一套公租房,由於房源緊張因此要不停的刷頁面,並且還有極驗的驗證碼,搞得很煩,效率也低,以後我就想寫一段程序,用來自動刷新頁面,若是有房子就給我發郵件。css
我遇到的第一個難題,就是頁面有極驗驗證,因此我就搜索了幾個破解極驗驗證的代碼,本身嘗試。html
原文連接:https://blog.csdn.net/qq_28379809/article/details/81210761java
首先,要想運行程序,須要一個chromedriver.exe文件,有須要能夠從個人百度雲盤上下載git
連接:https://pan.baidu.com/s/1scnppvHSjd02VrJkDQdNNQ
提取碼:ewyx
其次:我再部署maven項目的時候遇到org.mail不能使用,以後我是下載了jar包構建了才能夠的,這裏附上jar包web
連接:https://pan.baidu.com/s/1Mi9oNNRD_2tW-F47KZGqXg chrome
提取碼:58jp 數組
最後咱們能夠開始講解代碼了瀏覽器
想了解selenium是什麼的朋友,能夠請轉百度。併發
第一步dom
導入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有不足之處,請大神多多指教,謝謝