ChatGPTで作る「WordPress記事まとめアプリ」|NotebookLMで過去記事をAI分析!

ChatGPTで作るWordPress記事まとめアプリ|NotebookLMで過去記事をAI分析!
目次

はじめに:過去記事を“AI編集者”に読ませたい

ブログを書き続けていると、
「自分の記事ってどんな傾向があるんだろう?」
「方向性を整理したいけど、読み返す時間がない…」
そんな瞬間、ありませんか?

私は今回、NotebookLM(GoogleのAIノート)を使って
過去記事をまとめて分析してもらうことにしました。

ただし、NotebookLMに直接WordPressの記事を読み込ませることはできません。
そこで、ChatGPTに頼んで「XML → TXT変換アプリ」を一緒に作ってみたんです。


WordPressから記事データをエクスポートする

まず、WordPressから記事データを取り出します。
標準機能でとても簡単です。

  1. 管理画面の「ツール」→「エクスポート」へ
  2. 「投稿」を選択
  3. 「エクスポートファイルをダウンロード」をクリック

これで .xml 形式のファイル(WXRファイル)が手に入ります。
この中に、タイトル・カテゴリ・本文・日付などがすべて含まれています。


ChatGPTと一緒に作った「変換アプリ」

次のステップは、このXMLをNotebookLMで読めるTXT形式に整えること。
ChatGPTに相談しながらPythonでGUIアプリを作りました。

🧩 使用技術:

  • 言語:Python(標準ライブラリのみ)
  • GUI:Tkinter
  • 処理:XMLパース → テキスト整形

💻 開発のリアルタイム記録

開発にかかった時間は約20分
途中、エラーが2回出ました。

❌ 本文が出力されない
→ 原因は content:encodedexcerpt:encoded の混同。すぐ修正。

❌ full_tag が未定義
→ 変数のスコープを見直して解決。

さらに、出力フォルダを自由に選べるようにフォルダ選択機能を1回追加
結果、シンプルで使いやすいアプリに仕上がりました。


⚙️ 変換アプリの使い方

  1. アプリを起動
  2. 「XMLを選ぶ」ボタンでWordPressのエクスポートファイルを指定
  3. 出力先フォルダとファイル名を確認
  4. 「変換開始」をクリック

わずか数秒で、すべての記事がNotebookLM向けTXTファイルに変換されます。
100記事規模でも爆速。

出力形式はこんな感じ👇

# タイトル
大人になってからのギター入門|独学とレッスンの違い

# カテゴリ
ギター/音楽

# 投稿日
2024-12-18 10:00:00

# 本文
社会人になってからギターを始める人が増えています…
(本文続く)

---

HTMLタグやショートコードも削除されているので、NotebookLMでの読み込みがスムーズです。


実際のプログラムはこちら↓
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
NotebookLM Exporter (WXR → TXT)
--------------------------------
WordPressのエクスポートXML(WXR)をNotebookLM用TXTに変換するツール。
本文が出力されるよう修正済み。出力フォルダ選択機能を追加。
"""

import re
import sys
import traceback
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from html import unescape
from pathlib import Path
import xml.etree.ElementTree as ET

APP_TITLE = "NotebookLM Exporter (WXR → TXT)"
DEFAULT_OUT_NAME = "notebooklm_export.txt"

# --- HTMLクリーナ ---
SHORTCODE_PATTERN = re.compile(r"\[[^\[\]\n\r]*\]")
HTML_COMMENT_PATTERN = re.compile(r"<!--.*?-->", re.S)
SCRIPT_PATTERN = re.compile(r"<script.*?>.*?</script>", re.S | re.I)
STYLE_PATTERN = re.compile(r"<style.*?>.*?</style>", re.S | re.I)
TAG_PATTERN = re.compile(r"<[^>]+>")
MULTIBLANK_PATTERN = re.compile(r"\n{3,}")
TRAIL_SPACE_PATTERN = re.compile(r"[ \t]+\n")

def clean_html(html_text: str) -> str:
    if not html_text:
        return ""
    text = unescape(html_text)
    text = HTML_COMMENT_PATTERN.sub("", text)
    text = SHORTCODE_PATTERN.sub("", text)
    text = SCRIPT_PATTERN.sub("", text)
    text = STYLE_PATTERN.sub("", text)
    text = TAG_PATTERN.sub("", text)
    text = text.replace("\xa0", " ").replace("\r\n", "\n").replace("\r", "\n")
    text = TRAIL_SPACE_PATTERN.sub("\n", text)
    text = MULTIBLANK_PATTERN.sub("\n\n", text)
    return text.strip()

def convert_wxr_to_notebooklm_txt(xml_path: Path, out_path: Path):
    ns = {
        'content': 'http://purl.org/rss/1.0/modules/content/',
        'wp': 'http://wordpress.org/export/1.2/'
    }
    context = ET.iterparse(str(xml_path), events=("start", "end"))
    in_item = False
    count = 0
    out_path.parent.mkdir(parents=True, exist_ok=True)

    with out_path.open('w', encoding='utf-8', newline='\n') as fout:
        for event, elem in context:
            tag_full = elem.tag
            tag = tag_full.split('}', 1)[1] if '}' in tag_full else tag_full

            if event == 'start' and tag == 'item':
                in_item = True
                data = {'title': '', 'date': '', 'type': '', 'status': '', 'categories': [], 'content_html': ''}

            elif event == 'end' and in_item:
                if tag == 'title':
                    data['title'] = (elem.text or '').strip()
                elif tag == 'post_date':
                    data['date'] = (elem.text or '').strip()
                elif tag == 'post_type':
                    data['type'] = (elem.text or '').strip()
                elif tag == 'status':
                    data['status'] = (elem.text or '').strip()
                elif tag == 'category' and elem.get('domain') == 'category':
                    if elem.text:
                        data['categories'].append(elem.text.strip())
                elif tag == 'encoded':
                    if tag_full.startswith('{http://purl.org/rss/1.0/modules/content/}'):
                        data['content_html'] = elem.text or ''
                elif tag == 'item':
                    in_item = False
                    if data['type'] == 'post' and data['status'] == 'publish':
                        content = clean_html(data['content_html'])
                        cats = '/'.join(data['categories'])
                        block = f"# タイトル\n{data['title']}\n\n# カテゴリ\n{cats}\n\n# 投稿日\n{data['date']}\n\n# 本文\n{content}\n\n---\n"
                        fout.write(block)
                        count += 1
                    elem.clear()
        try:
            root = context.root
            if root is not None:
                root.clear()
        except Exception:
            pass
    return count

class App(ttk.Frame):
    def __init__(self, master):
        super().__init__(master)
        self.master.title(APP_TITLE)
        self.pack(fill=tk.BOTH, expand=True)
        self.xml_path = tk.StringVar()
        self.out_dir = tk.StringVar(value=str(Path.home()))
        self.out_name = tk.StringVar(value=DEFAULT_OUT_NAME)
        self._build_ui()

    def _build_ui(self):
        ttk.Label(self, text="WordPressエクスポートXML:").pack(anchor=tk.W, padx=8, pady=4)
        ttk.Entry(self, textvariable=self.xml_path, width=80).pack(padx=8, pady=2)
        ttk.Button(self, text="XMLを選ぶ", command=self.choose_xml).pack(padx=8, pady=4)

        frm_out = ttk.LabelFrame(self, text="出力")
        frm_out.pack(fill=tk.X, padx=8, pady=8)
        row1 = ttk.Frame(frm_out)
        row1.pack(fill=tk.X, pady=2)
        ttk.Label(row1, text="出力フォルダ:").pack(side=tk.LEFT)
        ttk.Entry(row1, textvariable=self.out_dir, width=60).pack(side=tk.LEFT, padx=8)
        ttk.Button(row1, text="選択", command=self.choose_outdir).pack(side=tk.LEFT)

        row2 = ttk.Frame(frm_out)
        row2.pack(fill=tk.X, pady=2)
        ttk.Label(row2, text="ファイル名:").pack(side=tk.LEFT)
        ttk.Entry(row2, textvariable=self.out_name, width=40).pack(side=tk.LEFT, padx=8)

        ttk.Button(self, text="変換開始", command=self.run_convert).pack(padx=8, pady=10)
        self.txt_log = tk.Text(self, height=10)
        self.txt_log.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)

    def log(self, msg):
        self.txt_log.insert(tk.END, msg + "\n")
        self.txt_log.see(tk.END)

    def choose_xml(self):
        path = filedialog.askopenfilename(filetypes=[("XMLファイル", "*.xml"), ("すべて", "*.*")])
        if path:
            self.xml_path.set(path)
            base = Path(path).stem
            self.out_name.set(f"{base}_notebooklm.txt")

    def choose_outdir(self):
        path = filedialog.askdirectory(title="出力先フォルダを選択")
        if path:
            self.out_dir.set(path)

    def run_convert(self):
        xml_path = Path(self.xml_path.get())
        out_dir = Path(self.out_dir.get())
        out_path = out_dir / self.out_name.get()
        if not xml_path.exists():
            messagebox.showerror(APP_TITLE, "XMLファイルを選択してください。")
            return
        try:
            self.log("変換を開始します…")
            count = convert_wxr_to_notebooklm_txt(xml_path, out_path)
            self.log(f"完了: {count} 件を {out_path} に出力しました。")
            messagebox.showinfo(APP_TITLE, f"完了しました! {count}件出力。")
        except Exception as e:
            self.log(traceback.format_exc())
            messagebox.showerror(APP_TITLE, f"エラー: {e}")

def main():
    root = tk.Tk()
    App(root)
    root.mainloop()

if __name__ == '__main__':
    main()

NotebookLMでの活用方法

変換したTXTをNotebookLMにアップロードします。

  1. NotebookLMを開く
  2. 「新しいノートブックを作成」
  3. 「+ソースを追加」→ 変換したTXTファイルをアップロード

NotebookLMが自動で内容を解析してくれるので、
ブログ全体の傾向やテーマをAI視点で把握できます。


🧠 実際に使ったプロンプト例

  • 「これらの記事の中で、最も多く扱っているテーマを教えてください」
  • 「カテゴリごとの傾向をまとめてください」
  • 「過去記事の内容から、新しく書けそうな記事案を提案してください」

NotebookLMが記事構造を理解しているので、
まるで自分専属の“AI編集者”がいるような感覚です。


まとめ:AIで記事整理がここまでラクになる

今回の流れをまとめると──

1️⃣ WordPressのエクスポート機能で .xml を取得
2️⃣ ChatGPTで作った変換アプリで .txt に変換
3️⃣ NotebookLMでAIに分析してもらう

これだけで、ブログ全体の方向性を可視化できました。
ChatGPTとNotebookLMの組み合わせは、まさにAI編集+AI分析の最強タッグです。


💬 最後にひとこと

ChatGPTと一緒に作業すると、思っている以上に“形になる”。
自分用ツールを作る体験は、ちょっとした発明のような楽しさがあります。

💡 注意事項
このアプリはChatGPTとのやり取りで作成したもので、動作保証はありません。
ご利用の際は自己責任でお願いします

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

このブログ「Re:AI Life」では、
ChatGPTを中心に、AIをちょっとだけ生活に取り入れて、毎日がちょっとラクになる方法をお届けしています。
「AIなんてわからないよ〜」という方こそ大歓迎!
同世代の仲間として、安心して読める・試せる・相談できる場所を目指しています。
どうぞよろしくお願いします😊

目次