技术博客

越是喧闹,越是孤独。越是寂寞,越是丰富
The more noisy, the more lonely. The more lonely, the more rich

越是喧闹,越是孤独。越是寂寞,越是丰富
The more noisy, the more lonely. The more lonely, the more rich

技术博客


使用Puppeteer获取无水印全景图

2023-12-29 Mendel
工具前端


在之前的文章 编写浏览器插件免费批量下载百度地图全景静态图 中,介绍了如何绕开限制直接通过百度地图Web API免费获取全景图的方式。在文章结尾提到了该方式获取的全景图有尺寸限制,最大1024*512,并且有水印。如果想要获取任意尺寸的无水印全景图,那么就跟随本文开始吧~




01

简单方案



之前获取全景图的原理,是拿官方demo的AK以及官方域名作为Referer来欺骗服务器,从而绕过限制实现下载。本文依然基于此方案,不同的是,之前是去调用Web API,这次是调用JS API。


JS API可以实现在网页上绘制出全景图,官方demo演示地址为:https://lbsyun.baidu.com/jsdemo.htm#webgl-pano3


在全景图区域点右键,可以看到“将图像另存为”操作,可以直接将此Canvas区域(通过WebGL渲染的)保存为png图片,并且图片没有水印。


至此,如果只是为了获取少量的全景图,就可以直接用在线编辑器,设置几个坐标,调整一下canvas区域的尺寸,即可实现保存为自定义大小的无水印全景图。



02


优化方案



如果要实现自动化的获取全景图,那么手动保存的操作就不合适了。起初,我尝试用JS自动保存Canvas图片:

var image = new Image()image.src = $0.toDataURL('image/png')  // $0为<canvas>元素document.body.appendChild(image)

结果发现该图片是全黑。关于此问题的原因可能有多种,有跨域的原因,有绘制上下文的原因,由于对百度绘制全景图的机制了解不足,继续深挖这条路难以走通,于是另辟蹊径。


再说,如果能这么轻易的保存,那么这个功能相当于就白嫖了,何况文档中说这个功能属于高级服务,需要付费的。


此路不通,那就尝试其他方案。既然在页面内通过JS不让保存canvas,那我们将视角从页面内部提升到页面之外,试试用puppeteer开启浏览器并进行截图呢?


如果要使用puppeteer截图,那么就需要将此页面在本地运行,由于接口的限制,使用官方的AK在本机localhost的页面是无法通过校验的。


不过,既然有了上一次的经验,我们这次通过修改本地host,将百度白名单域名lbs.baidu.com转向本机127.0.0.1,然后通过该域名访问,果不其然,成功了。

# 在hosts文件中添加 127.0.0.1  lbs.baidu.com



可见,百度全景图API服务对Referer及host做了简单的校验,完全可以通过自定义Referer或host绕过限制。


接下来,我们对代码进行修改,去除掉不需要的信息展示,隐藏道路指示控件和导航控件,让全景图区域全屏,即可得到一个铺满整个网页的干净的全景图。

var panorama = new BMapGL.Panorama('panorama');panorama.setPosition(new BMapGL.Point(116.423332, 39.946017));panorama.setOptions({    linksControlfalse// 隐藏道路指示控件    navigationControl: false // 隐藏导航控件})


然后我们通过puppeteer来启动浏览器,通过page.setViewport()设置可视区域的宽高,并调用page.screenshot() 截图,即可将指定尺寸的完整全景图保存到本地。

const page = await browser.newPage();await page.setViewport({width: pageWidth, height: pageHeight})await page.goto(`http://lbs.baidu.com:{port}/panorama.html`, {waitUntil'networkidle0'});await page.screenshot({path: imagePath});await page.close()


我们可以将全景图API所需的参数(经纬度、视角heading和pitch等通过URL query的方式传进puppeteer的页面,并在页面内部解析这些参数即可。最后再将保存的图片以http方式返回,这样就实现了自己开发API获取任意尺寸无水印的全景图了。当然由于每次调用都需要启动浏览器来进行,服务端开销较大,响应时间会较长,所以暂不适合大批量的调用。


这个程序在本地测试时,不论是否开启headless模式都没问题,但是部署到Linux服务器上,使用{headless:true} 发现截图为空白。基于此问题,ChatGPT给出的回答是,可能是headless模式对WebGL的支持有限所致,尝试使用非headless模式,并使用虚拟显示器(如xvfb)来模拟GUI环境。


在服务器上安装xvfb并设置:

sudo apt-get install xvfbXvfb :99 -screen 0 1024x768x24 &export DISPLAY=:99

puppeteer使用非headless模式:

browser = await puppeteer.launch({    headless: false,    args: ['--no-sandbox', '--disable-setuid-sandbox']});


经过测试,果然有效


至此,我们一共有两种免费获取百度地图全景图的方式,各有利弊,简单总结如下:


方案
优点
缺点
Web API
速度快,可批量
有水印,最大尺寸有限
JS API
无水印,可出高分辨率图
速度慢,不适合批量



(全文完)









相关文章