在puppeteer和request之間互相傳輸cookies

參考

前言

目的是實現用puppeteer實現登陸流程自動化,把登陸後獲取到的合法cookies傳輸給node端,實際大量抓取數據則使用request,提升抓取性能和可靠性。javascript

puppeteer -> request

  • 首先,給request默認參數中添加jar屬性:java

    const cookieJar = request.jar()
    const rp = request.defaults({
      jar: cookieJar,
      ... // 其它request默認參數
    })

    這樣,使用這個rp對象就會使用這個cookieJar對象來儲存從響應中解析出的cookies,也能夠經過操做cookieJar對象來增刪改查單個cookienode

  • 經過puppeteer從瀏覽器獲取cookiesgit

    const cookies = await page.cookies()

    這裏的cookies是個普通javascript對象的數組,每一個元素中包含name, value, domain, path, expires, httpOnly, secure等屬性github

  • 將瀏覽器獲取的cookies填充到cookieJar中
    這個步驟值得重點說道說道:npm

    • cookieJar中的每一個cookie是個Cookie對象,而不是簡單js對象,cookieJar.setCookie不接受簡單js對象,要麼建立一個Cookie對象,要麼傳入字符串讓這個方法自行解析,我這裏選擇第一個方案,所以必須引入依賴
    • request的cookie處理依賴於tough-cookie這個庫,所以須要顯式的安裝並引入這個庫,注意不要使用最新版,不然和request中的依賴版本不一致;須要從package-lock.json中找到tough-cookie的版本號,而後用npm install tough-cookie@x.x.x安裝特定版本
    • 另外,還有兩個地方須要注意:tough-cookie的Cookie對象使用key而不是name做爲cookie名;Cookie對象的expires屬性是個Date對象或者'Infinity'字符串,而puppeteer側的對象中expires是個unix時間戳,用-1表示永久;所以須要進行以下轉換:
    cookies.forEach(json => {
      const { name, domain } = json
      json.key = name
      json.expires = json.expires > 0 ? new Date(json.expires * 1000) : 'Infinity'
      const cookie = Cookie.fromJSON(json)
      cookieJar.setCookie(cookie, 'https://' + domain)
    })
  • 完成cookies的設置後nodejs側就至關於處於登陸後狀態了,能夠正常請求登陸後的權限內容

request -> puppeteer

  • 有時候仍是有從nodejs側逆向傳輸cookies到瀏覽器側的需求,好比借用瀏覽器完成一些有複雜操做的流程,這就涉及到上面一節的逆操做。
  • 這裏的麻煩是request包裝後的cookie操做方法並不全面,好比我發現沒法用cookieJar.getCookies()這個方法獲取到'.xxxx.com'這樣的domain下的cookies,所以只好本身經過分析源碼,寫了下面這個方法來一次性獲取cookieJar中的全部cookie對象json

    async function allCookies (jar) {
      const store = jar._jar.store
      return (await Promise.all(Object.keys(store.idx).map(d => util.promisify(store.findCookies).call(store, d, null)))).flat()
    }

    注意:api

    1. 以上'_jar'這樣的屬性屬於tough-cookie的內部私有屬性,這意味這上述代碼其實是一種hack方式,不必定適用於其它版本
    2. util.promisify是nodejs標準庫中的方法,用於把回調方式的函數轉成Promise方式
    3. Array.flat方法是node 11纔有的,若是用老版本,請自行寫方法展開數組
  • 而後就能夠簡單的把Cookie對象轉換爲puppeteer接受的普通js對象了數組

    const cookies = await allCookies(cookieJar)
    cookies = cookies.map(c => ({ ...c, expires: c.expires instanceof Date ? c.expires.getTime() / 1000 : -1, name: c.key }))
    await page.setCookie.apply(page, cookies)

    由於Page.setCookie接受的是變長參數而不是數組,所以用apply來調用瀏覽器

相關文章
相關標籤/搜索