神器在手,你也能做4399!_知乎_(站长工具4399)

来源:人气:0更新:2022-05-04 16:08:49

海洋cms论坛

无头浏览器(Headless Chrome)是一种没有操作界面的浏览器,常用于网页自动化测试,利用其提供的API,可以自动执行操作指令,它是测试利器,也是爬虫神器,Chrome则是自带该特性的王者

背景

这几天发现了几个好玩的Html5游戏,界面效果十分炫酷,简直是解压神器。但由于游戏网站是国外的,访问速度不稳定,尤其是用到Google提供的JS、字体文件时,有时连图片都无法正常显示,这逼我放大招啊!立马准备撸个爬虫将游戏代码下载到自己服务器来玩,岂不是爽歪歪。

方案

动手之前,先做个全面的分析,爬虫方案有很多,但这次有所不同,工具不对可能会干无用

分析

h5游戏和普通静态网站有所不同。通常静态网站是一次将资源文件全部加载到页面,通过标准的<script> 、<link>、 <img> 等html标签加载,简单的页面甚至可以直接Ctrl+S保存下来。

而游戏站点要复杂的多,通常会基于成熟的游戏引擎开发,需要加载大量的js、图片、音频等资源。游戏开始之前会有一个资源加载页面,等待期间使用Javascript程序异步加载游戏所需要的资源文件。

这个异步加载是势必要破的障碍。无论是Ctrl+S还是Curl、Wget,可以统统退下了,专业的事情得专业的工具,咱可以打开浏览器devtools来分析站点结构,但总不能手动一个一个的保存请求相应吧,工具能干的事,咱绝对不动手。

工具选择

想和异步请求打交道,最好的方式便是模拟浏览器访问,这方面早有先驱者为咱趟过坑了,核心是使用无头浏览器模拟真实用户操作,不过这次比较特殊,需要抓取所有前端资源文件并保存下来,而不是单纯的抓取页面标题、图片这类工作。我找了三个轻量级的方案进行分析选择

splash

特点:轻量级、快速部署、HTTP API,但人气不高 分析:容器部署十分简单,但我比较纠结如何高效的将所有资源文件保存下来,如果编写脚本传递给HTTP API执行返回资源,效率应该不高,所以暂时放弃该方案

phantomjs

PhantomJS - Scriptable Headless Browser

特点:老牌靠谱、API丰富,JS友好,关键还是我有经验啊~ 分析:文件太大,国内下载超慢,长时间运行有内存泄露问题,官方已弃,除此外都是优点。好在我本机环境早已备好,所以最初选择了该方案,然鹅万万没想到,在游戏开始渲染时,站点居然提示浏览器不支持WebGL。一口老血!查阅资料,早前官方明确说了不会支持该特性。好吧好吧,可惜了。

headless chrome

特点:Google出品,这一条就够了。之所以没有作为第一选择,还是因为我之前一直使用的是phantomjs,所以还未体会到Chrome的好啊。经此一战,妥妥的路转粉了 分析:出身高贵,毋庸置疑,又可以解决游戏渲染问题,就是它了

环境

我的理解,headless其实是Chrome浏览器的一种特性,可以在无图形界面的情况下解析网站资源,要控制浏览器的行为,需要对接其API,我选择了Node.js版的Puppeteer, 先做以下准备

1. Node.js环境

2. Chrome浏览器,非必需

3. 本文操作均在Mac系统下,其它系统类似

快速开始

Puppeteer的意思是“傀儡” ,很形象,它是一个Node库,提供了一个高级 API来通过 DevTools 协议控制 Chromium 或 Chrome

Puppeteer

先快速浏览一下Puppeteer的文档

文档:

官方提供两个包,一个是puppeteer, 一个是puppeteer-core,最大的区别在于core包不包含浏览器,其它差异详见文档说明。我本机已经安装有Google Chrome浏览器了,所以使用puppeteer-core即可

创建工程

在命令行,依次执行以下命令新建工程

$ mkdir headless-test $ cd headless-test $ npm init

一路enter,OK之后安装依赖

$ npm i puppeteer-core mkdirp测试

新建test.js文件,代码如下:

const puppeteer = require(puppeteer-core); (async () => { const browser = await puppeteer.launch({ executablePath: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome, //本机chrome路径 devtools: true, //调试打开浏览器 }); const page = await browser.newPage(); const url=; await page.goto(url , {waitUntil: networkidle2}); await page.waitFor(10000); await browser.close(); })();

执行node test.js ,可以看到chrome自动打开访问站点,并提示“受到自动测试软件控制“

游戏抓取

Html5游戏站点基本都是一个套路,在主页面中嵌入iframe游戏,需要抓取的就是iframe中加载的所有资源。我以GamePix游戏站为例,找一个游戏进行分析。

分析网页结构

这个游戏还不错,分析一下游戏的真实地址,游戏地址:

Play Top Shootout️ | Online & Unblocked | GamePix

1. chrome中访问该地址,除了游戏外,还有很多其它元素,所以很明显游戏真实地址在iframe中

2. 打开开发者工具进行调试,点击开始游戏按钮,看一下游戏加载的整个过程

3. 过程中可以看到加载进度,直至100%。现在说明游戏资源基本加载完成,可以审查游戏元素,查看iframe信息

(第一层iframe)

4. 此站点做了防护,在主页面内嵌入第一层iframe,该iframe内再嵌入第二层游戏iframe。游戏真实地址为game.builds子域下。

(第二层iframe,真实游戏地址)

5. 复制出第二层iframe的地址(游戏真实地址),在浏览器中直接访问,发现页面跳转到其它地址了,说明站点做了安全判断,不能直接访问游戏真实地址。

6. 复制出第一层iframe的地址,发现游戏可以正常访问。那就不纠结了!就从该地址入手,虽然会多出一些无用代码,但很容易剥离。

抓取实现

在编写代码之前,先分析下站点资源加载流程

思路

1. 打开url地址(第一层iframe地址),等待资源加载完成

2. 点击页面的“PLAY NOW ”

3. 等待游戏iframe资源加载完成

4. 点击游戏界面的“TAP TO CONTINE”开始游戏

5. 至此游戏资源基本加载完成

代码

具体代码实现,我已经完成,并稍作了一些优化,各位朋友可根据自身情况,进行修改。新建robot.js文件,代码如下

const puppeteer = require(puppeteer-core); const fs = require(fs); const mkdirp = require(mkdirp); const DEBUG =false; const gameName=gamepix-top-shoot; const url =?sid=20049&lang=en&rgx=1&gpxref=www.goboplay.com&utm_source=www.goboplay.com&utm_medium=publisherV3; const blackList=[ facebook ,google-analytics ,googletagmanager ,googleapis ,googlesyndication ]; const gameStart =async function (page) { //点击“PLAY NOW” await page.click(.gpxLoader-button,{delay: 300}); console.log(加载游戏iframe...); const gameFrame = page.frames().find(frame => frame.$(#game-frame)); try{ await Promise.all([ gameFrame.waitForNavigation({timeout:, waitUntil:networkidle2}), ]) }catch (e) { } const btnPosition ={ x:100, y:200 }; //点击“TAP TO CONTINUE” await page.mouse.click(btnPosition.x, btnPosition.y, { delay: 300 }); console.log(点击开始游戏按钮); }; (async () => { const browser = await puppeteer.launch({ executablePath: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome, devtools: DEBUG, //调试打开浏览器 }); const outDir=./data; const gameDir =outDir + / + gameName; try{ const page = await browser.newPage(); page.setDefaultTimeout(0); //超时设置 //处理弹窗,防止弹出导致网页加载中断 page.on(dialog, async dialog => { console.log(dialog.message()); await dialog.dismiss(); }); //拦截请求,黑名单中的地址,不发出请求 page.on(request ,function (request) { const url =request.url(); if(blackList && blackList.length>0){ for(let i in blackList){ if(url.indexOf(blackList[i])>-1){ request.abort(); return; } } } request.continue(); }) //将响应内容保存到本地 page.on(response, function (response) { const url =response.url(); const status = response.status() DEBUG && console.log(Get: ,status ,url); //状态码判断 if (status !==200 ) { console.log(Get Warning:,status,url); return; } if(url.split("://").length<2){ console.log(Save Error: ,url); return; } const filename = url.split("://")[1].split("?")[0]; const dir=filename.substr(0, filename.lastIndexOf(/)); response.text().then((body)=>{ mkdirp(gameDir+/+dir).then(res => fs.writeFile(gameDir + / +filename ,body ,function (e) { }) ) }) }); //设置浏览器界面尺寸 await page.setViewport({ width: 375, height: 667, deviceScaleFactor: 1, }); await page.setRequestInterception(true) //允许拦截请求 console.log(页面开始加载); await page.goto(url , {waitUntil: networkidle2});//超时问题 await page.waitFor(5000); console.log(页面初步加载完毕) await gameStart(page); console.log(游戏已开始); await page.waitFor(5000); await page.screenshot({path: gameDir + /screenshot.png}); //保存游戏截屏 }catch (e) { console.log(e) }finally { await browser.close() } })();

执行node robot.js,耐心等待程序执行,可以看到游戏下载过程,并成功保存文件到本地data目录中。

前面分析过了,游戏代码应该是在game.builds域名下,因此该目录下的文件是重点文件,其它目录的文件是否有被引用,可在游戏代码中全局搜索排除。

整体代码实现还是比较简单的,核心是等待资源加载完成,我已经标注了一些注释以供参考。实际上Puppeteer的可玩性十分强,甚至可以帮你自动玩游戏,还有更多有趣的玩法可以发掘

注意事项

代码实现过程,有几个注意事项,如果忽略可能会导致超时异常

1.有些游戏开始会有alert、confirm弹窗,需要监听处理,否则可能导致加载中断

2. 有些无用的资源可以拦截,尤其是google等无法正常访问的资源或者广告

3. 若游戏iframe为异步加载,则需要单独监听iframe是否加载完成

4. 游戏界面多为canvas,无法使用选择器,可使用坐标方式。

5. 调试过程可能产生pending请求,导致超时,但在headless下可能是正常

6. 设置合理的超时时间或异常处理,避免超时导致的异常

游戏代码处理

通过上面的步骤,可以将游戏资源全部下载到本地,但想要直接运行起来,可能还不行,有关键一步要处理,需要对游戏代码稍作处理。

不同站点游戏代码结构不同,但只要搞定一个游戏,基本整站都可以破解,由于都是前端代码实现,可以看到所有源码,只要有耐心就可以处理好,甚至可以写成自动处理脚本。尤其是国内一些站点,只需要小小的改动就可运行起来。

出于某些原因,这里我不再详细讲解,给大家提供一下几个思路

1. 确定游戏入口文件,通常是html文件

2. 修正html中加载的css、js文件路径

3. 修正js文件中加载资源的路径

4. 破解js中的一些安全限制,通常是通过一些传递参数判断

5. 去除广告、统计等无用代码

6. 找一些国内站点分析,结构更简单,套路都差不多

文章更新不及时,更多文章和交流见个人简介。


苹果cms在线采集接口