萬物皆可爬-puppeteer進階

萬物皆可爬-puppeteer進階

開始前

上篇文章,講解了如何快速使用puppeteer爬取一個簡單的視頻網站,可是,邏輯比較簡單,投機取巧,直接利用詳情頁分頁的邏輯爬取電視劇,可是這樣只能爬取 某一部電視劇,電影。這樣比較煩。想作本身的視頻網站仍是有點雞肋。固然還有一個棘手的問題,若是詳情頁有token驗證怎麼辦。javascript

本文涉及相關僅供學習交流,侵聯改java

內容預告

本文就這幾個問題進行解答:web

  1. 一鍵爬取某視頻網站所有電影/電視劇
  2. 自動填充基礎表單
  3. page.click()
  4. 滑塊驗證碼破解

視頻站

梳理一下爬取視頻網站須要幹那些事:數據庫

  1. 找到一個你喜歡的網站
  2. 爬取列表頁 - 獲取每一個視頻的詳情頁地址(注意:通常的列表頁都有分頁)
  3. 跳轉至詳情頁
  4. 獲取視頻播放地址 - 若是還須要選擇資源則再進行一次跳轉
  5. 關閉詳情頁

重複 3 ~ 5,待當前列表頁全部內容爬取後,跳轉至列表第二頁,以此類推,等所有列表頁爬取完畢後關閉瀏覽器。瀏覽器

知道步驟後,那麼開始吧bash

找到網站,分析它

爲了不廣告的嫌疑,demo中的視頻網址使用假地址,若是想要地址,請評論app

這是它的列表頁,能夠看出來,他是有分頁的。有兩種辦法less

  1. 分析它的分頁規則
  2. 使用page.click()

先使用分析分頁規則的方案實現dom

http://www.xxxxxxx.xx
http://www.xxxxxxx.xx/page/2
http://www.xxxxxxx.xx/page/3
...
複製代碼

它是什麼規則不用多介紹了吧async

開始爬

列表頁

若是對puppeteer API 不熟悉的,請移步萬物皆可爬-puppeteer實戰

const findAllMovie = async () => {
  console.log('開始參觀這個網站')
  const browser = await (puppeteer.launch({
    executablePath: puppeteer.executablePath(),
    headless: false
  }));
  /* @params 
   * pageSize:你想爬多少頁
   */
  for (let i = 1; i <= pageSize; i++) {
    // 用來存爬到的詳情頁地址
    var arr = []
    const targetUrl = `https://www.xxxxxxx.xx/page/${i}`
    const page = await browser.newPage();
    // 進入頁面
    await page.goto(targetUrl, {
      timeout: 0,
      waitUntil: 'domcontentloaded'
    });
    // 獲取根節點
    const baseNode = 'ul#post_container'
    const movieList = await page.evaluate(sel => {
      const  movieBox = Array.from($(sel).find('li'))
      var ctn = movieBox.map(v => {
        const url = $(v).find('.article h2 a').attr('href');
        return {url: url}
      })
      return ctn
    }, baseNode)
    arr.push(...movieList)
    // 準備爬取詳情頁
    await detailMovie(arr, page)
  }
  browser.close();
  console.log('Visit Over')
  return {msg: '同步完成'}
}
複製代碼

詳情頁

const detailMovie = async (arr, page) => {
  var detailArr = []
  console.log('當頁影片數:' + arr.length)
  for (let i = 0; i < arr.length; i++) {
    await page.goto(arr[i].url, {
      timeout: 0,
      waitUntil: 'domcontentloaded'
    })
    const baseNode = '.article_container.row.box' 
    // jq大法 不用過多說明了吧
    const movieList = await page.evaluate(sel => {
      const movieBox = Array.from($(sel).find('#post_content').find('p'))
      const urlBox = $(sel).find('#藍光高清 td a').attr('href')
      var tmp = [{}]
      var ctn = tmp.map((v,i) => {
        const imgUrl = $(movieBox[0]).find('a').attr('href');
        var info = $(movieBox[1]).text()
        return {
          imgUrl: imgUrl,
          name: info,
          urlBox: urlBox
        }
      })
      return ctn
    }, baseNode)
    console.log(movieList)
    detailArr.push(...movieList)
    console.log('抓取第 ' +  detailArr.length + ' 頁完成')
  }
  console.log('開始向數據庫添加數據')
  await addMovie(detailArr)
  page.close()
  return detailArr
}

複製代碼

過程

感受很爽有沒有!

結果

本身的視頻網站就建好了。

Form

puppeteer 強大之處,他能夠進行模擬操做,好比咱們可讓它本身進行百度

實現

很簡單,一個方法搞定

page.type(selector, text[, options])

  • selector,要輸入內容的元素選擇器。若是有多個匹配的元素,輸入到第一個匹配的元素
  • text,要輸入的內容
  • options
    • delay 每一個字符輸入的延遲,單位是毫秒。默認是0

注意: 每一個字符輸入後都會觸發keydown,keypress/input 和 keyup事件。

page.type('#mytextarea', 'Hello'); // 當即輸入
page.type('#mytextarea', 'World', {delay: 100}); // 輸入變慢,像一個用戶

複製代碼

接下來打開百度試試

const getForm = async () => {
  // puppteer 驗證
  const browser = await puppeteer.launch({headless: false});
  const page = await browser.newPage();
  await page.goto('https://baidu.com');
    await page.type('#kw', 'puppeteer', {delay: 100}); //打開百度後,自動在搜索框裏慢慢輸入puppeteer ,
  page.click('#su') //而後點擊搜索
  await page.waitFor(1000);
  const targetLink = await page.evaluate(() => {

    let url =  document.querySelector('.result a').href

    return url
  });
  console.log(targetLink);
  await page.goto(targetLink);
// await page.waitFor(1000);
  browser.close();
}

複製代碼

上述的例子中,咱們還使用的page.click方法。

page.click(selector, [options])

  • selector: 要點擊的元素的選擇器。 若是有多個匹配的元素, 點擊第一個。
  • options
    • button: left,right或者middle
    • clickCount: 默認是1
    • delay : mousedown 和 mouseup 之間停留的時間,單位是毫秒。默認是0

此方法找到一個匹配 selector 選擇器的元素,若是須要會把此元素滾動到可視,而後經過 page.mouse 點擊它。 若是選擇器沒有匹配任何元素,此方法將會報錯。

要注意若是 click() 觸發了一個跳轉,會有一個獨立的 page.waitForNavigation() Promise對象須要等待。 正確的等待點擊後的跳轉是這樣的:

const [response] = await Promise.all([
  page.waitForNavigation(waitOptions),
  page.click(selector, clickOptions),
]);
複製代碼

簡單滑塊驗證碼破解 + 模擬手機

步驟

  1. 找到滑塊
  2. 計算滑塊位置
  3. 分發事件
  4. 拖動
  5. 鬆手

實現

難點剖析

生成一個模擬器

const devices = require('puppeteer/DeviceDescriptors');
 const iPhone6 = devices['iPhone 6'];
 await page.emulate(iPhone6)

複製代碼

完整版

const getYzm = async () => {
  const devices = require('puppeteer/DeviceDescriptors');
  const iPhone6 = devices['iPhone 6'];
  const conf = {
    headless: false,
    defaultViewport: {
      width: 1300,
      height: 900
    },
    slowMo: 30
  }
  puppeteer.launch(conf).then(async browser => {
    var page = await browser.newPage()
    await page.emulate(iPhone6)
    await page.goto('https://www.dingtalk.com/oasite/register_h5_new.htm')
// 滑動驗證碼會檢測nabigator.webdriver這個屬性。所以咱們須要在滑動前將這個屬性設置爲false
// 接口的webdriver只讀屬性navigator指示用戶代理是否由自動化控制。
    await page.evaluate(async () => {
      Object.defineProperty(navigator, 'webdriver', {get: () => false})
    })
    // 錯誤輸入,觸發驗證碼
    await page.type('#mobileReal', '15724564118')
    await page.click('.am-button')
    await page.type('#mobileReal', '')
    await page.keyboard.press('Backspace')
    await page.click('._2q5FIy80')
    // 等待滑塊出現
    var slide_btn = await page.waitForSelector('#nc_1_n1t', {timeout: 30000})
    // 計算滑塊距離
    const rect = await page.evaluate((slide_btn) => {
// 返回元素的大小及其相對於視口的位置
      const {top, left, bottom, right} = slide_btn.getBoundingClientRect();
      return {top, left, bottom, right}
    }, slide_btn)
    console.log(rect)
    rect.left = rect.left + 10
    rect.top = rect.top + 10
    const mouse = page.mouse
    await mouse.move(rect.left, rect.top)
    // touchevent,而puppeteer只有mouseevent。所以須要經過某個方法,在滑動前先將事件傳遞出去。
    await page.touchscreen.tap(rect.left, rect.top) // h5須要手動分發事件 模擬app的事件分發機制。
    await mouse.down()
    var start_time = new Date().getTime()
    await mouse.move(rect.left + 800, rect.top, {steps: 25})
    await page.touchscreen.tap(rect.left + 800, rect.top,)
    console.log(new Date().getTime() - start_time)
    await mouse.up()
    console.log(await page.evaluate('navigator.webdriver'))
    console.log('end')
    // await page.close()
  })
}
複製代碼
相關文章
相關標籤/搜索