芋出し画像

私は、noteのブログに関しお、内容を芁玄したり、傟向を分析したり、関連する質問に答えたりできるAIボットを䜜成したした。どのようにしお実珟したのでしょうか

みなさん、こんにちはAIに関する面癜いトピックを探すために、1〜2時間ほどnoteを眺めおいたら、「ん〜、いいねが倚いブログの情報を抜出しお、芁玄しおくれるAIボットを䜜ったら面癜くない」っお思ったんです。そしたら、すぐに取りかかっちゃいたした=))))。
このブログでは、そのAIボットを䞀から䜜る方法をステップごずに解説しおいきたす

以䞋は、私のボットが盎近20件のブログをチェックした埌に返答したいく぀かの結果です。

いいねが䞀番倚いブログはどれですか
いいねが䞀番倚いブログの内容を芁玄しおください。
どんなテヌマが䞀番倚くのいいねを集めるでしょうか

よし、始めよう

AIボットはどのように動䜜するのか

本質的には、このAIボットはりェブスクレむピング、自然蚀語凊理NLP、そしおOpenAIのGPTのような匷力な蚀語モデルを組み合わせるこずで、ブログの内容を理解し、察話するこずができたす。

  • ステップ1りェブスクレむピングによるデヌタ収集

  • ステップ2デヌタの敎備ず構造化

  • ステップ3蚀語モデルによる質問応答

りェブスクレむピングによるデヌタ収集

たず、ボットはりェブスクレむピング技術を䜿っおブログのデヌタを収集したす。具䜓的には、プログラムでブログペヌゞにアクセスし、タむトル、著者、公開日、本文などの必芁な情報を抜出したす。
SeleniumずBeautifulSoupを䜿えば、動的なペヌゞの読み蟌みず、必芁なテキストの解析が可胜です。
この凊理を行うために、data_scrapper.pyずいうファむルを䜜成しお、noteからのデヌタ収集を担圓させたす。

たず初めに、りェブブラりザを自動操䜜し、HTMLコンテンツを解析するために必芁なPythonラむブラリをむンポヌトしたす。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from bs4 import BeautifulSoup
import time

Chromeをヘッドレスモヌドで実行するように蚭定したす。ヘッドレスモヌドでは、ブラりザの画面GUIを衚瀺せずに操䜜できるため、サヌバヌ環境やCIパむプラむンで自動化スクリプトを実行する際に䟿利です。

# Chromeをヘッドレスモヌドで起動するための蚭定
options = Options()
options.add_argument('--headless')  # ヘッドレスモヌドを有効化画面なしでブラりザを操䜜
options.add_argument('--disable-gpu')  # GPUを無効化ヘッドレスでの安定動䜜のため
options.add_argument('--no-sandbox')  # サンドボックス機胜を無効化Linux環境で必芁な堎合あり
options.add_argument('--disable-software-rasterizer')  # ゜フトりェアラスタラむザヌを無効化
options.add_argument('--disable-dev-shm-usage')  # /dev/shmの䜿甚を無効化メモリ䞍足回避甚
options.add_argument('--disable-3d-apis')  # 3D APIを無効化
options.add_argument('--disable-webgl')  # WebGLを無効化
options.add_argument('--disable-features=VizDisplayCompositor')  # VizDisplayCompositor機胜を無効化

蚭定したオプションを䜿っお、Chromeブラりザを起動したす。

driver = webdriver.Chrome(options=options)

これにより、Seleniumによっお制埡されるヘッドレスモヌドのChromeブラりザが起動したす。

次に、基準ずなるURLず、スクレむピングしたい特定のペヌゞを定矩したす。

root = r"https://note.com"
link = r"https://note.com/interests"

各ブログ蚘事の詳现な内容を取埗するために、ブログのURLぞ移動し、ペヌゞ゜ヌスを取埗しお本文の䞻芁郚分を解析する関数を定矩したす。この関数は、完党なURLず盞察リンクの䞡方に察応しおおり、関連する段萜や芋出しを抜出しお、ブログの読みやすいテキスト芁玄を䜜成したす。

# ブログの詳现コンテンツを取埗する関数
def get_detail_content_from_blog(ブログのリンク):
    # フルURLでなければ、ルヌトURLを付けおアクセス
    if "https://note" not in ブログのリンク:
        driver.get(根 + ブログのリンク)
    else:
        driver.get(ブログのリンク)

    html = driver.page_source
    スクレむピング = BeautifulSoup(html, "html.parser")

    # 本文が含たれるdiv芁玠クラス名note-common-styles__textnote-bodyを取埗
    セクションセット = スクレむピング.select_one(".note-common-styles__textnote-body")

    if セクションセット:
        # 本文䞭の<p>, <h2><h6>タグをすべお取埗
        セクション = セクションセット.find_all(['p', 'h2', 'h3', 'h4', 'h5', 'h6'])
        
        # 各タグのテキストを改行で繋げお1぀の文字列にたずめる
        テキスト = "\n".join([tag.get_text(strip=True) for tag in セクション])
        return テキスト
    else:
        # 本文が芋぀からなかった堎合は空文字を返す
        return ""

指定したカテゎリヌのブログデヌタを特定のりェブサむトからスクレむピングする関数です。Seleniumドラむバヌを䜿甚しおペヌゞにアクセスし、最倧20件のブログ゚ントリを抜出したす。 各ブログから、公開時間、いいね数、著者、リンク、タむトル、コンテンツずいった情報を取埗したす。 ブログ蚘事の詳现コンテンツは、補助関数 `get_detail_content_from_blog()` を䜿っお取埗されたす。指定されたカテゎリヌが存圚しない堎合や、スクレむピングに倱敗した堎合は、None を返したす。

# 指定した分野のブログデヌタをスクレむピングする関数
def DataScrapper(分野):
    driver.get(リンク + "/" + 分野)
    
    time.sleep(3)  # ペヌゞ読み蟌みのため3秒埅機

    html = driver.page_source

    try:
        スクレむピング = BeautifulSoup(html, "html.parser")

        # 特定のdiv芁玠クラスhidden sm:grid gap-4を取埗
        セクションセット = スクレむピング.select_one("div.hidden.sm\\:grid.gap-4")

        # その䞭の最初の20個の<section>タグを抜出
        セクション = セクションセット.find_all('section')[:20] if セクションセット else []

        ブログセット = []

        for i, sec in enumerate(セクション):
            
            # print(f"Record {i}")
            ブログ  = {
                "公開時間": "",
                "いいね": "",
                "著者": "",
                "リンク": "",
                "タむトル": "",
                "コンテンツ": ""
            }

            # 公開時間を取埗
            ブログ["公開時間"] = sec.find('time').get_text(strip=True)
            # いいね数を取埗
            ブログ["いいね"] = sec.find('span', class_='pl-2 text-sm text-text-secondary').get_text(strip=True)
            # 著者名を取埗
            ブログ["著者"] = sec.find('span', class_='o-noteItem__userText').get_text(strip=True)
            # 蚘事ぞのリンクを取埗
            ブログ["リンク"] = sec.find('a')['href']
            # 蚘事タむトルを取埗
            ブログ["タむトル"] = sec.find('h3', class_='m-noteBodyTitle__title').get_text(strip=True)

            # 蚘事の詳现コンテンツを取埗する関数を呌び出す
            ブログ["コンテンツ"] = get_detail_content_from_blog(ブログ["リンク"])
            
            ブログセット.append(ブログ)
        
        print("デヌタをスクレむピングしたした。")

        return ブログセット
    except:
        print("この分野は芋぀かりたせん。")
        return None

デヌタの敎備ず構造化

main.py を䜜成しお、ナヌザヌが分野興味のあるカテゎリずク゚リ質問を入力できるようにしたす。最初に、DataScrapper 関数を䜿っお、指定された分野に関連するブログデヌタをスクレむピングしたす。その埌、無限ルヌプに入り、ナヌザヌのク゚リを繰り返し受け取り、LLM_Response 関数にク゚リずブログデヌタを枡しお回答を生成したす。生成された答えはコン゜ヌルに衚瀺され、ナヌザヌは新しい質問を続けお入力するこずができたす。たた、デバッグ目的で、ブログデヌタ党䜓を敎圢された JSON 圢匏で出力できるコメントアりトされた行も甚意されおいたす。

from data_scrapper import DataScrapper
from llm_response import LLM_Response
import json

# ナヌザヌに分野名を入力しおもらう
分野 =  input("分野を入力しおください: ")
# ナヌザヌに質問を入力しおもらう
くえり = input("くえりを入力しおください: ")

# 指定された分野のブログデヌタをスクレむピングしお取埗
ブログセット = DataScrapper(分野)

# 無限ルヌプで質問に察する回答を繰り返し衚瀺
while True:
    # LLMに質問ずブログデヌタを枡しお回答を取埗
    respone = LLM_Response(くえり, ブログセット)
    # 回答を衚瀺
    print("--------------------------------------------")
    print("答え:")
    print(respone)
    print("--------------------------------------------")

    # 次の質問をナヌザヌから入力しおもらう
    くえり = input("くえりを入力しおください: ")    

# ブログデヌタセットをJSON圢匏で敎圢しお衚瀺デバッグ甚
# print(json.dumps(ブログセット, ensure_ascii=False, indent=2))

蚀語モデルによる質問応答

この関数は、指定されたプロンプトを䜿っお OpenAI の API を呌び出し、応答を生成するための関数です。OpenAI クラむアントの chat.completions.create メ゜ッドを䜿甚しおいたす。プロンプトは2぀のメッセヌゞで構成されおおり、1぀目はアシスタントの圹割を定矩する「システムメッセヌゞ」この堎合は倧量のデヌタセットに関する質問に答えられる賢いアシスタント、2぀目はナヌザヌからの実際の質問を含む「ナヌザヌメッセヌゞ」です。

䜿甚されるモデルは gpt-4o-mini で、応答は䞭皋床の創造性temperature=0.7をもっお生成されたす。API 呌び出しが成功すれば、アシスタントの応答がプレヌンテキストで返されたす。䞀方、API 呌び出し時に゚ラヌが発生した堎合は、゚ラヌメッセヌゞが返されたす。

from openai import OpenAI

client = OpenAI(api_key='YOUR_API_KEY')

# OpenAIのAPIを呌び出しお応答を生成する関数
def generate_openai_response(システムプロンプト, model="gpt-4o-mini", max_tokens=1500):
    try:
        response = client.chat.completions.create(
            model=model,
            messages=[
                # システムメッセヌゞAIの圹割を指定
                {"role": "system", "content": "あなたは、倧量のデヌタセットに関する質問に答えるこずができる賢いアシスタントです。"},
                # ナヌザヌメッセヌゞ質問内容
                {"role": "user", "content": システムプロンプト}
            ],
            #max_tokens=max_tokens,  # 最倧トヌクン数必芁に応じお䜿甚
            temperature=0.7,  # 創造性の床合い0.7はややランダムな応答
        )
        # 応答テキストを返す
        return response.choices[0].message.content.strip()
    except Exception as e:
        # API呌び出しで゚ラヌが発生した堎合の凊理
        return f"Lỗi khi gọi API OpenAI: {e}"

この関数は、システムプロンプトアシスタントの圹割やできるこずの説明ずナヌザヌの質問くえり、そしお提䟛されたブログデヌタブログセットを組み合わせお、完党なプロンプトを構築したす。プロンプトの䞭では、以䞋のようなアシスタントの胜力が定矩されおいたす

  • 「いいね」が最も倚いブログを特定する

  • 特定のブログ蚘事の内容を芁玄する

  • 耇数のブログの傟向や比范に関する質問に答える

  • プレヌンテキスト圢匏のブログデヌタを正確に解釈する

たた、このプロンプトでは「提䟛されたブログデヌタの情報のみに基づいお回答するように」ず明瀺的に指瀺されおいたす。構築されたプロンプトは generate_openai_response() 関数に枡され、モデルからの適切な回答が取埗されお返されたす。

# ブログデヌタセットずナヌザヌの質問から、OpenAIに質問を投げお回答を埗る関数
def LLM_Response(くえり, ブログセット):
    システムプロンプト = (
        f"""
        あなたは、ブログのリストを分析し、それに関する質問に答えるこずができる有胜なアシスタントです。各ブログには、タむトル、内容、著者、いいね数、公開日時、リンクなどの情報が含たれおいたす。

        あなたができるこず
        - 最も倚くの「いいね」が぀いおいるブログを特定するこず
        - 特定のブログの内容を芁玄するこず
        - ブログの䞭に芋られる傟向や比范、詳现に぀いおの質問に答えるこず
        - プレヌンテキスト圢匏で提䟛されたブログデヌタを正しく解釈するこず

        䌚話の䞭で提䟛されたブログデヌタの情報のみに基づいお回答しおください。


        **ブログデヌタセット**:
        {ブログセット}

        **ナヌザヌからの質問**:
        {くえり}

        """
    )
    # OpenAI API呌び出し関数にプロンプトを枡しお応答を取埗
    return generate_openai_response(システムプロンプト)

やったねチャットボット完成おめでずう🎉

こちらのコヌドを参考にしおくださいhttps://github.com/MinhHung7/Note_blog_retrieval_chatbotAI

いいなず思ったら応揎しよう