技术博客

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

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

技术博客


编写浏览器插件免费批量下载百度地图全景静态图

2023-07-31 Mendel
工具前端


本文介绍一种方式可以免费获取百度地图API中的全景静态图。





01

全景图API简介



百度地图全景静态图的官网文档网址为:

https://lbsyun.baidu.com/faq/api?title=viewstatic


目前此服务不是免费的,是需要申请和付费的。该服务通过HTTP接口提供,传入必要的全景图参数(坐标、图片宽高、水平/垂直视角等)和访问密钥AK即可。


如果你现在需要批量爬取一些全景图做学习研究使用,但预算有限的话,可以尝试一下我研究出来的一种批量下载全景图的方法。



02


绕过接口限制



正常情况下,通过百度地图开放平台中创建的新应用的AK,是没有此API的访问权限的,会提示如下信息:

{"message":"APP 服务被禁用","status":"240"}


于是,我想到了官方API文档中有很多Demo网页,那么Demo中用的AK应该是拥有较高服务权限的,于是打开了JS API的demo页面,如下图:


其中最右栏是一个<iframe>,查看一下它的源代码:


在这里发现了它的ak,这个ak目前是用于Web端的JS API的,那么能不能适用于HTTP接口呢?于是我试了下用这个ak去访问全景图的API,没想到还真有变化,虽然还是返回错误信息,但是返回信息不同了:

{"message":"APP Referer校验失败","status":"220"}


这样,至少能说明,这个ak应该是具备访问全景图权限的,只是调用的源(即Origin/Referer/IP白名单等)不合要求。那么该怎么办?


我做了一个尝试,因为这个全景图API的域名是api.map.baidu.com,所以我本地创建了一个网页index.html,网页中有一个按钮,点击时将创建一个<img>,并将src指向这个api url。此时如果直接访问这个网页并点击按钮,那么肯定是看不到图片的。于是我修改本地的host,在hosts文件中添加了一行:

127.0.0.1 api.map.baidu.com

接下来,用任何一款http server(如nginx/php -S 等)在index.html所在目录以80端口启动,即可通过 http://api.map.baidu.com/index.html 打开此页面。此时,将刚刚添加的那一行host配置再注释掉,然后回来点击网页中的按钮。不可思议的事情发生了,图片竟然展示出来了!


激动了一阵子后,我继续思考,看来这个接口所谓的APP Referer的限制在浏览器端是可以突破的。只要当前网页的域名是 api.map.baidu.com (也可以是baidu相关的其他域名,如lbs.baidu.com等),那么在此网页中发起的api请求(包括<img>的src和<a>的href)是被允许的,无论当前网页显示的域名是否在本地进行了DNS映射配置。



03


实现图片下载



虽然图片展示出来了,但是如果我想把图片下载下来呢?当然可以用简单的手动方式:图片右键另存为。不过如果我想批量下载呢,一个个手动点击肯定就行不通了。其实可以直接通过代码完成下载:

let a = document.createElement('a')a.href = '//api.map.baidu.com/panorama/v2?...'a.download = 'panorama.jpg'a.click()


这里,通过代码创建了<a>标签,并且设置了download属性为下载文件名,然后再触发click事件即可实现下载。但要注意,download属性仅在同域(协议、域名、端口均相同)下生效。如果网页和href请求的域不同,将无法实现下载功能,此时相当于<a>标签的正常点击效果,即在网页中直接展示图片。


关于同域,我们前面已经完全可以做到了:

  • 域名相同:在本地配置host与api一致

  • 端口相同:在本地启动80端口

  • 协议相同:本地虽然使用https不如http方便,但是由于目标服务器是支持http协议的(会重定向到https),所以href属性直接使用 '//' 开头继承当前协议即可



04


做成浏览器插件



功能虽然是实现了,但是使用起来并不方便,需要反复开启和关闭hosts配置。在host开启时还会影响到api.map.baidu.com的正常访问。那么有没有更优雅的方式来实现呢?


我想到了编写一个浏览器插件来实现这个功能。浏览器插件通常就是在特定域名下的网页中运行的脚本。如果我编写一个专门在api.map.baidu.com下的脚本就可以避免host配置的问题了。


编写Chrome浏览器插件的资料网上有很多,这里简单介绍一下,先创建一个目录,添加manifest.json和content.js两个文件。manifest.json为插件配置文件,代码如下:

{    "manifest_version": 3,    "name": "Baidu Map Panorama Downloader",    "version": "1.0",    "permissions": [        "activeTab",        "https://api.map.baidu.com/"    ],    "content_scripts": [        {            "matches": ["*://api.map.baidu.com/*"],            "js": ["content.js"],            "css": ["bootstrap.min.css"]        }    ]}


这样,浏览器将会在api.map.baidu.com这个域名的网页下自动加载这个插件,执行content.js。为了稍微美化一下插件的样式,我添加了bootstrap的css文件,也放在了插件目录。


插件主程序content.js主要做的事情如下:


1)加载css样式表:由于Chrome对插件的权限有控制,可能无法加载外部的资源文件,所以需要用插件本地化加载css文件,通过chrome.runtime来实现:

const link = document.createElement('link')link.rel = 'stylesheet'link.href = chrome.runtime.getURL('bootstrap.min.css')document.head.appendChild(link)


2)绘制插件的DOM界面:用DOM字符串构建一个div添加到body里即可。界面可以根据自己的需求进行设计,例如用一个<textarea>存储多行坐标点数据,再增加一些全景图参数配置的<input>节点,最后放一个“一键下载”的按钮等。


3)按钮绑定事件:参考前文提到的创建<a>并设置href及download属性的代码即可。


插件编写完成后,在Chrome浏览器的扩展程序页中,开启“开发者模式”,并点击“加载已解压的扩展程序”,选择插件所在的目录即可完成插件的添加。


最后,在浏览器输入网址:https://api.map.baidu.com/panorama/v2 后,插件的界面就展示出来啦!




05


下期预告



至此,这个插件可以方便快捷地实现全景静态图的批量下载了,但是由于该api服务返回的图片尺寸最大为1024*512,并且图片有水印,如果你对分辨率和水印有更高的要求,那么这个插件暂时就无法满足。


我将在下一篇文章中介绍,在单张全景图生成的场景中,如何突破1024*512的限制,并且完全去除水印。敬请期待~


(全文完)






相关文章