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的页面,均可以实现无限跨域。
  • 由于这是四个方法里面最麻烦(也是维护上来讲跨度较大)的一个,这里咱们不展开讲解谷歌插件的开发具体方式,仅仅提供思路。若是对此方案感兴趣,欢迎给我流言。

固然,以上目前一共四种方式,若是还有其余的,欢迎你们补充!

相关文章
相关标签/搜索