浏览器支持跨域。说实话在puppeteer里面意义不大,本身就是在node环境下。但是在我们的测试项目和一些特殊场景下的操作中,可能确实需要这样的能力,所以本文作为puppeteer支持跨域的记录。什么是跨域?这里先解释一下跨域。有基础的同学可以直接看下面的跨域方法,这里略过。跨域判断:不同的协议(http/https),不同的主域名(taobao/baidu),不同的子域名(www/blog),不同的端口号(3000/3001),以上四种,只要一个两个网址不匹配,即跨域。跨域带来的问题就是发送ajax等所有交换数据的方法都会失效。简单来说,你不能调整其他的API。用2020年最形象的比喻,两个网站之间:道路封闭。如何在puppeteer中解开它?下面我们就来说说吧。第一种方式:官方提供的API首先,我们先看看官方文档,看看是怎么说的。page.setBypassCSP(enabled)enabled[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type))设置绕过页面的Content-Security-Policy.returns:[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise))切换绕过页面的内容安全策略。注意CSP旁路发生在CSP初始化而不是评估时。通常这意味着应该在导航到域之前调用page.setBypassCSP。api非常简单,只需输入一个布尔值即可。说了这么多,最重要的其实是一点:应该在导航开始前调用。只需确保代码在此之前执行,您应该不会有太大问题。另一个api是:page.setRequestInterceptionhttps://github.com/puppeteer/puppeteer/blob/master/docs/api.md#pagesetrequestinterceptionvalue官方给出了这个例子,很直白:page.setRequestInterception(true);page.on('request',interceptedRequest=>{if(interceptedRequest.url().endsWith('.png')||interceptedRequest.url().endsWith('.jpg'))interceptedRequest.abort();elseinterceptedRequest.continue();});等待page.goto('https://example.com');这里唯一需要注意的隐藏操作是:如果开启拦截,会自动开启无缓存状态。第二种方法:node直接捕获并导入这个方法。这个方法应该是大家最容易想到的。node.js的爬虫npm包也有很多。随便找一个只要能爬取就可以拿下来,二次处理后再返回当前操作页面。下面是一个简单演示的demo代码:letpage=awaitbrowser.newPage();awaitpage.goto(`https://www.baidu.com`);//第一种是直接选择元素,当元素在页面中执行某个函数并返回一个结果letresult=awaitpage.$eval('#kw',(el)=>{//...dosomethingreturnnewPromise((resolve,reject)=>{//等待一个Promise完成,点击时继续下一步el.addEventListener('click',()=>{resolve('I'我完成了!')})})});console.log(result)//如果页面返回的结果是这样的,就已经实现了爬取,然后再次进入页面做一些其他的操作。类似的功能还有很多,可以参考官方文档。constresult=awaitpage.evaluate(x=>{returnPromise.resolve(8*x);},7);//后一个参数为节点环境传入的值console.log(result);//prints"56"官方也提供了一个方法:page.exposeFunction(name,puppeteerFunction)https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#pageexposefunctionname-puppeteerfunction其实可以让你挂当一个节点方法在window下,然后在页面执行环境中,就可以调用这个节点方法。推荐这个官方方法。官方案例:constpuppeteer=require('puppeteer');constfs=require('fs');(async()=>{constbrowser=awaitpuppeteer.launch();constpage=awaitbrowser.newPage();page.on('console',msg=>console.log(msg.text()));awaitpage.exposeFunction('readfile',asyncfilePath=>{returnnewPromise((resolve,reject)=>{fs.readFile(filePath,'utf8',(err,text)=>{if(err)reject(err);elseresolve(text);});});});awaitpage.evaluate(async()=>{//使用window.readfile读取文件内容constcontent=awaitwindow.readfile('/etc/hosts');console.log(content);});awaitbrowser.close();})();但是在登录的时候,这个方案就很麻烦了。如果要抓cookie,最好模拟第??一种方法用两个域名登录网站启用跨域,直接互相调用接口。第三种方法:chrome参数跨域这种方法是我个人认为最推荐的一种。直接启动的时候,让浏览器支持跨域(随便你),没有任何限制。代码一贴出来大家就明白了。不明白的可以参考我之前的文章Puppeteer系列步进日志-2-去掉自动提示。开头提到的chrome参数有很详细的解释。这里我们使用:--disable-web-securitywaitpuppeteer.launch({args:['--disable-web-security']})和往常一样,让我们??看看Peter在列表中介绍了什么:--disable-web-security?不要强制执行同源策略。(供测试网站的人使用。)?关闭网页内容安全策略不要强制执行同源策略。(针对测试网站的用户。)该参数的作用是控制内容安全策略。添加后页面可以在不同域名下无限跨域。第四种方式:使用google插件跨域插件跨域也是一种方式,虽然限制比较多。但不排除在某些特定的项目需求下可能会派上用场(反正我们穷,遇到事情可以来这里找解决办法,先记录一下)。首先我们知道,在插件中,如果是跨域,需要在background.js中完成,然后通过消息通信的方式转给content.js。另一种方式是直接生成一个background.html,或者自定义html页面,通过google插件加载后可以完全跨域。只要在chrome-extension://XXXXXXX/A.html的页面,就可以实现无限跨域。因为这是四种方法中最麻烦的(在维护方面也有较大的跨度),所以这里不对具体开发谷歌插件的方法进行说明,仅提供思路。如果您对这个程序感兴趣,请给我传谣。当然,目前有以上四种方法。如果还有其他的,欢迎补充!
