由于特殊业务需要, 抓取部分百度地图并拼接成一个完整的图片. 可以根据百度地图的图片切片编号下载并使用 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();
});