越是喧闹,越是孤独。越是寂寞,越是丰富
The more noisy, the more lonely. The more lonely, the more rich
越是喧闹,越是孤独。越是寂寞,越是丰富
The more noisy, the more lonely. The more lonely, the more rich
在之前的文章 编写浏览器插件免费批量下载百度地图全景静态图 中,介绍了如何绕开限制直接通过百度地图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({
linksControl: false, // 隐藏道路指示控件
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 xvfb
Xvfb :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 | 无水印,可出高分辨率图 | 速度慢,不适合批量 |
(全文完)