Puppeteer 系列踩坑日誌—4—跨域的幾種方式

瀏覽器支持跨域,說實話在puppeteer裏面其實意義不太大,自己就在node環境中。可是在咱們測試項目和一些特殊場景下的操做中,的確可能就須要這樣的能力,所以此文做爲讓puppeteer支持跨域的一種記錄。

什麼是跨域?

這裏先解釋下跨域,有基礎的同窗能夠直接看下面的幾種跨域方式,跳過這裏。html

  • 跨域的判斷:node

    • 協議不一樣(http/https)
    • 主域名不一樣(taobao/baidu)
    • 子域名不一樣(www/blog)
    • 端口號不一樣(3000/3001)
  • 以上四個,兩個url只要有一個條件對不上,那就是屬於跨域了。跨域形成的問題就是,發送ajax等交換數據的方式會所有失效。簡單來講,就是你調其餘家api調不出來。
  • 用一個2020年最形象的比喻,兩個網站之間:封路了。puppeteer中怎麼解開呢?下面來講。
第一種方式:官方提供的API
  • 首先咱們查閱一下官方文檔,看下里面是怎麼說的。

page.setBypassCSP(enabled)

Toggles bypassing page's Content-Security-Policy.git

NOTE CSP bypassing happens at the moment of CSP initialization rather then evaluation. Usually this means that page.setBypassCSP should be called before navigating to the domain.github

api很是簡單,就是放入一個布爾值。說了那麼多,其實最主要的是一個點: 導航開始前應該調用完畢它。web

只要確保代碼在此以前執行,就不會有太大問題。ajax

另一個api是: page.setRequestInterception https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#pagesetrequestinterceptionvaluechrome

這個官方給出了例子,也說得很直白:npm

page.setRequestInterception(true);
page.on('request', interceptedRequest => {
if (interceptedRequest.url().endsWith('.png') || interceptedRequest.url().endsWith('.jpg'))
interceptedRequest.abort();
else
interceptedRequest.continue();
});
await page.goto('https://example.com');api

這裏惟一要注意的一個隱藏操做是:啓用了攔截,那麼就會自動開啓無緩存狀態。跨域

第二種方式:node直接抓取導入
  • 這種方式應該是你們最容易想到的,node的爬蟲類npm包也有很多。隨便找一個只要能爬都能拿下來,而後再進行二次處理後返回到當前操做的頁面上。
  • 咱們這裏來一個demo代碼,簡單演示下:

let page = await browser.newPage();
await page.goto(`https://www.baidu.com`);
//第一種是直接選擇元素,當元素在頁面裏執行了某個功能,返回回來一個結果
let result = await page.$eval('#kw', (el)=>{
//...作一些事情
return new Promise((resolve,reject)=>{//等待一個Promise完成,當點擊了才能繼續下一步
el.addEventListener('click',()=>{
resolve('我執行完了!')
})
})
});
console.log(result)//從頁面裏面發回的結果

這樣的話,就實現了抓取好了,再次進入頁面裏面作一些其餘操做。類似的功能還有很多,能夠參考官方文檔裏。

const result = await page.evaluate(x => {
return Promise.resolve(8 * x);
}, 7);//後面的這個參數是要node環境傳入的值
console.log(result); // prints "56"

官方還提供了一個方法:page.exposeFunction(name, puppeteerFunction) https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#pageexposefunctionname-puppeteerfunction

其實就是能夠容許你掛在一個node方法在window下,而後在頁面執行環境中,能夠去調用到這個node方法,比較推薦的仍是這種官方的作法。

官方案例:

const puppeteer = require('puppeteer');
const fs = require('fs');

(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on('console', msg => console.log(msg.text()));
await page.exposeFunction('readfile', async filePath => {
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (err, text) => {
if (err)
reject(err);
else
resolve(text);
});
});
});
await page.evaluate(async () => {
// use window.readfile to read contents of a file
const content = await window.readfile('/etc/hosts');
console.log(content);
});
await browser.close();
})();

  • 可是碰到登陸類的,這個方案就很麻煩了,要去抓cookie,那還不如第一種方法模擬登錄好兩個域名的網站開啓跨域,直接互相調用接口。
第三種方式:chrome參數跨域
  • 這種方式是我以爲最推薦的一種,直接啓動的時候讓瀏覽器支持跨域(想幹啥幹啥),沒有任何限制。
  • 代碼一貼你們就懂了,不懂可參考我以前的文章 Puppeteer 系列踩坑日誌—2—去掉自動化提示 開篇講到的chrome參數,裏面有很是詳盡的解釋。這裏咱們使用:--disable-web-security

await puppeteer.launch({
args: [
'--disable-web-security'
]
})

老樣子,仍是看下peter的列表裏咋介紹的:

--disable-web-security

Don't enforce the same-origin policy. (Used by people testing their sites.)

關閉網頁內容安全策略

不要強制執行同一來源策略。(供測試網站的用戶使用。)

這個參數的做用就是拿來控制內容安全策略的,加完之後,頁面不一樣域名下就能夠無限跨域了。

第四種方式:利用谷歌插件跨域
  • 插件跨域也是一種辦法,雖然限制比較多。可是不排除在某些特定的項目需求下,可能會派上用場(反正我們窮舉辦法,遇到事情就能夠來這裏找一個方案嘛,先記錄下來)
  • 首先咱們知道,插件裏面,跨域的話,須要在 background.js 裏去完成,而後在經過消息通訊的方式傳輸到content.js裏面來。
  • 另一種作法則是,直接生成一個background.html,或者是一個自定義的html頁面,被谷歌插件加載後,這個頁面便可實現徹底跨域。只要在chrome-extension://XXXXXXX/A.html的頁面,均可以實現無限跨域。
  • 由於這是四個方法裏面最麻煩(也是維護上來講跨度較大)的一個,這裏咱們不展開講解谷歌插件的開發具體方式,僅僅提供思路。若是對此方案感興趣,歡迎給我流言。

固然,以上目前一共四種方式,若是還有其餘的,歡迎你們補充!

相關文章
相關標籤/搜索