はじめに
こんにちは、エンジニア2年目の嶋田です。
まずは、この記事を開いていただきありがとうございます!
先日以下の記事を投稿しました。
この記事を実際にどのように生成したのかを解説していきたいと思います!
前編と後編の2部構成にしました。
後編では、実際のコードを公開し、コードの内容について説明します。
前編はこちら
私が作成した記事は、完璧なものではありません。
実際のデータを使用して何か新しいものを作り出す、という基本的な流れのシェアです。
データの精度や記事の正確性に関してはまだまだ改善の余地があると思います。
学習過程の共有であると思いながら読んでいただけるとありがたいです。
もし、読者の皆さんの中で、より良い方法や改善点をお持ちの方がいれば、ぜひ教えてください🙇♂️
概要
「QiitaのAPIを利用して記事データを取得し、取得したデータを基にして新しい記事を生成すること」が今回のゴールです。この過程では、複数の技術が用いられています。requests
とbeautifulsoup4
を使ってWebスクレイピングを行い、configparser
で設定ファイルを管理します。さらに、langchain-community
とopenai
ライブラリを使用して、GPTモデルに基づいたテキスト生成を行っています。
/qiita/src/qiita.py
import requests
import csv
import configparser
import logging
logging.basicConfig(level=logging.INFO)
config = configparser.ConfigParser()
config.read('../config.ini')
access_token = config['Qiita']['access_token']
def fetch_and_save_articles(url, output_csv_file, headers):
with open(output_csv_file, mode='w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
writer.writerow(['id', 'title', 'body', 'url'])
response = requests.get(url, headers=headers)
if response.status_code != 200:
raise Exception(f"Request returned an error: {response.status_code} {response.text}")
articles = response.json()
for article in articles[:20]:
article_id = article['id']
title = article['title']
body = article['body']
url = article['url']
body = body.replace('\n', ' ').replace('\r', ' ')
writer.writerow([article_id, title, body, url])
logging.info("Processed articles")
if __name__ == '__main__':
headers = {'Authorization': f'Bearer {access_token}'}
popular_articles_url = 'https://qiita.com/api/v2/items'
popular_output_csv_file = '../data/qiita_popular_articles2.csv'
fetch_and_save_articles(popular_articles_url, popular_output_csv_file, headers)
username = 'shimada_slj'
user_articles_url = f'https://qiita.com/api/v2/users/{username}/items'
user_output_csv_file = '../data/qiita_shimada_articles2.csv'
fetch_and_save_articles(user_articles_url, user_output_csv_file, headers)
/qiita/src/langchain-qiita.py
import csv
import random
import openai
from openai import OpenAI
import configparser
import os
import logging
logging.basicConfig(level=logging.INFO)
config = configparser.ConfigParser()
config.read('../config.ini')
OPENAI_API_KEY = config['OpenAI']['api_key']
client = OpenAI(api_key=OPENAI_API_KEY)
def load_csv_data(csv_file_path):
articles = []
with open(csv_file_path, mode='r', encoding='utf-8') as file:
csv_reader = csv.DictReader(file)
for row in csv_reader:
articles.append(row)
return articles
def choose_theme_from_popular_articles(popular_articles):
titles = [article['title'] for article in popular_articles]
return random.choice(titles)
def generate_inspired_theme(articles, theme):
inspiration = "以下のテーマに基づいて新しい記事を作成してください:\n"
inspiration += f"テーマ: {theme}\n"
inspiration += "このテーマに触発された内容や書き方を踏まえて、"
inspiration += "技術初学者をターゲットにし、実践的なガイド、コードスニペットを提供し、"
inspiration += "読者がトレンドを理解し、自身のプロジェクトに活用できるようにすることを目指します。"
return inspiration
def generate_article_with_openai(theme):
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": f"{theme}\n"}
]
)
return response.choices[0].message.content
def save_article_to_txt(article, output_txt_file_path):
with open(output_txt_file_path, 'w', encoding='utf-8') as file:
file.write(article)
def load_generated_article(file_path):
with open(file_path, 'r', encoding='utf-8') as file:
return file.read()
def identify_sections(article_content):
sections = {}
current_section = None
for line in article_content.split("\n"):
if line.startswith("# "):
current_section = line[2:]
sections[current_section] = ""
elif current_section:
sections[current_section] += line + "\n"
return sections
def generate_detailed_content_for_sections(sections):
detailed_sections = {}
for title, content in sections.items():
prompt = f"以下のセクションについて詳細な記事を書いてください:\nセクション: {title}\n{content}\n詳細な内容:"
detailed_content = generate_article_with_openai(prompt)
detailed_sections[title] = detailed_content
return detailed_sections
def assemble_article(detailed_sections):
article = ""
for title, content in detailed_sections.items():
article += f"# {title}\n{content}\n\n"
return article
def generate_detailed_article_with_openai(sections):
prompt = "以下のセクションに基づいて詳細な記事を作成してください:\n"
for title, content in sections.items():
prompt += f"セクション: {title}\n概要: {content}\n\n"
prompt += "詳細な内容:"
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": prompt}
]
)
detailed_article = response.choices[0].message.content
return detailed_article
# main関数内での使用例
def main():
generated_article_path = '../generated/generated_article99.txt'
article_content = load_generated_article(generated_article_path)
sections = identify_sections(article_content)
detailed_article = generate_detailed_article_with_openai(sections)
save_article_to_txt(detailed_article, '../generated/detailed_generated_article.txt')
logging.info("詳細な記事が保存されました: ../generated/detailed_generated_article.txt")
if __name__ == '__main__':
main()
コードの解説
/qiita/src/qiita.py
の解説
qiita.py
では、Qiita APIを使って最新の人気記事や特定ユーザーの記事を取得し、それらをCSVファイルに保存しています。
-
configparser
を使用して、設定ファイルからQiitaのアクセストークンを読み込んでいます。 -
requests
ライブラリを使って、QiitaのエンドポイントにHTTP GETリクエストを送信しています。 - 応答から得られた記事データをCSV形式で保存しています。
これにより、記事生成のための生データとして使用できる情報を得ることができます。
/qiita/src/langchain-qiita.py
の解説
langchain-qiita.py
では、取得した記事データを基にしてOpenAI GPT-3.5-turboモデルを使用し、新しい記事の生成を行っています。
-
load_csv_data
関数でCSVデータを読み込み、記事のリストを取得します。 -
choose_theme_from_popular_articles
関数でランダムにテーマを選択します。 -
generate_inspired_theme
関数で、選ばれたテーマを基に記事の概要を生成します。 -
generate_article_with_openai
関数で、GPT-3.5-turboモデルを用いて詳細な記事を生成します。 -
save_article_to_txt
関数で生成した記事をテキストファイルに保存します。
これにより、AIが生成した記事の初稿を作成し、さらにその内容を拡充することができます。
記事生成プロセスの最終段階
最後に、main
関数では上記のステップをすべて組み合わせて、記事の生成から保存までのプロセスを自動化しています。
-
load_generated_article
関数で、GPT-3.5-turboによって生成された記事を読み込みます。 -
identify_sections
関数で、読み込んだ記事の中から各セクションを特定します。 -
generate_detailed_content_for_sections
関数で、各セクションに詳細な内容を追加します。 -
assemble_article
関数で、すべてのセクションを組み立てて完成記事を形成します。 - 最後に、
save_article_to_txt
関数でこの完成記事をファイルに保存し、プロセスを完了します。
最後に
以上でコードの解説を終えます。ここに示したコードは、実際に私が使用したものであり、QiitaのAPIからデータを取得して新しい記事を生成する一連のプロセスを自動化しています。
ぜひ皆さんもこのコードを実際に動かしてみて、自動生成を試してみてください!
もし、このコードや記事に関して質問や提案があれば、お気軽にコメントしてください。
最後までお付き合いいただき、ありがとうございました。