百度百科介紹:css
Selenium [1] 是一個用於Web應用程序測試的工具。Selenium測試直接運行在瀏覽器中,就像真正的用戶在操做同樣。支持的瀏覽器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。這個工具的主要功能包括:測試與瀏覽器的兼容性——測試你的應用程序看是否可以很好得工做在不一樣瀏覽器和操做系統之上。測試系統功能——建立迴歸測試檢驗軟件功能和用戶需求。支持自動錄製動做和自動生成 .Net、Java、Perl等不一樣語言的測試腳本。html
Browser | Component |
---|---|
Chrome | chromedriver(.exe) |
Internet Explorer | IEDriverServer.exe |
Edge | MicrosoftWebDriver.msi |
Firefox | geckodriver(.exe) |
Safari | safaridriver |
根據本身的環境進行下載,將下載好的壓縮包解壓到項目的根目錄不須要安裝,各個瀏覽器的版本和dirver的版本的選擇須要相近,不能盲目選擇最新的版本,不然會出現意想不到的bug,建議最好將瀏覽器升級至最新的穩定版本並選擇對應的包。前端
npm install selenium-webdriver
// 1. 引入selenium-webdriver包,解構須要的對象和方法 const {Builder, By, Key, until} = require('selenium-webdriver'); // 2. 將須要的代碼包在一個自執行函數中 (async function example() { // 實例化 driver 對象, chrome 表明使用的瀏覽器 let driver = await new Builder().forBrowser('chrome').build(); try { // 須要打開的網站地址 await driver.get('https://www.baidu.com/'); // Key.RETURN enter回車 // By.id('id') 百度查詢滴輸入內容 // 找到元素 向裏面發送一個關鍵字並按回撤 await driver.findElement(By.id('kw')).sendKeys('酒店', Key.RETURN); // 等1秒以後,驗證是否搜索成功 // await driver.wait(until.titleIs('酒店_百度搜索'), 1000); } finally { // 關閉瀏覽器 // await driver.quit(); } })();
一句話搞定自動打開掘金首頁git
// 自動打開掘金 await driver.get('https://juejin.im/timeline');
在瀏覽器中,查看頁面的佈局結構,找到小冊的位置,如果使用jq進行dom選擇,則是 $('.main-header-box .nav-item:nth-of-type(4)')
github
selenium中的By擁有不少的選擇,其使用規則和jq很是類似,By.css('.main-header-box .nav-item:nth-of-type(4)')即可以找到對應的元素,調用click事件就能夠模擬自動點擊web
// 點擊小冊子的navBar 切換路由到小冊 await driver .findElement(By.css('.main-header-box .nav-item:nth-of-type(4)')) .click(); await driver.sleep(1000)
while (true) { let listViewError = true; try { // 獲取小冊列表 let _li = await driver.findElements(By.css('.list-wrap .books-list .item')); console.log(_li.length); for (let i = 0, _len = _li.length; i < _len; i++) { const itemInfo = _li[i]; const title = await itemInfo.findElement(By.css('.info .title')).getText() const desc = await itemInfo.findElement(By.css('.info .desc')).getText() let price = await itemInfo.findElement(By.css('.info .price-text')).getText() _result.push({ title, desc, price }) } console.log('_result',_result); } catch (error) { if (error) listViewError = false; } finally { if (listViewError) break; } }
在獲取列表的時候,爲何會在最外層增長一個while呢?在 try catch 中的處理又是起到什麼做用?chrome
- while能確保會不斷的獲取數據,直到頁面渲染完成獲取到指望的數據
- try catch 能夠保證程序在遇到異常時不會直接終止程序,能夠繼續運行
- listViewError表示程序是否存在異常狀況,如果存在則會進入 catch 中,這個時候 listViewError 爲 false,finally 則不會走break,會繼續執行while程序,直到能獲取到數據finally才爲true,這個時候 finally中則會break 整個while的循環,跳出循環繼續執行
幾十行的代碼即可以將掘金的小冊數據所有爬到,仍是簡單和好用的npm
所有代碼api
/* * @Author: nordon-wang * @Date: 2019-08-13 11:05:36 * @Description: 爬取掘金數據 */ const { Builder, By, Key, until } = require('selenium-webdriver'); let _result = []; // 用來收集獲取的數據 (async function start() { let driver = await new Builder().forBrowser('chrome').build(); try { // 自動打開掘金 await driver.get('https://juejin.im/timeline'); // 點擊小冊子的navBar 切換路由到小冊 await driver .findElement(By.css('.main-header-box .nav-item:nth-of-type(4)')) .click(); await driver.sleep(1000) // 點擊二級菜單 await clickViewNav(driver); await driver.sleep(1000) // 獲取數據 await getList(driver); } catch (error) { console.log(error); } finally { let timer = setTimeout(async () => { clearTimeout(timer); await driver.quit(); }, 600000); } })(); // 獲取渲染完成的按鈕 async function clickViewNav(driver) { while (true) { let viewNavError = true; try { await driver .findElement(By.css('.main-container .view-nav .nav-item:nth-of-type(2)')) .click(); } catch (error) { if (error) viewNavError = false; } finally { if (viewNavError) break; } } } // 獲取列表數據 // 頁面在渲染完成以前沒法獲取到頁面的元素 async function getList(driver) { while (true) { let listViewError = true; try { // 獲取小冊列表 let _li = await driver.findElements(By.css('.list-wrap .books-list .item')); console.log(_li.length); for (let i = 0, _len = _li.length; i < _len; i++) { const itemInfo = _li[i]; const title = await itemInfo.findElement(By.css('.info .title')).getText() const desc = await itemInfo.findElement(By.css('.info .desc')).getText() let price = await itemInfo.findElement(By.css('.info .price-text')).getText() _result.push({ title, desc, price }) } console.log('_result',_result); } catch (error) { if (error) listViewError = false; } finally { if (listViewError) break; } } }