在工作中,我們經常需要去獲取一些資料,但是這些資料可能需要從第三方平臺才可以獲取到。這個時候,爬蟲系統就可以幫助我們來完成這些事情。
提到爬蟲系統,很多人都會想到使用python。但實際上,語言只是一種工具,其背後的設計思想和技術原理才是精髓,這篇關於Java分散式爬蟲的文章會帶著大家一步一步搭建一個適合Java開發者的爬蟲系統。
現在,我們就來嘗試下通過自動化方法來獲取https://www.cnblogs.com/的首頁內容。在正式開始編寫程式碼之前,我們需要安裝兩個重要的程式,一個是chromedriver,一個是chrome。
chrome瀏覽器的下載地址:https://chrome.en.softonic.com/
chromedriver下載地址:http://chromedriver.storage.googleapis.com/index.html
注意:在安裝這兩個軟體的時候,它們的版本需要對應起來才能正常work。
接下來我要給大家介紹一下Selenium webdriver這個開源元件,Selenium是一個用於Web應用程式測試的工具。Selenium測試直接執行在瀏覽器中,就像真正的使用者在操作一樣。支援的瀏覽器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera,Edge等。Selenium webdriver是程式語言和瀏覽器之間的通訊工具,它的工作流程如下圖所示。
@Service
public class WebDriverFactory {
@Value("${chrome.path}")
private String chromePath;
@Autowired
private ProxyPool proxyPool;
public WebDriver createWebDriver(boolean useProxy) {
System.setProperty(ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY, "/Users/****/Downloads/chromedriver");
ArrayList<String> arguments = Lists.newArrayList("--no-sandbox",
"--disable-dev-shm-usage",
"--disable-web-security",
"--ignore-certificate-errors",
"--allow-running-insecure-content",
"--allow-insecure-localhost",
"--disable-images",
"--disable-gpu",
"--disable-blink-features=AutomationControlled",
"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.16 Safari/537.36",
"--cache-control=no-cache");
ChromeOptions options = new ChromeOptions();
options.setHeadless(true);
options.addArguments(arguments);
/** 設定使用代理 **/
if (useProxy) {
Proxy proxy = proxyPool.getProxy();
options.setProxy(proxy);
}
Map<String, Object> prefs = Maps.newHashMap();
prefs.put("profile.default_content_settings.popups", 1);
prefs.put("profile.default_content_setting_values.notifications", 1);
options.setExperimentalOption("prefs", prefs);
ChromeDriver webDriver = new ChromeDriver(options);
Map<String, Object> params = Maps.newHashMap();
// params.put("source", "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})");
params.put("source", "() => {" +
" if (navigator.webdriver === false) {" +
" continue" +
" } else if (navigator.webdriver === undefined) {" +
" continue" +
" } else {" +
" delete Object.getPrototypeOf(navigator).webdriver" +
" }" +
" }");
webDriver.executeCdpCommand("Page.addScriptToEvaluateOnNewDocument", params);
webDriver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS).pageLoadTimeout(20, TimeUnit.SECONDS)
.setScriptTimeout(10, TimeUnit.SECONDS);
return webDriver;
}
}
@Test
public void testGrabPage() {
WebDriver webDriver = null;
try {
String currentPageUrl = "https://xiaozhuanlan.com/";
webDriver = webDriverFactory.createWebDriver(false);
webDriver.get(currentPageUrl);
Thread.sleep(1000);
String html = webDriver.getPageSource();
System.out.println(html);
} catch (Exception e) {
e.printStackTrace();
} finally {
webDriver.quit();
}
}
在上一個部分中,我們可以獲取到「部落格園」首頁的完整內容,在這一篇文章中我們將實現在百度網站自動化搜尋「部落格園「,並且跳轉到「部落格園」首頁。
在實現模擬登入之前,我們需要掌握如何定位到自己關心的元素。Selenium中有8種方法可以定位到元素。具體的定位方法可以檢視org.openqa.selenium.By這個類。假設我們現在需要定位到如下一個元素:
<tagName attributeName='attributeValue'></tagName>
那麼我們可以根據以下的方法進行定位:
上述介紹的元素定位方法如果發現有多個元素可以匹配的,則會選擇該頁面中第一個符合條件的元素。
接下來,我們編寫模擬使用者搜尋「部落格園」行為的程式碼,
@Test
public void searchTest() {
FenbiChromeDriver webDriver = null;
try {
webDriver = (FenbiChromeDriver) webDriverFactory.createWebDriver(false);
String currentPageURL = "http://www.baidu.com";
webDriver.get(currentPageURL);
Thread.sleep(2000);
WebElement searchInputElem = webDriver.findElement(By.xpath("//*[@id=\"kw\"]"));
searchInputElem.sendKeys("部落格園");
WebElement searchButtonElem = webDriver.findElement(By.xpath("//*[@id=\"su\"]"));
searchButtonElem.click();
Thread.sleep(2000);
WebElement searchResultList = webDriver.findElement(By.xpath("//*[@id=\"content_left\"]"));
WebElement xiaozhuanlanElem = searchResultList.findElement(By.xpath("//*[@id=\"1\"]/div/div[1]/h3/a"));
xiaozhuanlanElem.click();
Thread.sleep(2000);
System.out.println(webDriver.getPageSource());
} catch (Exception e) {
e.printStackTrace();
} finally {
webDriver.quit();
}
}
結合上一部分中的WebDriverFactory,並執行上面的程式碼,我們就可以自動跳轉到部落格園網站的首頁了。
當我們需要判斷我們關注的元素是否載入完畢的時候,在Selenium框架下有隱式等待和顯式等待兩種方式。
隱式等待是在建立webdriver的時候設定的超時時間,在整個的webdriver生命週期內都是有效的。設定了隱式等待後,Selenium在執行findElement的DriverCommand時候會一直等待,直到獲取到對應的元素。設定隱式等待的方法如下:
接下來,我們跟隨Selenium的隱式等待模式來看看Selenium抓取網頁的處理流程是什麼樣的。
@Test
public void webDriverWaitTest() {
FenbiChromeDriver webDriver = null;
try {
webDriver = (FenbiChromeDriver) webDriverFactory.createWebDriver(false);
new WebDriverWait(webDriver, 20).until((Function<WebDriver, Boolean>) driver -> {
String currentPageURL = "http://www.baidu.com";
driver.get(currentPageURL);
String html = driver.getPageSource();
if(html.contains("hello word")) {
return true;
} else {
return false;
}
});
} catch (Exception e) {
e.printStackTrace();
} finally {
webDriver.quit();
}
}