replace_recursive.py の技術ドキュメント

プログラムの動作

replace_recursive.py は、指定されたソースディレクトリ内のファイルを再帰的に走査し、その内容を加工して新しいターゲットディレクトリにコピーするPythonスクリプトです。主に、ウェブサイトのコンテンツを移行する際に、内部リンクやリソースへのパスを自動的に修正することを目的としています。

このプログラムの主な機能は以下の通りです。

  1. ファイルの走査とコピー: 指定されたソースディレクトリ(デフォルトでは msl)以下の全てのファイルとディレクトリを再帰的に走査し、ターゲットディレクトリ(デフォルトでは msl_titech)に同じ構造でコピーします。

  2. 条件付き文字列置換: 特定のファイル拡張子(デフォルトでは .html, .css, .js)を持つファイルに対してのみ、定義された正規表現ルールに基づいた文字列置換を行います。

  3. エンコーディングの自動検出: 文字列置換を行う際、ファイルのエンコーディングを自動的に検出し、読み込みを行います。書き出しは常に utf-8 エンコーディングで行われます。

  4. 除外リスト: 特定のファイル名を指定することで、それらのファイルを文字列置換の対象から除外することができます(ただし、コピーは行われます)。

  5. パスの修正: 具体的な置換ルールはスクリプト内にハードコードされており、ウェブサイトのベースURLやリソースパスを新しいパス形式に変換することを目的としています。例えば、絶対URLを相対パスやサブディレクトリパスに変換します。

このプログラムは、静的なウェブサイトをサーバー上の異なるパスにデプロイしたり、開発環境と本番環境でパスの構成が異なる場合に、手動での修正作業を軽減するのに役立ちます。

原理

このプログラムは、いくつかの標準ライブラリと非標準ライブラリを組み合わせてファイルの走査、エンコーディング検出、そして文字列置換を実現しています。

  1. ディレクトリの走査 ( os.walk ): os.walk(source_dir) 関数を使用して、指定された source_dir 以下の全てのサブディレクトリとファイルを再帰的に探索します。これにより、元のディレクトリ構造を維持したまま、全てのファイルを処理対象とすることができます。

  2. ファイルのコピーとパス操作 ( os および shutil ):

    • os.path.join(): ディレクトリ名とファイル名を結合して完全なファイルパスを生成します。

    • os.path.relpath(): あるファイルパスが基準ディレクトリに対してどのような相対パスであるかを計算します。これにより、ソースディレクトリからの相対パスをターゲットディレクトリに適用できます。

    • shutil.copy2(): ファイルのコピーを行います。この関数は、元のファイルのメタデータ(アクセス時刻、変更時刻、パーミッションなど)も可能な限り保持してコピーするため、単なる内容のコピーよりも実用的な選択肢です。

    • os.makedirs(): ターゲットディレクトリやサブディレクトリが存在しない場合に、それらを再帰的に作成します。

  3. ファイルエンコーディングの検出 ( chardet ): chardet.detect() 関数は、与えられたバイナリデータから最も可能性の高い文字エンコーディングを推定します。これにより、Shift-JISEUC-JPUTF-8 など、様々なエンコーディングで保存されたファイルを正しく読み込むことが可能になります。プログラムは、まずファイルをバイナリモード ('rb') で読み込み、chardet でエンコーディングを検出した後、検出されたエンコーディングでテキストモード ('r') でファイルを再度読み込みます。

  4. 正規表現による文字列置換 ( re.sub ): re モジュールの re.sub() 関数を使用して、ファイルの内容に対して正規表現に基づく文字列置換を行います。

    • プログラム内で定義されている replace_list は、[正規表現パターン, 置換文字列] のペアのリストです。

    • 例えば、[r'https:\/\/www\.msl\.titech\.ac\.jp\/', '/msl_titech/'] というルールは、https://www.msl.titech.ac.jp/ という絶対URLを、ウェブサイトのサブディレクトリパス /msl_titech/ に置換します。正規表現パターン内の特殊文字(この場合は /)は \ でエスケープされています。

    • re.sub(pattern, repl, text) は、text 内で pattern にマッチする全ての箇所を repl で置換した新しい文字列を返します。この処理が replace_list 内の全てのルールに対して順次適用されます。

必要な非標準ライブラリとインストール方法

このプログラムは、ファイルエンコーディングの自動検出のために chardet ライブラリを使用しています。

インストールは pip コマンドで行うことができます。

pip install chardet

必要な入力ファイル

このプログラムの動作には、処理対象となるソースディレクトリが必要です。 デフォルトでは、スクリプトと同じ階層に msl という名前のディレクトリが存在し、そのディレクトリ以下に加工したいファイル群が格納されていることを想定しています。

例:

.
├── replace_recursive.py
└── msl/
    ├── index.html
    ├── about/
    │   └── index.html
    ├── assets/
    │   ├── style.css
    │   └── script.js
    └── images/
        └── logo.png
  • source_dir 変数(デフォルトは 'msl')で指定されたディレクトリとその中のファイル。

  • convert_file_extensions 変数(デフォルトは ['.html', '.css', '.js'])で指定された拡張子を持つファイルが文字列置換の対象となります。

  • exclude_list 変数(デフォルトは ['500.html'])に含まれるファイル名は、文字列置換の対象から除外されます。

これらの入力ファイルは、実際のウェブサイトのコンテンツ(HTML、CSS、JavaScriptファイル、画像など)を模した形式であることが期待されます。

生成される出力ファイル

プログラムの実行後、target_dir 変数(デフォルトは 'msl_titech')で指定されたパスに、処理されたファイルとディレクトリが生成されます。

例:

.
├── replace_recursive.py
├── msl/
│   └── ... (入力ファイル群)
└── msl_titech/           <-- このディレクトリが生成される
    ├── index.html        <-- 内容が置換される
    ├── about/
    │   └── index.html    <-- 内容が置換される
    ├── assets/
    │   ├── style.css     <-- 内容が置換される
    │   └── script.js     <-- 内容が置換される
    └── images/
        └── logo.png      <-- 内容は変更されずにコピーされる
  • source_dir と同じディレクトリ構造が target_dir 以下に再現されます。

  • convert_file_extensions に含まれる拡張子を持つファイルは、定義された replace_list に基づいて文字列が置換され、utf-8 エンコーディングで保存されます。

  • それ以外の拡張子を持つファイル(例: .png, .jpg, .pdf など)は、内容の変更なしにそのままコピーされます。

  • exclude_list に含まれるファイル(例: 500.html)は、文字列置換の処理は行われず、そのままコピーされます。

  • target_dir が存在しない場合は、プログラムによって自動的に作成されます。

コマンドラインでの使用例 (Usage)

このプログラムはコマンドライン引数を受け取らないため、実行は非常にシンプルです。 replace_recursive.py ファイルが保存されているディレクトリで以下のコマンドを実行します。

python replace_recursive.py

注意: ソースディレクトリ (source_dir)、ターゲットディレクトリ (target_dir)、置換対象のファイル拡張子 (convert_file_extensions)、除外リスト (exclude_list)、および置換ルール (replace_list) は、スクリプトの先頭部分にグローバル変数としてハードコードされています。これらの設定を変更したい場合は、スクリプトのソースコードを直接編集する必要があります。

コマンドラインでの具体的な使用例

前提条件

  1. replace_recursive.py がカレントディレクトリに存在します。

  2. chardet ライブラリがインストール済みです。

  3. 以下のディレクトリ構造とファイル内容が存在すると仮定します。

    .
    ├── replace_recursive.py
    └── msl/
        ├── index.html
        └── assets/
            └── style.css
    
  4. msl/index.html の内容(例):

    <!DOCTYPE html>
    <html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>MSL Home</title>
        <link rel="stylesheet" href="./assets/style.css">
    </head>
    <body>
        <h1>Welcome to MSL</h1>
        <p>Visit our main site at <a href="https://www.msl.titech.ac.jp/">MSL Website</a>.</p>
        <p>Local asset path: <img src="/assets/image.png"></p>
    </body>
    </html>
    
  5. msl/assets/style.css の内容(例):

    body {
        font-family: Arial, sans-serif;
        background-color: #f0f0f0;
        /* Absolute URL in CSS */
        background-image: url('https://www.msl.titech.ac.jp/images/bg.png');
    }
    

実行コマンド

python replace_recursive.py

実行結果

プログラムは msl ディレクトリを走査し、msl_titech ディレクトリを生成します。

Convert msl/index.html (index.html) to msl_titech/index.html
Convert msl/assets/style.css (assets/style.css) to msl_titech/assets/style.css

msl_titech ディレクトリが生成され、その中に変換されたファイルが保存されます。

msl_titech/index.html の内容は以下のようになります(置換ルールに基づいて変更されます):

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>MSL Home</title>
    <link rel="stylesheet" href="/msl_titech/assets/style.css">
</head>
<body>
    <h1>Welcome to MSL</h1>
    <p>Visit our main site at <a href="/msl_titech/">MSL Website</a>.</p>
    <p>Local asset path: <img src="/msl_titech/assets/image.png"></p>
</body>
</html>

msl_titech/assets/style.css の内容は以下のようになります:

body {
    font-family: Arial, sans-serif;
    background-color: #f0f0f0;
    /* Absolute URL in CSS */
    background-image: url('/msl_titech/images/bg.png');
}

このように、スクリプト内の replace_list で定義された正規表現パターンに従い、元のファイルのURLやパスが新しいターゲットパス(/msl_titech/)に自動的に修正されます。