全国オンライン対応受付 平日 9:00-18:00
お問い合わせ

開発記録 / 1cto.jp制作記 / Vol.26 1cto.jp制作記 Vol.3

記事 01

54本の記事を一括でデータベースに投入した話——SCP・Python・psycopg2の連携

1cto.jpのデータベースにはまだ入っていない。ファイルとしては存在しているが、サイトから読めない。1本ずつ管理画面から入力するという選択肢もあったが、54本は現実的ではない。

2026-06-07 公開

ローカルに記事が54本あった。

1cto.jpのデータベースにはまだ入っていない。ファイルとしては存在しているが、サイトから読めない。1本ずつ管理画面から入力するという選択肢もあったが、54本は現実的ではない。

スクリプトで一括投入することにした。


まず方針を決めた。

記事ファイルはローカルのDropboxフォルダに保存されている。Dropboxはリアルタイム同期しているが、EC2サーバーにはDropboxが入っていない。記事ファイルをEC2に転送してから、EC2上でスクリプトを実行する。

転送にはSCPを使う。SCPはSSH経由でファイルをコピーするコマンドだ。EC2への接続にはpemキーを使っているので、SCPでも同じキーを指定する。


最初にPowerShellからSCPを実行した。失敗した。

エラーメッセージはこうだった。「Bad permissions. Try removing permissions for user: UNKNOWN\\UNKNOWN」

Windowsのssh-agentがpemキーのパーミッションを厳しくチェックする。PowerShellが使うSSHクライアントはOpenSSHの実装だが、キーのアクセス権が「自分だけ」でないと弾かれる。

Claude Codeにも同じことが起きたが、Bashツールから実行すると動いた。Claude CodeはWindowsとWSL/bash双方のツールを持っていて、Bashツールはパーミッションの扱いが違う。原因を調べるより先に、Bashツールに切り替えた方が早かった。


記事ディレクトリのパスに日本語が含まれていた。

D:/シンプルシステム株式会社 Dropbox/伊藤勝彦/000claude/1cto/articles/

Windowsのパスに日本語が入ることは珍しくないが、コマンドラインで扱うときは注意が必要だ。シングルクォートで括って渡した。Bashはダブルクォートでも動くが、スペースが含まれるパスはどちらかで括らないとエラーになる。

転送自体は成功した。13シリーズ分のディレクトリとmdファイルが/home/ubuntu/1cto/articles_import/にコピーされた。


次にEC2上でPythonスクリプトを実行した。insert_articles.pyというファイルだ。

スクリプトの設計はシンプルだ。ボリュームの定義(vol_no・名称・説明・シリーズ名・価格・対象ディレクトリ・記事番号の範囲)を配列に持つ。各ボリュームについて、対象ディレクトリからファイルを取得し、タイトルと要約を抽出して、データベースに投入する。

既に存在する記事はスキップする。べき等性を持たせてあるので、何度実行しても重複しない。

def insert_article(cur, vol_no, article_no, title, content, summary, is_free):
    cur.execute('SELECT article_id FROM articles WHERE volume_no=%s AND article_no=%s',
                (vol_no, article_no))
    if cur.fetchone():
        print(f'vol{vol_no} art{article_no} SKIP: {title[:40]}')
        return
    cur.execute('''INSERT INTO articles (...) VALUES (...)''', (...))

実行結果は49本の投入だった。54本と思っていたが、一部のシリーズで記事数が予定より少なかった。スキップは0件だったので重複もない。

DBの総記事数が49から98になった。

1ランすると全ボリュームの進捗が流れる。vol11 INSERT: 入退室管理システム制作記 Vol.1vol12 INSERT: 予約システム制作記 Vol.1、というログが続く。どのボリュームが入ったか確認しながら眺めた。


記事の数が増えることと、記事が読まれることは別の話だ。

98本がDBに入った。サイトからアクセスすれば読める。次の課題はStripeのprice_idだ。各ボリュームに対応する価格IDをStripe管理画面で設定しないと、購入フローが動かない。

記事を書いて、DBに入れて、Stripeに繋ぐ。プラットフォームとしての完成は、この順序で進む。

次回は、insert_articles.pyがタイトルと要約をどう抽出したか、スクリプトの内部設計を書く。

シンプルシステム株式会社 代表 伊藤勝彦