使用selenium進行爬取掘金前端小冊的數據

Selenium 簡介

百度百科介紹:css

Selenium [1] 是一個用於Web應用程序測試的工具。Selenium測試直接運行在瀏覽器中,就像真正的用戶在操做同樣。支持的瀏覽器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。這個工具的主要功能包括:測試與瀏覽器的兼容性——測試你的應用程序看是否可以很好得工做在不一樣瀏覽器和操做系統之上。測試系統功能——建立迴歸測試檢驗軟件功能和用戶需求。支持自動錄製動做和自動生成 .Net、Java、Perl等不一樣語言的測試腳本。html

使用流程

  1. 根據平臺下載須要的webdrive
Browser Component
Chrome chromedriver(.exe)
Internet Explorer IEDriverServer.exe
Edge MicrosoftWebDriver.msi
Firefox geckodriver(.exe)
Safari safaridriver

根據本身的環境進行下載,將下載好的壓縮包解壓到項目的根目錄不須要安裝,各個瀏覽器的版本和dirver的版本的選擇須要相近,不能盲目選擇最新的版本,不然會出現意想不到的bug,建議最好將瀏覽器升級至最新的穩定版本並選擇對應的包。前端

  1. 下載依賴包
npm install selenium-webdriver
  1. 玩一下官方demo, 作適當的代碼修改並根據代碼進行註釋
// 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();
  }
})();

爬取掘金小冊數據

  • 實現的功能
    • 自動打開掘金頁面的首頁
    • 自動點擊小冊前端進行路由切換
    • 將前端的所有小冊數據爬取
  • 注意點
    • 因爲selenium內部都是基於promise進行的封裝,全部的方法調用其實返回的都是一個promise對象,所以會大量的使用async語法

自動打開掘金頁面的首頁

一句話搞定自動打開掘金首頁git

// 自動打開掘金
await driver.get('https://juejin.im/timeline');

自動點擊進行路由跳轉

  1. 在瀏覽器中,查看頁面的佈局結構,找到小冊的位置,如果使用jq進行dom選擇,則是 $('.main-header-box .nav-item:nth-of-type(4)')
    github

  2. 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)
  1. 當點擊小冊以後,會調用 await driver.sleep(1000),由於當頁面點擊以後,頁面會進行從新渲染,此時防止接下來的操做,獲取不到數據或者獲取的數據不是指望值,增長一個延遲確保數據的準確性

將前端的所有小冊數據爬取

  1. 根據driver.findElements(By.css('.list-wrap .books-list .item'))獲取前端小冊列表,須要注意的是,當頁面點擊navBar以後,頁面會進行從新渲染,可是此時如果直接去獲取小測列表將會存在風險,由於在頁面沒有渲染完成以前獲取不到指望值,而代碼也會異常終止程序的運行
  2. 進行迭代取出但願獲取的數據,根據itemInfo.findElement(By.css('.info .title')).getText()
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

  1. while能確保會不斷的獲取數據,直到頁面渲染完成獲取到指望的數據
  2. try catch 能夠保證程序在遇到異常時不會直接終止程序,能夠繼續運行
  3. 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;
    }
  }
}
相關文章
相關標籤/搜索