由于特殊业务需要, 抓取部分百度地图并拼接成一个完整的图片. 可以根据百度地图的图片切片编号下载并使用 ImageMagick 拼接起来. 实现如下, 基于 Node.js 环境:
// https://secure.parksandresorts.wdpromedia.com/media/maps/prod/shdr-baidu/10/20/735893/467549.jpg
// https://secure.parksandresorts.wdpromedia.com/media/maps/prod/shdr-baidu/10/20/735962/467608.jpg
'use strict';
const https = require('https');
const fs = require('fs');
const path = require('path');
const childProcess = require('child_process');
const basePath = `${path.resolve('.')}/百度地图抓取`;
const baseUri = 'https://secure.parksandresorts.wdpromedia.com/media/maps/prod/shdr-baidu/10/20';
const startY = 735893;
const endY = 735962;
const startX = 467549;
const endX = 467608;
// 初始化目录
mkdirp(basePath);
mkdirp(`${basePath}/output`);
// 创建文件目录
function mkdirp(dir) {
childProcess.execSync(`mkdir -p ${dir}`);
}
// 构建文件下载地址
function buildFileUrl(y, x) {
return `${baseUri}/${y}/${x}.jpg`;
}
// 构建文件路径
function buildFilePath(y, x) {
y = y + 1 - startY;
x = x + 1 - startX;
mkdirp(`${basePath}/${y}`);
return `${basePath}/${y}/${x}.jpg`;
}
// 下载文件
function downloadFile(y, x) {
let filePath = buildFilePath(y, x);
// 已下载文件不再重复下载
if (fs.existsSync(filePath)) return Promise.resolve();
let fileStream = fs.createWriteStream(filePath);
let fileUrl = buildFileUrl(y, x);
let message = `\n Downloading file: ${fileUrl}`;
return new Promise(function(resolve) {
https.get(fileUrl, function(res) {
res.pipe(fileStream).on('finish', function() {
fileStream.close();
// console.log(`${message} ..... Done!`);
resolve();
}).on('error', function(e) {
console.log(`${message} ..... Error => ${e.message}`);
resolve();
});
});
});
}
// 执行堆栈, 支持批量抓取
function execStack(stack, batchNumber) {
batchNumber = batchNumber || 1;
let promises = [];
if (stack.length) {
for (let i = 0; i < batchNumber; i++) {
if (stack.length) {
promises.push(downloadFile.apply(null, stack.shift()));
}
}
return Promise.all(promises).then(function() {
return execStack(stack, batchNumber);
});
} else {
return Promise.resolve();
}
}
// 读取文件夹, 过滤 output 和 .DS_Store, 并排序
function readdir(dir, isDir) {
if (fs.lstatSync(dir).isDirectory()) {
return (fs.readdirSync(dir) || []).filter(function(subDir) {
if (subDir === 'output' || subDir === '.DS_Store' || subDir === 'result.jpg') return false;
if (isDir) return fs.lstatSync(`${dir}/${subDir}`).isDirectory();
return true;
}).sort(function(a, b) {
a = parseInt(a);
b = parseInt(b);
if (a === b) return 0;
if (a > b) return -1;
if (a < b) return 1;
}).reverse();
} else {
return [];
}
}
// 合并图片, 输出到 output 中
function combineImages(imagePaths, name, direction) {
imagePaths = imagePaths || [];
if (!imagePaths.length) return;
let images = imagePaths.join(' ');
let outputPath = `${basePath}/output/${name}`;
childProcess.execSync(`convert ${images} ${direction} ${outputPath}`);
return outputPath;
}
// 纵向合并图片
function combineImagesVertically(imagePaths, name) {
return combineImages(imagePaths, name, '-append');
}
// 横向合并图片
function combineImagesHorizontally(imagePaths, name) {
return combineImages(imagePaths, name, '+append');
}
// 合并所有图片
function combineAll() {
let dirs = readdir(basePath);
dirs = dirs.map(function(dir) {
let dirPath = `${basePath}/${dir}`;
let files = readdir(dirPath);
files = files.map(function(file) {
return `${dirPath}/${file}`;
});
return combineImagesVertically(files, `${dir}.jpg`);
});
combineImagesHorizontally(dirs, 'result.jpg');
}
// 抓取
function scratch() {
let i,j;
let stack = [];
for (i = startY; i <= endY; i++ ) {
for(j = startX; j <= endX; j++) {
stack.push([i, j]);
}
}
return execStack(stack, 80);
}
// 抓取并合并图片
scratch().then(function() {
combineAll();
});