読者です 読者をやめる 読者になる 読者になる

S.F. Page

Programming,Music,etc...

移行スクリプトをnode.jsで作りはじめる

WordPress node.js Wordpressから静的HTMLへ ブログ JavaScript

ぼちぼちと移行スクリプトを書き始める

昨日くらいからぼちぼちとWordpressコンテンツを静的HTML化するスクリプトをnode.jsベースで書き始めた。 数日くらい前からStaticPressでいろいろ試行錯誤したりソースコードも読んでみた。このプラグインは自己スクレイピング的な方法で静的HTML化を達成している。 これはこれでいけそうな気もするけれども、私は移行と同時にデザインをBootstrapベースに変えたい。Bootstrapベースのテーマファイルを作って切り換え、その後StaticPressで移行するという方法もあるけれども、私はあえてプラグイン・コードを参考にnode.jsで移行スクリプトを書いてみることにした。

node.jsで書く理由

1番の理由はJavaScriptベースだからだ。 私が好きな言語は2つ。1つはJavaScript,もう一つはC++である。エキスパートではまるでなく、お粗末な使い方しかできないがもう手に馴染んでおりこれ以外の言語には手を出す気はない。 2番目の理由はnode.jsはクライアントPCの作業ツールとしても使える点だ。 私は仕事でもちょっとしたバッチ処理はWSHで書いている。いまだにPowerShellには移行していない。WSHはデータベースにもアクセスできたりファイル・システムへのアクセスも思いのままにできる。コマンド実行もできその出力を取り込んで加工することもできる。COM(ActiveX)オブジェクトも使用できる。Logparser(これももうobsoleteなものかもしれない)を使えばログ解析もできる。ローカルPCでの作業に必要なものはすべて揃っている。 なぜWSHを使っているかというとJavaScriptが使えるからである。Windowsのバッチ処理はこの方向で進化してほしかったが、残念ながらPowerShellに切り替わってしまいWSH自体はWindows8.1でもサポートされているものの、いずれはなくなってしまうだろう。 以前趣味で日記システムを自作して使っていたときも、HTMLファイルの生成にWSHを使っていた。これはADOを経由しMDBにデータを置き、HTAをフロントエンドとして編集システムを作り、静的HTMLをMDBから生成しFTPでアップロードするものであった。気に入っていたが私は豊富な機能のブログCMS勢に屈したのである。 node.jsもそれと同じことができる。node.jsはサーバー・サイドのJavaScript実行環境であるが、クライアントPCにインストールすることももちろんできる。ファイル・システムにもアクセスできるし、データベースへのアクセスもできるようになっている。ブラウザ経由ではあるがUIつきのローカルアプリを作ることだってできるだろう。モジュールも移行するには十分すぎるほどのもの数多くのものが揃っており、OSを問わず使用すること(制限があるものもあるかもしれないけれども)ができる。 またクライアントサイドのライブラリ(jQuery,d3.js等)もnode.jsにポーティングされており、DOM操作も行える。クライアント処理していたものをサーバーサイドに持って行ったり、またその逆も容易にできそうだ。 クライアントPCに編集システムを置いて、サーバーサイドでは閲覧のみといったことももちろんできるだろう。

やりたいこと(願望)

やりたいこと(願望)をあらめて列挙しておくと以下のとおりだ。
  • 静的HTMLはnginx
    • MUST
      • nginxがすべてのユーザートラフィックをさばく
      • http_gzip_static_moduleを使用し、トラフィックを最適化
      • 以前のWordpressとのURL互換性はできる限りnginxで吸収する
      • SEOを考慮する
        • Googleのガイドラインに従う
        • Ajaxクロール対応を行う
  • 動的HTMLはnode.js
    • MUST
      • 静的HTMLを生成する。同時にgzip化して保存しておく
      • 静的HTMLはChrome対応を念頭に置く
      • 生成するHTMLはHTML5準拠とし、セマンティックなタグは積極的に使用する
      • RDFaを導入する
      • RSS、XMLサイトマップも同時に吐く
      • Twitter,Tumblr,Youtubeとも連携する
        • oAuth対応を行う
        • APIを積極利用する
      • ページを素早く閲覧できるようにクライアントサイドでの表示方法を工夫する。
    • WANT
      • コメント機能を実現する
      • 編集機能を実装する
        • Markdownエディタ
        • イメージをGoogle DriveもしくはOne Driveにアップロードする機能
移行スクリプトは「動的HTMLはnode.js」の機能を担うことになる。 すべて実現するかどうかはわからないけれども、Wordpressからの脱却だけは最低かなえたい。涼しくなるころには移行し終えたいが、この暑さでやる気が削がれ大幅に遅れる可能性もある。

開発環境

クライアントPC上の開発環境は以下のとおりである。
  • node.js
    • Express
    • EJSかECT
    • My SQL
    • その他必要なモジュール
  • Visual Studio Express 2013 For Web
    • Web Essentials
    • node.js Tools
VSもオープンソース系の開発ツールが充実しており、インテリセンスも思いのほか効く(cssも対象)し、慣れているのでWebMatrixに代わり使用することにする。ただエディタ部分での評価しかしていないし、デバッグ機能は使用するつもりはあまりない。 実装・デバッグはクライアントで行い、これをGithub経由で開発サーバーにアップしテスト、問題なければ本番サーバーにアップする。中間に開発サーバーを入れるのは今回システムはファイル・システムに依存するので、その部分を開発サーバーでテストする必要があるからだ。例えばパーミッション・オーナー・リンクなどである。

移行スクリプトの構成

移行スクリプトはmysqlモジュールでWordpressのデータベースにアクセスし、ECTでレンダリングし、fsでファイルに書き込む。今のコードはこんな感じだ。
/// Wordpressのデータベースからコンテンツを読み込み、ectでレンダリングして指定ディレクトリ放り込む
// 変換コードはStaticPressのコードも参考にしている

var fs = require('fs');
var zlib = require('zlib');
var q = require('q');
//var gzip = zlib.createGzip({level: zlib.Z_BEST_COMPRESSION});
var ECT = require('ect');
var ect = ECT({ root : './' });
var dbj = fs.readFileSync('dbinfo.json', 'utf-8');
console.log(dbj);
var dbinfo = JSON.parse(dbj);
var home_dir = './blog/';

//app.js
var mysql = require('mysql');
var TABLE = 'wp_posts';
//mysqlクライアント作成
var client = mysql.createConnection({
  user: dbinfo.user,
  password: dbinfo.password,
  database: dbinfo.db
});


//データの検索
client.query(
  'select * from ' + TABLE + ' where post_status = "publish" order by post_date asc',
  function (err, result, field) {
    if (err) {
        throw err;
    }
    var i = 0;
    
    var loop = function () {
        
        var r = result[i];
        ect.render('template.html',
        {
            header: '',
            title: r.post_title,
            body: r.post_content,
            date: r.post_date
        }, function (err, cont) {
            if (err) throw err;
      //年月のディレクトリを作る
            var dest_dir = home_dir + r.post_date.getFullYear();
            var dest_dir_m = dest_dir + '/' + ('0' + r.post_date.getMonth()).slice(-2);//+ '/' + ('0' + r.post_date.getDate()).slice(-2);
            fs.mkdir(dest_dir,'0644' , function (err) {
                if (!fs.existsSync(dest_dir)) { throw err; }
                fs.mkdir(dest_dir_m, function (err) {
                    if (!fs.existsSync(dest_dir_m)) { throw err; }
                    var cont_path = dest_dir_m + '/' + decodeURI(r.post_name) + '.html';
                    fs.writeFileSync(cont_path, cont);
          // gzipファイルを作る
                    var out = fs.createWriteStream(cont_path + '.gz');
                    fs.createReadStream(cont_path)
                     .pipe(zlib.createGzip({ level: zlib.Z_BEST_COMPRESSION }))
                     .pipe(out);
                    ++i;
                    if (i < 100) {
                        loop();
                    }
                });
            });
        });
        
        
        //        fs.writeFileSync(cont_path + '.gz', zlib.createGzip({ level: zlib.Z_BEST_COMPRESSION }));
        //        inp.pipe(zlib.createGzip({ level: zlib.Z_BEST_COMPRESSION })).pipe(out);
        /*
                    fs.chown(dest_dir + decodeURI(r.post_name) + '.html', 'nginx','webusers', function (err) {
                        if (err) throw err;
                        fs.chmod(dest_dir + decodeURI(r.post_name) + '.html', '0444');
                    })*/
    
    };
    loop();
    client.end();
  }
);
しかし非同期処理ばりばりなのでC++でいうところのasyncやPPLが欲しいところである。なにやらnode.jsにもasyncやqとかいうものがありそれを使うと見通しの良いコードが書けるらしいのでこれから勉強することにする。