今回のチャットボットでは、表示しているウェブページの内容以外に、事前に用意したテキスト情報もリソースとして使用しますが、このテキスト情報として、他のウェブページの内容をあらかじめテキストにして持っておくようにします。
そのために、ウェブページをスクレイピングした後、中の文字列をテキストのみの情報に変換し、それをテキストデータとして格納する処理を作ります。
cors_config.jsonファイルの内容に基づき、指定されたウェブページをスクレイピング後、中の文字列をjson, txtファイルとして保存するプログラム
update_extra.py
#!/usr/bin/env python3 import os import json import requests from bs4 import BeautifulSoup from openai import OpenAI # 環境変数 OPENAI_API_KEY を必須 client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) if not client: raise ValueError("Missing OPENAI_API_KEY environment variable") BASE = os.path.dirname(__file__) CONFIG_PATH = os.path.join(BASE, 'cors_config.json') EXTRA_BASE = os.path.join(BASE, 'extra') def fetch_text_only(url): r = requests.get(url, timeout=15) r.raise_for_status() soup = BeautifulSoup(r.text, 'html.parser') # <article> を優先 target = soup.select_one('article') or soup.body text = '\n'.join( line.strip() for line in target.get_text(separator='\n').splitlines() if line.strip() ) return text def structure_content(text): # OpenAI を使ってテキストを体系化したJSONに変換 system_prompt = ( "以下の原文から、キーと値のペアで体系的に情報を抽出し、\n" "JSONオブジェクトとして出力してください。\n" "見出しやセクション名をキーに、対応する内容を値としてください。" ) user_prompt = text[:3000] # 長すぎる場合は先頭3000文字まで resp = client.chat.completions.create( model='gpt-4o-mini', messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ], temperature=0.0, max_tokens=800 ) # レスポンスからJSONを抽出 content = resp.choices[0].message.content.strip() try: data = json.loads(content) except json.JSONDecodeError: # JSON パース失敗時は生テキストを返す return None, content return data, None def main(): # 設定読み込み with open(CONFIG_PATH, encoding='utf-8') as f: cfg = json.load(f) for site in cfg.get('sites', []): token = site['token'] extra_dir = site.get('extra_dir') files = site.get('extra_files', []) if not extra_dir or not files: continue target_dir = os.path.join(EXTRA_BASE, extra_dir) os.makedirs(target_dir, exist_ok=True) for ef in files: fname = ef.get('filename') url = ef.get('url') if not fname or not url: continue try: print(f"[{token}] Fetching {url} → {fname}") text = fetch_text_only(url) # 本文テキストをそのまま保存 text_path = os.path.join(target_dir, fname) with open(text_path, 'w', encoding='utf-8') as fw: fw.write(text) # 体系化 JSON 生成 json_data, raw = structure_content(text) json_fname = fname.replace('.txt', '.json') json_path = os.path.join(target_dir, json_fname) with open(json_path, 'w', encoding='utf-8') as fj: if json_data: json.dump(json_data, fj, ensure_ascii=False, indent=2) else: # パースできなかった場合、生テキストを comments フィールドに json.dump({"unstructured": raw}, fj, ensure_ascii=False, indent=2) except Exception as e: print(f"[{token}] ERROR fetching {url}: {e}") if __name__ == '__main__': main()
次にこのpythonファイルを実行するためのモジュールをインストールします。
sudo apt update sudo apt install -y python3-venv python3-full cd /var/www/chatbot python3 -m venv venv source venv/bin/activate pip install beautifulsoup4 requests deactivate
これでupdate_extra.pyが実行できるようになりました。
次にこのupdate_extra.pyがサービス再起動のたびに起動するようにします。
定期的にウェブページの内容が書き換わることを想定し、サービス再起動のたびに生成しなおすようにするためです。
(CRONに設定し定期的に実行する形でも構いませんし、一回実行したらそれで良いという方はご自身で一度実行するだけでOKです。その場合はここから以下の設定は不要です。)
sudo nano /etc/systemd/system/chatbot.service
1行追加します。
[Unit] Description=Chatbot Service After=network.target [Service] User=www-data WorkingDirectory=/var/www/chatbot ExecStartPre=/var/www/chatbot/venv/bin/python /var/www/chatbot/update_extra.py <-ここ追加 ExecStart=/var/www/chatbot/venv/bin/gunicorn --chdir /var/www/chatbot --bind unix:/var/www/chatbot/chatbot.sock chat:app Restart=always [Install] WantedBy=multi-user.target EOF
最後にsystemdに設定を反映してサービスを再起動
sudo systemctl daemon-reload sudo systemctl restart chatbot sudo systemctl status chatbot
するとupdate_extra.pyが実行され、しばらくするとcors_config.jsonに記載したextraディレクトリにjson, txtファイルが生成されます。
中身を確認して、取得したウェブページの内容だけが書かれていれば無事完了です。
※ なお、ここでは自動でテキストファイルを生成しましたが、べつに自動で生成しなければいけないわけではありません。ご自身で手動でテキストファイルを作っておく形でも構いません。