Dirty Mashup用の小汚い秘技を貴女に


【正しいのは最終出力だけ度:あたたたた!】

いや、秘技だけに。

後ろ指さされる系TIPSはまだまだ続くぜ!

ダダ漏れ系になっております。

そう言えば、私が、ほとんど「Hello, World」並にあらゆる言語で書いてきた、お手軽Dirty Mashup用の処理があります。(電車賃管理用に、Excel VBAですら書きました)

その名も、getBetweenAll。どんな環境でも手軽で高速にマッシュアップ用のコードが書きたいと思っても、必ずしもドキュメントツリーの解析ができるライブラリが手元にあるとは限りません。かといって、正規表現ではノイズをたくさん拾ってきてしまうので、ちょっと厳しいです。もちろん、このやり方でも、最後は正規表現で値を取り出すことが多いですが、どう考えても最初から正規表現では、思わぬものを拾いすぎます。

例えば、絞り込みのために、「最終更新日」~「<form」の中の、「<ul」~「</ul>」の中の、という風にタグもインライン要素も関係なく、単純に文字列で絞り込んでいくと、あら不思議、正規表現で簡単に取り出せるようになるのです。汚いですが、超手軽。うまく絞り込みのキーワードを見つけると、対象サイトががらりとリニューアルされても、案外耐えたりします。ラッキーロバストな性質、と呼びたい感じです。indexOfだけなので、ムダに速いです。ほんとは文字列コピーがされないような作りにできればさらにパフォーマンスが良いのですが(そもそもの出自は、遠い昔Cで書いたCGIの一部なので、メモリコピーなしの実装でした)、それやろうとすると頑張るか凝るかになりそうなので、これ以上はいらんかな、と思います。

と言うわけで、以下。CoffeeScript版とJavascript版です。

dirtymashup.coffee
http = require('http')

getBetweenAll = (buff,sstr,estr) ->
  result = []
  slen = sstr.length
  elen = estr.length
  spos = 0
  epos = -elen

  while 1
    spos = buff.indexOf(sstr,epos+elen)
    if spos == -1 then return result
    epos = buff.indexOf(estr,spos+slen)
    if epos == -1 then return result
    testspos = buff.indexOf(sstr,spos+1)
    while (testspos+slen <= epos)
      spos = testspos
      testspos = buff.indexOf(sstr,spos+1)
      if testspos == -1
        break
      if testspos+slen == epos
        spos = testspos
        break
    result.push(buff.slice(spos+slen,epos-1))

download = (url,next) ->
  buff = ''
  req = http.get url,(res) ->
    res.setEncoding 'utf8'
    res.on 'data',(xml) ->
      buff += xml
    res.on 'end', ->
      #console.log 'ok '+buff.length
      return next(buff)
  req.on 'error',(err) ->
    #console.log 'error: '+err
    buff = ''
    return next(buff)
  return

# test

url = 'http://qiita.com/mm-git/items/ced8482a41cd070c245a'

download url, (buff) ->
  result = getBetweenAll buff,'profile_image_url&quot;:&quot;','&quot;}</script>'
  for str in result
    console.log str
dirtymashup.js
(function() {
  var http;//import module
  var getBetweenAll;
  var download;
  var url;//test

  http = require("http");

  getBetweenAll = function(_this) {
    return function(buff, sstr, estr) {
      var elen;
      var epos;
      var result;
      var slen;
      var spos;
      var testspos;

      result = [];
      slen = sstr.length;
      elen = estr.length;
      spos = 0;
      epos = -elen;
      for (;1;) {
        spos = buff.indexOf(sstr, epos + elen);
        if (spos === -1) {
          return result;
        }
        epos = buff.indexOf(estr, spos + slen);
        if (epos === -1) {
          return result;
        }
        testspos = buff.indexOf(sstr, spos + 1);
        for (;testspos + slen <= epos;) {
          spos = testspos;
          testspos = buff.indexOf(sstr, spos + 1);
          if (testspos === -1) {
            break;
          }
          if (testspos + slen === epos) {
            spos = testspos;
            break;
          }
        }
        result.push(buff.slice(spos + slen, epos - 1));
      }
    };
  }(this);

  download = function(_this) {
    return function(url, next) {
      var buff;
      var req;

      buff = "";
      req = http.get(url, function(res) {
        res.setEncoding("utf8");
        res.on("data", function(xml) {
          return buff += xml;
        });
        return res.on("end", function() {
          return next(buff);
        });
      });
      req.on("error", function(err) {
        buff = "";
        return next(buff);
      });
    };
  }(this);

// test

  url = "http://qiita.com/mm-git/items/ced8482a41cd070c245a";
  download(url, function(buff) {
    var i;
    var len;
    var result;

    result = getBetweenAll(buff, "profile_image_url&quot;:&quot;", "&quot;}\x3c/script>");
    i = 0;
    len = result.length;
    for (;i < len;i++) {
      console.log(result[i]);
    }
    return;
  });
}).call(this);