WordPress:はてなブログからWordPressへの移行ツール Ver.2

ある日突然、はてなブログから WordPress に移行しようと思い始め、自分自身のデータはすでに問題なく移行できる状態になっているのですが、そのツール制作にはまって Ver.2 まで作ってしまいました。



Ver.1 とその問題点

はてなブログからWordPressへワンクリックで移行するツールの hatena2wp.php の詳細は以下の記事にあります。


www.imuza.com


このツールでできることは、はてなブログのエクスポートファイルを指定すると、そのファイルからはてなフォトライフの画像 URL を抜き出してダウンロードし、WordPress の正規画像保存と同等のフォルダ仕様でコピーすることと、エクスポートファイルの画像 URL の書き換えなどの整形を一括して行うことです。

詳細は上記記事をご覧ください。


で、このツールの Ver.1 では、エクスポートファイルの画像 URL を1ヶ所だけ手作業で書き換える必要があります。Ver.2 では、それを自動でドメイン名を取得して自動化しようとしたのですが、考えてみれば、実際に移行する場合にはいきなり独自ドメインで立ち上げて移行することは考えづらく、通常レンタルサーバーの仮ドメインに移行し、十分なチェックをしてから独自ドメインでアクセスできるようにすると考えられます。


ですのでツールとしては、エクスポートファイルの画像 URL をユーザーが自由に決められるようにするべきということになります。


はてなブログ to WordPress 移行ツール Ver.2

ver.2 では2段階方式にしました。

まず、hatena2wp-1.php では、はてなブログのエクスポートファイルを指定しますと、はてなフォトライフから画像ファイルを WordPress にコピーします。これは Ver.1 と同じです。


続いて、「続いて、エクスポートファイルの整形を行います」のリンクをクリックしますと、hatena2wp-2.php に移り、はてなエクスポートファイルの画像 URL をドメイン付きにするかしないか、また運用はドメイン直下かサブディレクトリかをユーザーに入力するよう促します。

ドメイン名、あるいはフォルダー名を指定しますと、エクスポートファイル内の画像 URL を書き換え、その他の整形を同時にします。


具体的な指定方法は次のとおりです。

指定(例) 画像 URL
https://hogehoge https://hogehoge/wp-content/uploads/(yyyy)/(mm)/(ファイル名)
https://hogehoge/wordpress https://hogehoge/wordpress/wp-content/uploads/(yyyy)/(mm)/(ファイル名)
blankと入力 /wp-content/uploads/(yyyy)/(mm)/(ファイル名)
/wordpress /wordpress/wp-content/uploads/(yyyy)/(mm)/(ファイル名)



ダウンロード

GitHub からクローンするか、下の hatena2wp-1.php hatena2wp-2.php をコピーして保存してください。保存ディレクトリにはサーバーの書き込み権限が必要です。


使い方

  • WordPress がインストールされたディレクトリに hatena2wp などのディレクトリをつくる
  • そのディレクトリに ftp で hatena2wp-1.php hatena2wp-2.php を入れる
  • WordPress の URL/hatena2wp/hatena2wp-1.php にアクセスする
  • はてなブログのエクスポートファイルを選択する
  • 画像ファイルのコピーが終了すると、ファイル整形へのリンクが表示されるのでクリックする
  • 画像ファイルのパスを指定する
  • 終了を待つ
  • プラグイン「Movable Type・TypePad インポートツール」を使い、「mt-export.txt のインポート」をクリックする


これで、はてなブログから WordPress への移行が完了します。はてなブログのパーマリンクを維持するには「はてなブログ用リライトルールプラグイン」を入れてください。


なお、移行したファイルは、そのままでは WordPress のメディアライブラリには表示されませんが画像表示には問題はありません。その画像を後に他の記事で使う予定がなければメディアライブラリに登録する必要はないと思います。


「Media from FTP」でメディアライブラリに一括登録する場合は、「日付」の選択や「 アップロードしたファイルを年月ベースのフォルダーに整理」のチェックを外さないとすべてのファイルが登録年月のフォルダに入ってしまい、画像が表示されなくなってしまいますので要注意です。


f:id:ausnichts:20210904210206j:plain


エクスポートファイルの整形の詳細

エスクポートファイルは次の整形をしています。


  • 画像の URL 変更、alt, title 等不要属性削除
  • 不要なクラス削除
  • 見出しの変更 h3->h2, h4->h3, h5->h4
  • キーワードリンク削除
  • Youtube リンクを https に変更


整形後は、プラグイン「Movable Type・TypePad インポートツール」で即インポートできるように整形済みファイルを wp-content/mt-export.txt に保存しています。


終了後

作成した hatena2wp ディレクトリ、mt-export.txt を削除してください。


移行ツール Ver.2

hatena2wp-1.php

f:id:ausnichts:20210905095954j:plain


<?php
session_start();
$token = isset($_POST["token"]) ? $_POST["token"] : "";
$flg = false;
if ($token == ''){
    $token = uniqid('', true);
    $_SESSION['token'] = $token;
}else{
    $session_token = isset($_SESSION["token"]) ? $_SESSION["token"] : "";
    unset($_SESSION["token"]);
    if($token == $session_token) {
        $flg = true;
    }else{
        $token = uniqid('', true);
        $_SESSION['token'] = $token;
    }
}
?>

<!DOCTYPE html>
<html>
<head>
   <title>はてなブログからWordPressへ</title>
</head>
<body>
    <p>はてなブログから WordPress へ移行のためのサポートツール-1です</p>
    <p>はてなブログのエクスポートファイルから、はてなフォトライフの URL を取り出し、その画像を wp-contents/uploads 以下の yyyy/mm ディレクトリにコピーします</li>
    <hr>
    <p>はてなブログのエクスポートファイルを指定してください</p>
    <p>画像ファイルのコピーはそれなりの時間を要しますので、進行状態は 100ファイルずつ表示します<br>ただし、サーバーの設定によっては逐次表示されずに終了後一括表示される場合があります</p>
    <form enctype="multipart/form-data" action="hatena2wp-1.php" method="POST">
        <input type="hidden" name="token" value="<?php echo $token;?>">
        <input type="file" name="uploaded_file"></input><br>
        <input type="submit" value="Upload"></input>
    </form>
<?PHP
if($flg && !empty($_FILES['uploaded_file'])){
    $path = './';
    $path = $path . basename( $_FILES['uploaded_file']['name']);

    if(move_uploaded_file($_FILES['uploaded_file']['tmp_name'], $path)) {
        echo "ファイル " . basename( $_FILES['uploaded_file']['name']) . " がアップロードされました<br><br>";

        $pattern1 = '/(https?:\/\/cdn-ak\.f\.st-hatena.com\/images\/fotolife\/.+?(jpg|gif|png))/';
        $pattern2 = '/^https?:\/\/cdn-ak\.f\.st-hatena.com\/images\/fotolife\/.+?\/.+?\/(\d{4})(\d{2})\d{2}\/(.+)$/';
        $array = array();

        $hatena_text = file_get_contents($path);
        preg_match_all($pattern1, $hatena_text, $matches);
        $img_urls = array_unique($matches[1]);  
        $total = count($img_urls);
        $count = 0;

        echo "画像ファイルは " . $total . " ファイルあります<br>";
        echo "表示は 100ファイルコピーごとに更新します<br><br>";
        ob_flush();
        flush();
    
        foreach($img_urls as $url){
            if($img_data = @file_get_contents($url)){
                preg_match($pattern2, $url, $matches);
                $directory_name = '../wp-content/uploads/' . $matches[1] . '/' . $matches[2];
                $img_file_name = $directory_name . '/' . $matches[3];

                if(!is_dir($directory_name)){
                    mkdir($directory_name, 0705, true);
                }
                file_put_contents($img_file_name, $img_data);
                $array[] = $img_file_name;
                $count++;
                if($count % 100 == 0){
                    echo $count . " / " . $total . "<br>";
                    ob_flush();
                    flush();
                }
            }
        }
        echo $total . " / " . $total . "<br><br><hr>";

        $new_file = implode("\n", $array);
        $new_file_name = "images_list.txt";
        file_put_contents($new_file_name, $new_file);
        file_put_contents("uploadfile.txt", $path);
        echo "続いて、エクスポートファイルの整形を行います<br><a href='hatena2wp-2.php'>ファイル整形へ</a>";
    } else{
        echo "ファイルをアップロードできませんでした";
    }
}
?>
</body>
</html>


hatena2wp-2.php

f:id:ausnichts:20210905100021j:plain


hatena2wp-2.php 単独では動きません。

<?php
session_start();
$token1 = isset($_POST["token1"]) ? $_POST["token1"] : "";
$token2 = isset($_POST["token2"]) ? $_POST["token2"] : "";
$flg = false;
if ($token1 == '' && $token2 == ''){
    $token1 = uniqid('', true);
    $_SESSION['token1'] = $token1;
}elseif($token1 != ''){
    $session_token1 = isset($_SESSION["token1"]) ? $_SESSION["token1"] : "";
    unset($_SESSION["token1"]);
    if($token1 == $session_token1) {
        $flg = true;
        $token1 = '';
        $token2 = uniqid('', true);
        $_SESSION['token2'] = $token2;
    }else{
        $token1 = uniqid('', true);
        $_SESSION['token1'] = $token1;
    }
}elseif($token2 != ''){
    $session_token2 = isset($_SESSION["token2"]) ? $_SESSION["token2"] : "";
    unset($_SESSION["token2"]);
    if($token2 == $session_token2 && isset($_POST['submit'])){
        $flg = true;
    }elseif(isset($_POST['submit'])){
        $token2 = uniqid('', true);
        $_SESSION['token2'] = $token2;
    }else{
        $token2 = '';
        $token1 = uniqid('', true);
        $_SESSION['token1'] = $token1;
    }           
}
?>

<!DOCTYPE html>
<html>
<head>
   <title>はてなブログからWordPressへ</title>
</head>
<body>
    <p>はてなブログから WordPress 移行のためのサポートツール-2です</p>
    <p style="margin-bottom:0;">エクスポートファイルを WordPress 用に整形します</p>
    <ul style="margin-top:0;">
        <li>画像の URL 変更、alt, title 等不要属性削除</li>
        <li>見出しの変更 h3->h2, h4->h3, h5->h4</li>
        <li>キーワードリンク削除</li>
        <li>Youtube リンクを https に変更</li>
    </ul>
    <hr>
    <p>はてなエクスポートファイルの画像 URL をドメイン付きにするかしないか、また運用はドメイン直下かサブディレクトリかを指定してください</p>
    <table border="1" style="border-collapse: collapse">
    <thead>
        <tr>
            <th>指定(例)</th>
            <th>画像 URL</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>https://hogehoge</td>
            <td>https://hogehoge/wp-content/uploads/(yyyy)/(mm)/(ファイル名)</td>
        </tr>
        <tr>
            <td>https://hogehoge/wordpress</td>
            <td>https://hogehoge/wordpress/wp-content/uploads/(yyyy)/(mm)/(ファイル名)</td>
        </tr>
        <tr>
            <td>blankと入力</td>
            <td>/wp-content/uploads/(yyyy)/(mm)/(ファイル名)</td>
        </tr>
        <tr>
            <td>/wordpress</td>
            <td>/wordpress/wp-content/uploads/(yyyy)/(mm)/(ファイル名)</td>
        </tr>
    </tbody>
    </table><br>
    <form action="hatena2wp-2.php" method="POST">
        <input type="hidden" name="token1" value="<?php echo $token1 ?>">
        <input type="text" name="wp_path"><br>
        <input type="submit" value="Submit">
    </form>
<?php
if($flg && isset($_POST['submit'])){
    $wp_path = $_POST['wp_path'];
    $path = file_get_contents('uploadfile.txt');
    $hatena_text = file_get_contents($path);
    $patterns = array (
        '/https?:\/\/cdn-ak\.f\.st-hatena.com\/images\/fotolife\/.+?\/.+?\/(\d{4})(\d{2})\d{2}\/(.+\.(jpg|gif|png))/', 
        '/alt="f:id:.+?"/', 
        '/title="f:id:.+?"/', 
        '/ figure-image-fotolife mceNonEditable/', 
        '/ class="mceEditable"/', 
        '/<h3 /', 
        '/<\/h3>/', 
        '/<h4 /', 
        '/<\/h4>/', 
        '/<h5 /', 
        '/<\/h5>/', 
        '/<a .*href="http:\/\/d\.hatena\.ne\.jp\/keyword\/.+?".*?>(.+)?<\/a>/', 
        '/<iframe.+?youtube\.com\/embed\/(.+)?\?.+?<\/iframe>/'
        );
    $replace = array (
        $wp_path . '/wp-content/uploads/$1/$2/$3', 
        'alt=""', 
        'title=""', 
        '', 
        '', 
        '<h2 ', 
        '</h2>', 
        '<h3 ', 
        '</h3>', 
        '<h4 ', 
        '</h4>', 
        '$1', 
        '<iframe src="https://www.youtube.com/embed/$1?enablejsapi=1" width="560" height="315" frameborder="0" allowfullscreen></iframe>'
        );

    $new_file = preg_replace($patterns, $replace, $hatena_text);

    $new_file_name = "../wp-content/mt-export.txt";
    file_put_contents($new_file_name, $new_file);
    echo "WordPress 用に整形したファイルを " . $new_file_name . " に保存しました<br>
      プラグイン「Movable Type・TypePad インポートツール」を使い、「mt-export.txt のインポート」をクリックしてください<br><br>";
    
    $image_list = file_get_contents("images_list.txt");
    $new_file = str_replace('..', $wp_path, $image_list);
    $new_file_name = "images_list.txt";
    file_put_contents($new_file_name, $new_file);  
    echo "アップロードしたファイル一覧を " . $new_file_name . " に保存しました";

}elseif($flg && !empty($_POST['wp_path'])){
    $path = $_POST['wp_path'] == 'blank' ? '' : $_POST['wp_path'];
    echo "画像 URL は " . $path . "/wp-content/uploads/(yyyy)/(mm)/(ファイル名) でいいですか?<br>";
    echo "<form action='hatena2wp-2.php' method='POST'>";
    echo '<input type="hidden" name="token2" value="' . $token2 . '">';
    echo "<input type='hidden' name='wp_path' value='" . $path . "'>"; 
    echo "<input type='submit' name='submit' value='YES'> ";
    echo "<input type='submit' value='NO'><br>";
    echo "</form>";
}
?>
</body>
</html>


ライセンス等

ご使用の場合は以下の注意事項をお守りください。

  • ライセンスは IMUZA.com にあります。
  • 紹介は歓迎ですが、バグ対応ができなくなりますので転載はしないでください。
  • 当記事の Javascript を使用してのはてなブログ用テーマの制作、公開は商用以外は自由です。ただし、記事内、あるいは紹介サイト内に IMUZA.com へのリンクを挿入してください。
  • 当記事の CSS への改変、公開は自由です。
  • 特別問題が発生することはありませんが自己責任でお使いください。
    問題が発生した場合は削除すればもとに戻ります。
  • お問い合わせ、バグの報告、仕様変更のご要望等は Contact Us までお願いします。