前言:
最近玩爬蟲的時候,遇到一個國外的圖片網站,具體哪個就不說了,這個站很有意思,即使拿到了圖片的鏈接,用httpclient下載都不行,不是User-Agent的原因,不知道圖片服務器的後端有什麼校驗,沒辦法了,只能用Selenium上了,js逆向成本太高了(其實是我不擅長0.0)
這個站用的:
下面進入正題:
既然拿到圖片url也不能用httpclient下載了,那我直接下載整個網頁怎麼樣?下載整個網頁,圖片不就也保存下來了嗎?
想到這個辦法後我立馬動手了起來,保存網頁可以鼠標右鍵另存爲,也可以鍵盤Ctrl+S
當然是鍵盤Ctrl+S 更簡單啦
一、怎麼在Selenium裏面調用鍵盤事件Ctrl+S呢?
我試過new 一個Action,調用sendKeys方法,不管用;然後考慮用awt包下的Robot類;
聽名字也知道是一個機器人類,裏面有很多模擬鍵盤和鼠標操作的API
二、Ctrl+S之後文件名怎麼修改?怎麼點擊保存?
這就要用到有個AutoIt的工具,這工具可以模擬windows環境下的鍵鼠操作,但是比Robot類更靈活,Robot類的鼠標移動只能通過像素點來確定移動位置,而AutoIt可以根據預先寫好的腳步,自動定位到Windows窗口的按鈕、輸入框等等
下載地址:https://www.autoitscript.com/site/autoit/downloads/
安裝完成之後先打開AutoIt Windows Info
打開之後:
打開一個網頁的另存爲的窗口,點擊拖動Finder Tool
點擊右邊control面板可以看到,窗口的一些信息,id,class這些等會都要用到;
同理,點擊拖動Finder Tool到保存按鈕那裏,也能看到對應按鈕的信息
然後打開第二個工具上面截圖中的 SciTE Script Editor來編輯腳本:
這個工具很強,有很多用法,想具體學習可以參考:https://www.jb51.net/shouce/autoit/?tdsourcetag=s_pctim_aiomsg
下面是我用的腳本:
;該腳本的語法是: ;分號代表註釋
;ControlFocus ( "title", "窗口文本", controlID) 設置輸入焦點到指定窗口的某個控件上
;WinWait ( "title題" , "窗口文本" , 超時時間 ) 暫停腳本的執行直至指定窗口存在(出現)爲止
;ControlSetText ( "title", "窗口文本", controlID, "新文本" ) 修改指定控件的文本
;Sleep ( 延遲 ) 使腳本暫停指定時間段
;ControlClick ( "title", "窗口文本", 控件ID , 按鈕 , 點擊次數 ) 向指定控件發送鼠標點擊命令
;其中,title即AutoIt Window Info識別出的Title字段,controlID即AutoIt Window Info識別
;出的Class和Instance的拼接,如上圖拼接後的結果應爲:Button1
ControlFocus("另存爲","","")
;暫停腳本的執行直至指定窗口存在(出現)爲止
WinWait("[CLASS:#32770]","",10)
;第二步:填充文件名地址,其中$CmdLine[1]代表exe執行時的動態參數,
;例如 Runtime.getRuntime().exec("D:\\saveAs.exe "+文件名),這樣就可以動態改變文件名
ControlSetText("另存爲","","Edit1",$CmdLine[1])
;延時函數
;Sleep(2000)
;第三步:點擊保存按鈕,進行下載,title:另存爲,"text"寫成空,controlId:寫成Button2
;Button2是剛剛拖動Finder Tool獲取到的“保存”按鈕的id,可能每個人不一樣
ControlClick("另存爲","","Button2")
點擊保存之後,放到D盤,然後鼠標右鍵點擊compile script完成編譯之後,會在D盤生成一個文件名一樣的exe可執行文件,等會就要調用這個文件來執行腳本
三、下面就是java代碼了
@Autowired
private WebDriverPool webDriverPool;
public void getPic() throws Exception {
// 這裏用隊列實現的一個WebDriverPool,也可以自己new一個,但是比較耗資源
ChromeDriver chromeDriver = webDriverPool.get();
ChromeDriver openDetailDriver = webDriverPool.get();
for (int i = 1; i < 200; i++) {
// 打開網址主頁
chromeDriver.get("https://***.com/page/" + i);
Thread.sleep(1000);
List<WebElement> elements = chromeDriver.findElements(By.xpath("//article[@class='item-list']"));
for (WebElement element : elements) {
WebElement alabel = element.findElement(By.xpath(".//div[@class='post-thumbnail']/a"));
WebElement titleElement = element.findElement(By.xpath(".//h2[@class='post-box-title']/a"));
String pictureDetailPage = alabel.getAttribute("href");
String picTitle = titleElement.getText();
log.info("圖片首頁地址:{}", pictureDetailPage);
openHomePage(openDetailDriver, false, pictureDetailPage, picTitle);
}
}
webDriverPool.closeAll();
}
/**
* 打開每個圖片的主頁
*
* @param url 主頁地址
* @throws Exception
*/
public void openHomePage(ChromeDriver chromeDriver, boolean recursive, String url, String picTitle) throws Exception {
chromeDriver.get(url);
Thread.sleep(5000);
Robot robot = new Robot();
while (true) {
try {
// 等待頁面加載完成
WebDriverWait webDriverWait = new WebDriverWait(chromeDriver, 10);
webDriverWait.until(ExpectedConditions.presenceOfElementLocated(By.id("fukie2")));
log.info("加載完畢");
break;
} catch (Exception e) {
log.info("打開頁面失敗,刷新");
chromeDriver.navigate().refresh();
Thread.sleep(10000);
}
}
robot.keyPress(KeyEvent.VK_CONTROL);
robot.keyPress(KeyEvent.VK_S);
robot.keyRelease(KeyEvent.VK_S);
robot.keyRelease(KeyEvent.VK_CONTROL);
Thread.sleep(1000);
// 調用autoIt動態修改文件名,避免文件名重複
Runtime.getRuntime().exec("D:\\saveAs.exe " + picTitle + random.nextInt(10000));
Thread.sleep(1000);
Thread.sleep(10000);
if (!recursive) {
List<WebElement> pages = chromeDriver.findElements(By.xpath("//div[@id='fukie2']/div[2]/a"));
for (int i = 1; i <= pages.size(); i++) {
log.info("當前下載第{}頁", i + 1);
openHomePage(chromeDriver, true, url + "" + (i + 1), picTitle);
}
log.info("下載完當前頁,關閉");
}
}
在SpringBoot項目中使用awt包下的類,需要在啓動類加一行代碼:
System.setProperty("java.awt.headless", "false");
運行代碼,你會看到每一頁都被下載下來了,下載下來的網頁裏面包含了html、css、js、jpg,最後只需要過濾提取圖片就行了。這樣開發效率高,但是也有問題,如果需要多線程同時打開多個chrome下載的話,Robot類的鍵盤操作是需要聚焦的,也就是說如果採用多線程爬取,就需要在多個chrome中切換窗口才能高性能的下載,如果有高人有更好的解決辦法的話,請不吝賜教
以上是全文,如有不正確的地方,歡迎交流