連番の付いた複数の画像ファイルをダウンロードするJScript(WSH)スクリプト ― 同期処理

URL、フォルダ名を書き換えれば好きに使える。
連番がゼロ埋めの固定桁の場合は createUrl(i) をカスタマイズする。

連番を1から始め、HTTPステータス404 Not Foundが返ってくるまでファイルをダウンロードする。
なお、本スクリプトを実行すると、Internet Explorerに履歴が残る

非同期でダウンロードするスクリプトも作った。400ファイルが15秒ほど速くダウンロード出来る。
連番の付いた複数の画像ファイルを非同期でダウンロードするJScript(WSH)スクリプト - happynow’s diary


SyncDownload.js

var folderName = 'DownloadFolder';
var baseUrl = 'http://aaa/xxx/123.jpg';

// URLのファイル名の数字部を # に変換。例)http://aaa/xxx/123.jpg を http://aaa/xxx/#.jpg に変換
baseUrl = baseUrl.replace(/\d+\.(jpg|png)$/i, '#.$1');

//連番からURLを作成する関数 (カスタマイズ用)
function createUrl(i) {

  return baseUrl.replace('#', i);

/*
  簡単な処理の例
    return 'http://xxxx/xxxx/' + i + '.jpg'
    return baseUrl + i + '.jpg'

  ゼロ埋めする例 (URLが http://xxxx/xxxx/b0041.jpg のような場合)

    var seq = ('0000' + i).slice(-4);  //例: 23 を '0023' に編集
    return 'http://xxxx/xxxx/b' + seq + '.jpg'
*/
}


//連番の最大値。ただし HTTP Status 404 Not Found が返ってきた場合、この値を更新し後続のHTTP要求を停止させている
var maxIndex=999;

var fso = new ActiveXObject('Scripting.FileSystemObject');

var logFilePath = WScript.ScriptFullName.replace(/\.[^.]+$/, '.log');

try {
  if(fso.FileExists(logFilePath)) {
    //既存のログファイルを削除する
    fso.DeleteFile(logFilePath, true);
  }
} catch(e) {}

var startTime = new Date();
putLog('START');

var fileCount = 0;

//ダウロード用のフォルダ作成(カレントフォルダ配下)
var folderPath = createDownloadFolder(folderName);

//HTTP要求オブジェクトの作成
var xhr = new ActiveXObject('Msxml2.ServerXMLHTTP');

//URLからファイル名を抜き出すための正規表現オブジェクト
var reFileName = /[^\/]+$/;

var i;
for(i = 1; i <= maxIndex; i++) {

  var url = createUrl(i);
  var fileName = url.match(reFileName)[0];
  var filePath = fso.BuildPath(folderPath, fileName);

  //putLog(i + ' url=' + url);
  //putLog(i + ' fileName=' + fileName);

  downloadFile(url, filePath, i);
}

emptyLine();
putLog('File Count=' + fileCount);

var endTime = new Date();
var elapsedTime = Math.round(((endTime - startTime) / 1000)*10)/10;
putLog('END (Elapsed Time:' + elapsedTime + 'sec)');

WScript.Echo('File Count=' + fileCount);


function downloadFile(url, filePath, index) {

  try {
    emptyLine();
    var log_i = function (s) { putLog('[' + index + '] ' + s); };

    var t = 90 * 1000;
    xhr.setTimeouts(t,t,t,t);

    xhr.open('GET', url, false);
    xhr.setRequestHeader('Content-type', 'image/jpeg');

    log_i('send ' + url);
    xhr.send(null); // 送信

    if (xhr.readyState == 4) {
      if (xhr.status == 200) {
        var adTypeBinary = 1, adSaveCreateOverWrite = 2;

        // バイナリファイルを保存するためのオブジェクト
        var strm = new ActiveXObject('Adodb.Stream');

        strm.Type = adTypeBinary;
        strm.Open();
        strm.Write(xhr.responseBody); // 書き込み
        strm.Savetofile(filePath, adSaveCreateOverWrite); // 保存
        strm.Close();

      } else {
        log_i('retured HTTP status : ' + xhr.status);
        if (xhr.status == 404) {
          if (index < maxIndex) {
            maxIndex = index;
          }
        }
      }
    }
  } catch(e){
    var WININET_E_TIMEOUT             = 0x80072EE2;  //The operation timed out.
    var WININET_E_INVALID_URL         = 0x80072EE5;  //The URL is invalid.
    var WININET_E_UNRECOGNIZED_SCHEME = 0x80072EE6;  //The URL does not use a recognized protocol.
    var WININET_E_NAME_NOT_RESOLVED   = 0x80072EE7;  //The server name or address could not be resolved.
    var WININET_E_PROTOCOL_NOT_FOUND  = 0x80072EE8;  //A protocol with the required capabilities was not found.

    function createErrorMessage(e) {
      var description = e.description.replace(/\s+$/g, '');

      switch(e.number >>> 0) {
        case WININET_E_UNRECOGNIZED_SCHEME:
          description = 'The URL does not use a recognized protocol';
      }
      return 'Error: ' + description + ' (0x' + toHex(e.number) + ')';
    }

    var errorCode = e.number & 0xFFFF;

    switch(e.number >>> 0) {
      case WININET_E_INVALID_URL:
      case WININET_E_UNRECOGNIZED_SCHEME:
      case WININET_E_NAME_NOT_RESOLVED:
      case WININET_E_PROTOCOL_NOT_FOUND:

        WScript.Echo(url + '\n\n' + createErrorMessage(e) );
        WScript.Quit(errorCode);
        break;
      case WININET_E_TIMEOUT:
        log_i('timeout');
        break;
    }

    log_i(createErrorMessage(e));

  } finally {
    if(fso.FileExists(filePath)) {
      log_i('done ' + url);
      fileCount++;
    } else {
      log_i('failed ' + url);
    }
    xhr.abort();
  }
}

function createDownloadFolder(folderName) {

  var basePath = fso.BuildPath(fso.GetParentFolderName(WScript.ScriptFullName), folderName);
  var folderPath = basePath;

  var i = 1;
  while(fso.FolderExists(folderPath) || fso.FileExists(folderPath)) {
    i++;
    folderPath = basePath + ' (' + i + ')';
  }

  fso.CreateFolder(folderPath);

  return folderPath;
}

function toHex(n) {
  return (n >>> 0).toString(16).toUpperCase()
}

function putLog(s) {
  if (s != null){
    s = dateToString(new Date()) + '  ' + s;
  }
  try {
    var ForAppending = 8, Unicode = -1;
    var ts = fso.OpenTextFile(logFilePath, ForAppending, true, Unicode);
    if(s == null) {
      ts.WriteLine();
    } else {
      ts.WriteLine(s);
    }
    ts.Close();
  } catch(e) {}
}

function emptyLine() {
  putLog(void 0);
}

function dateToString(dt) {
  var s = '';
  s += dt.getFullYear();
  s += '/' + ('00' + (dt.getMonth() + 1)).slice(-2);
  s += '/' + ('00' + dt.getDate()).slice(-2);
  s += ' ' + ('00' + dt.getHours()).slice(-2);
  s += ':' + ('00' + dt.getMinutes()).slice(-2);
  s += ':' + ('00' + dt.getSeconds()).slice(-2);
  s += '.' + ('000' + dt.getMilliseconds()).slice(-3);
  return s;
}