見出し画像

chatGPT-4に「ウェブスクレイピング→情報をPDF保存の自動化ツール開発」で作ってと頼んだら・・・コード1行も書かずに出来た😅

ある所で、「ウェブスクレイピング→情報をPDF保存の自動化ツール開発」は案件定義で3日かかりますよと書いてありました。
chatGPT-4使ったらどうなるんだろうと思ってやってみました。
結論をいうと「余裕で案件定義もプログラムもコードを一行も書かずに出来ました
以下、「Q.」が質問(プロンプト)です。「A.」がchatGPT-4からの回答です。
プロンプタ(魔術師)とchatGPTとのリアルなやり取りです。長いですよ😅



Q.あなたはプログラマです。ウェブスクレイピング→情報をPDF保存の自動化ツール開発はどのような案件がひつようでしょうか?
A.ウェブスクレイピングと情報をPDF保存の自動化ツールを開発するには、以下の要件が必要です。

  1. プロジェクトの目的と範囲の明確化: 開発するツールがどのようなウェブサイトから情報を取得し、どのような形式でPDFに保存するかを決定します。

  2. 使用するプログラミング言語とライブラリ: Python, JavaScript, Rubyなど、ウェブスクレイピングとPDF生成に適した言語を選び、必要なライブラリやモジュールを選定します。

  3. ウェブスクレイピングの方法: BeautifulSoup, Scrapy, Seleniumなどのライブラリを使用して、ウェブサイトから情報を取得します。

  4. データの解析と整形: 取得したデータを解析し、必要な情報だけを抽出して整形します。

  5. PDF生成: 抽出したデータをPDF形式に変換するために、ReportLab, FPDF, WeasyPrintなどのライブラリを使用します。

  6. 自動化の実装: スケジュールやトリガーに基づいてプロセスを自動化するために、cronやWindowsタスクスケジューラー、AWS Lambdaなどのツールを利用します。

  7. エラーハンドリングと通知: プロセス中に発生する可能性のあるエラーを検出し、適切に処理する仕組みを実装します。また、エラー発生時に通知が届くように設定します。

  8. テストとデバッグ: 開発したツールが正確に機能することを確認するために、テストとデバッグを行います。

  9. ドキュメントと使い方の説明: ツールの使い方や設定方法、カスタマイズ方法を明確に説明するドキュメントを作成します。

これらの要件を満たすことで、ウェブスクレイピングから情報をPDF形式で保存する自動化ツールの開発を行うことができます。
Q.上記をユースケースでPlantUMLで表現して頂けますか

A.PlantUMLを使って、ウェブスクレイピングから情報をPDF形式で保存する自動化ツールのユースケース図を以下に示します。

@startuml
!define AWSPUML https://raw.githubusercontent.com/awslabs/aws-icons-for-plantuml/v14.0/Legacy/
!includeurl AWSPUML/AWSCommon.puml

actor User

package "Web Scraping Automation Tool" {
    usecase "Define project objectives and scope"
    usecase "Select programming language and libraries"
    usecase "Implement web scraping methods"
    usecase "Analyze and format data"
    usecase "Generate PDF"
    usecase "Implement automation"
    usecase "Handle errors and notifications"
    usecase "Test and debug"
    usecase "Create documentation and user guide"
}

User --> "Define project objectives and scope"
User --> "Select programming language and libraries"
User --> "Implement web scraping methods"
User --> "Analyze and format data"
User --> "Generate PDF"
User --> "Implement automation"
User --> "Handle errors and notifications"
User --> "Test and debug"
User --> "Create documentation and user guide"

@enduml

この図は、ユーザーとウェブスクレイピング自動化ツールの開発プロセスを示しています。ユーザーは各ユースケースを実行して、ツールの開発を進めます。

Q.上記をシーケンス図でPlantUMLで出力して頂けますか

A.PlantUMLを使って、ウェブスクレイピングから情報をPDF形式で保存する自動化ツールのシーケンス図を以下に示します。

@startuml
actor User
participant "Project\nScope" as PS
participant "Programming\nLanguage & Libraries" as PLL
participant "Web Scraping\nMethods" as WSM
participant "Data\nAnalysis & Formatting" as DAF
participant "PDF\nGeneration" as PDFG
participant "Automation\nImplementation" as AI
participant "Error Handling\n& Notifications" as EHN
participant "Testing\n& Debugging" as TD
participant "Documentation\n& User Guide" as DUG

User -> PS : Define project objectives and scope
PS --> User : Scope defined

User -> PLL : Select programming language and libraries
PLL --> User : Language & libraries selected

User -> WSM : Implement web scraping methods
WSM --> User : Web scraping methods implemented

User -> DAF : Analyze and format data
DAF --> User : Data analyzed & formatted

User -> PDFG : Generate PDF
PDFG --> User : PDF generated

User -> AI : Implement automation
AI --> User : Automation implemented

User -> EHN : Handle errors and notifications
EHN --> User : Errors handled & notifications set

User -> TD : Test and debug
TD --> User : Tool tested & debugged

User -> DUG : Create documentation and user guide
DUG --> User : Documentation & user guide created

@enduml

このシーケンス図は、ユーザーがウェブスクレイピング自動化ツールの開発プロセスを実行する順序を示しています。各ステップが完了すると、次のステップへ進みます。

Q.上記を元にクラス図をPlantUMLで記述頂けますか

A,ウェブスクレイピングから情報をPDF形式で保存する自動化ツールのクラス図を、PlantUMLを使って以下に示します。

@startuml

class Scraper {
    +url: String
    +scraping_library: String
    +scrape(): List[Data]
}

class DataExtractor {
    +data: List[Data]
    +analyze(): List[Data]
    +format(): List[FormattedData]
}

class PDFGenerator {
    +formatted_data: List[FormattedData]
    +pdf_library: String
    +generate(): PDF
}

class Automation {
    +schedule: String
    +trigger: String
    +run(): void
}

class ErrorHandler {
    +handle_error(): void
    +send_notification(): void
}

class Tester {
    +test(): void
    +debug(): void
}

class Documentation {
    +create_user_guide(): void
    +create_technical_documentation(): void
}

Scraper --> DataExtractor : provides data
DataExtractor --> PDFGenerator : provides formatted data
PDFGenerator --> Automation : provides PDF
Automation ..> ErrorHandler : handles errors
Tester ..> Scraper : tests & debugs
Tester ..> DataExtractor : tests & debugs
Tester ..> PDFGenerator : tests & debugs
Tester ..> Automation : tests & debugs
Tester ..> ErrorHandler : tests & debugs
Documentation ..> Scraper : documents
Documentation ..> DataExtractor : documents
Documentation ..> PDFGenerator : documents
Documentation ..> Automation : documents
Documentation ..> ErrorHandler : documents

@enduml

このクラス図は、ウェブスクレイピング自動化ツールの開発に関連する主要なクラスとその相互作用を示しています。各クラスは、関連する機能をカプセル化し、他のクラスと協力してツールの全体的な機能を実現します。


Q.あなたはプログラマです。上記のクラス図を元にPythonで実装をお願い致します。入力としてウインドウに①スクレイピング先のURLと②この文字を含む行を取り出す③出力先ファイル名の入力欄とスクレイピングの「スタート」「ストップ」のボタンをつけてください。

A,以下の実装は、指定された要件を満たすためにPythonとTkinterを使用しています。ウェブスクレイピングにはBeautifulSoupを使用し、PDFの生成にはReportLabを使用しています。

まず、必要なライブラリをインストールします:

pip install beautifulsoup4
pip install requests
pip install reportlab
pip install lxml

次に、以下のコードを実行してください:

import tkinter as tk
from tkinter import ttk
from bs4 import BeautifulSoup
import requests
import re
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas

class Scraper:
    def __init__(self, url, keyword):
        self.url = url
        self.keyword = keyword

    def scrape(self):
        response = requests.get(self.url)
        soup = BeautifulSoup(response.content, "lxml")
        text = soup.get_text()
        lines = text.split("\n")
        filtered_lines = [line for line in lines if self.keyword in line]
        return filtered_lines

class PDFGenerator:
    def __init__(self, data, filename):
        self.data = data
        self.filename = filename

    def generate(self):
        pdf = canvas.Canvas(self.filename, pagesize=letter)
        text_object = pdf.beginText(50, 750)
        for line in self.data:
            text_object.textLine(line)
        pdf.drawText(text_object)
        pdf.save()

class App:
    def __init__(self, root):
        self.root = root
        self.root.title("Web Scraping Tool")

        self.url_label = ttk.Label(root, text="URL:")
        self.url_label.grid(column=0, row=0)

        self.url_entry = ttk.Entry(root, width=40)
        self.url_entry.grid(column=1, row=0)

        self.keyword_label = ttk.Label(root, text="Keyword:")
        self.keyword_label.grid(column=0, row=1)

        self.keyword_entry = ttk.Entry(root, width=40)
        self.keyword_entry.grid(column=1, row=1)

        self.filename_label = ttk.Label(root, text="Output Filename:")
        self.filename_label.grid(column=0, row=2)

        self.filename_entry = ttk.Entry(root, width=40)
        self.filename_entry.grid(column=1, row=2)

        self.start_button = ttk.Button(root, text="Start Scraping", command=self.start_scraping)
        self.start_button.grid(column=0, row=3)

        self.stop_button = ttk.Button(root, text="Stop Scraping", command=self.stop_scraping)
        self.stop_button.grid(column=1, row=3)

    def start_scraping(self):
        url = self.url_entry.get()
        keyword = self.keyword_entry.get()
        scraper = Scraper(url, keyword)
        data = scraper.scrape()

        filename = self.filename_entry.get()
        if not filename.endswith(".pdf"):
            filename += ".pdf"
        pdf_generator = PDFGenerator(data, filename)
        pdf_generator.generate()
        print("Scraping completed and PDF generated.")

    def stop_scraping(self):
        self.root.quit()

if __name__ == "__main__":
    root = tk.Tk()
    app = App(root)
    root.mainloop()

このコードは、Tkinterを使用してGUIを作成し、指定されたウェブページからキーワードを含む行を抽出し、それらの行をPDFに保存します。スタートボタンを押すと、スクレイピングが開始され、指定されたキーワードを含む行がPDFに保存されます。ストップボタンを押すと、アプリケーションが終了します。以下は、以前の説明の続きです。

このコードは、Tkinterを使用してGUIを作成し、指定されたウェブページからキーワードを含む行を抽出し、それらの行をPDFに保存します。スタートボタンを押すと、スクレイピングが開始され、指定されたキーワードを含む行がPDFに保存されます。ストップボタンを押すと、アプリケーションが終了します。

この実装では、ウェブスクレイピング、データの抽出と整形、PDFの生成、およびTkinterを使用したGUIの作成が行われています。ただし、この実装では、エラーハンドリング、通知、自動化、テスト、デバッグ、およびドキュメント作成に関する機能は含まれていません。これらの機能は、実際のプロジェクトに応じて追加することができます。

このコードは、指定された要件を満たすシンプルな実装の例です。実際のアプリケーションでは、さらなるエラーハンドリング、機能の追加、およびカスタマイズが必要になる場合があります。また、実際のウェブサイトでのスクレイピングは、ウェブサイトの所有者の許可が必要であり、適切なアクセス制御とリソースの使用に注意する必要があります。

Q.PDFファイルは出来ましたが中には何も入っていませんでした

A.申し訳ありません。この問題は、BeautifulSoupによるウェブページのテキストの取得が適切でないために発生する可能性があります。以下のコードは、Scraperクラスのscrape()メソッドを変更して、テキストの抽出を改善しました。また、start_scraping()メソッドを変更して、データが空の場合に警告メッセージを表示します。

class Scraper:
    def __init__(self, url, keyword):
        self.url = url
        self.keyword = keyword

    def scrape(self):
        response = requests.get(self.url)
        soup = BeautifulSoup(response.content, "lxml")
        paragraphs = soup.find_all("p")
        text = " ".join([p.get_text() for p in paragraphs])
        lines = text.split("\n")
        filtered_lines = [line for line in lines if self.keyword in line]
        return filtered_lines

class App:
    def __init__(self, root):
        # ... (GUI部分のコードは変更なし)

    def start_scraping(self):
        url = self.url_entry.get()
        keyword = self.keyword_entry.get()
        scraper = Scraper(url, keyword)
        data = scraper.scrape()

        if not data:
            print("No data found containing the specified keyword.")
            return

        filename = self.filename_entry.get()
        if not filename.endswith(".pdf"):
            filename += ".pdf"
        pdf_generator = PDFGenerator(data, filename)
        pdf_generator.generate()
        print("Scraping completed and PDF generated.")

    # ... (他のメソッドは変更なし)

この変更により、ウェブページから段落(<p>タグ)のテキストが抽出され、キーワードを含む行が適切にPDFに書き込まれるはずです。ただし、ウェブページの構造によっては、テキストが他のタグに格納されている場合があります。その場合は、scrape()メソッドをさらにカスタマイズして、適切なタグを対象にテキストを抽出する必要があります。

Q.コピー間違いするといけませんので、省略無しで全てのコードを書いて頂いても宜しいでしょうか

A.もちろんです。以下に、省略なしで全てのコードを示します。

import tkinter as tk
from tkinter import ttk
from bs4 import BeautifulSoup
import requests
import re
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas

class Scraper:
    def __init__(self, url, keyword):
        self.url = url
        self.keyword = keyword

    def scrape(self):
        response = requests.get(self.url)
        soup = BeautifulSoup(response.content, "lxml")
        paragraphs = soup.find_all("p")
        text = " ".join([p.get_text() for p in paragraphs])
        lines = text.split("\n")
        filtered_lines = [line for line in lines if self.keyword in line]
        return filtered_lines

class PDFGenerator:
    def __init__(self, data, filename):
        self.data = data
        self.filename = filename

    def generate(self):
        pdf = canvas.Canvas(self.filename, pagesize=letter)
        text_object = pdf.beginText(50, 750)
        for line in self.data:
            text_object.textLine(line)
        pdf.drawText(text_object)
        pdf.save()

class App:
    def __init__(self, root):
        self.root = root
        self.root.title("Web Scraping Tool")

        self.url_label = ttk.Label(root, text="URL:")
        self.url_label.grid(column=0, row=0)

        self.url_entry = ttk.Entry(root, width=40)
        self.url_entry.grid(column=1, row=0)

        self.keyword_label = ttk.Label(root, text="Keyword:")
        self.keyword_label.grid(column=0, row=1)

        self.keyword_entry = ttk.Entry(root, width=40)
        self.keyword_entry.grid(column=1, row=1)

        self.filename_label = ttk.Label(root, text="Output Filename:")
        self.filename_label.grid(column=0, row=2)

        self.filename_entry = ttk.Entry(root, width=40)
        self.filename_entry.grid(column=1, row=2)

        self.start_button = ttk.Button(root, text="Start Scraping", command=self.start_scraping)
        self.start_button.grid(column=0, row=3)

        self.stop_button = ttk.Button(root, text="Stop Scraping", command=self.stop_scraping)
        self.stop_button.grid(column=1, row=3)

    def start_scraping(self):
        url = self.url_entry.get()
        keyword = self.keyword_entry.get()
        scraper = Scraper(url, keyword)
        data = scraper.scrape()

        if not data:
            print("No data found containing the specified keyword.")
            return

        filename = self.filename_entry.get()
        if not filename.endswith(".pdf"):
            filename += ".pdf"
        pdf_generator = PDFGenerator(data, filename)
        pdf_generator.generate()
        print("Scraping completed and PDF generated.")

    def stop_scraping(self):
        self.root.quit()

if __name__ == "__main__":
    root = tk.Tk()
    app = App(root)
    root.mainloop()

このコードは、Tkinterを使用してGUIを作成し、指定されたウェブページからキーワードを含む段落(<p>タグ)のテキストを抽出し、それらの行をPDFに保存します。スタートボタンを押すと、スクレイピングが開始され、指定されたキーワードを含む行がPDFに保存されます。ストップボタンを押すと、アプリケーションが終了します。このコードは、指定された要件を満たすシンプルな実装の例です。実際のアプリケーションでは、さらなるエラーハンドリング、機能の追加、およびカスタマイズが必要になる場合があります。また、実際のウェブサイトでのスクレイピングは、ウェブサイトの所有者の許可が必要であり、適切なアクセス制御とリソースの使用に注意する必要があります。

この実装では、ウェブページから<p>タグで囲まれた段落のテキストを抽出していますが、ウェブページの構造によっては、テキストが他のタグに格納されている場合があります。その場合は、scrape()メソッドをさらにカスタマイズして、適切なタグを対象にテキストを抽出する必要があります。

また、現在の実装では、ページ内のすべてのテキストを一度に抽出し、キーワードを含む行をフィルタリングしています。これにより、一部のウェブページで期待される結果が得られない場合があります。この問題を解決するために、さまざまなタグやセクションに対応するように、抽出ロジックを調整することができます。

最後に、このアプリケーションは、基本的なウェブスクレイピングとPDF生成の機能を提供していますが、実際のプロジェクトでは、より高度な機能やエラーハンドリングが必要になる場合があります。例えば、プロキシの使用、ユーザーエージェントの変更、リクエストの遅延、リトライロジックの追加、またはページ内の特定のセクションや要素に対するアクセス制御などの機能を実装することができます。これらの機能は、ウェブサイトの所有者との協力や、適切な利用規約や法律を遵守することを確認しながら実装することが重要です。

Q.pdfに文字はでましたが以下のように文字化けしました。 ■■■■■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■2■■■■■■■■3■■■■■■■■■■■■■■■■■■■■ 4/9■■■■■■■■■■3■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ blog.hatenablog.com■■■■■■■■… ■■■■■■■■■■■■■■■■■■■■■■■■■4■2■■■■■■4■8■■■■■2023■4■■2■■■■■■30■■*1■ # ■■■■■■■■■■■■■■ 1 ■■■■■■■■■■■■■ - #■■■■■ by id:shimobayashi 2 ■■■■■■■■■■■■■■■■■■■■■■ - Letter from Kyoto by id:kkzy9 3 ■■■■■■■■■■■■■■■■■■■■■■■■ - ■■■■■ by id:kamiyakenkyujo 4 ■■■■■■■■■… ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■2023■3■17■■■2023■3■24■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■2023■3■17■■■202… ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■3■■■■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■ ■■■■■■■… ■■■■■BloggerJIN■■■ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ■■■■■■■■■■ ■■■■■■■■■■■■■■■■■ ■■2022■■■■■■■■■■■■■■■■■■■■■… ■■■■■■■■■■■■2023■■■■■■ ■■■■■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■■■THE ■■■■■■■■■■■■■ www.youtube.com ■■■■ ■■■■ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■… ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

A.文字化けが発生している理由は、ウェブページに含まれる日本語文字が、PDFファイル生成時に正しくエンコードされていないことが原因です。この問題を解決するには、ReportLab ライブラリを使用して PDF に日本語フォントを埋め込む必要があります。

以下に、日本語フォントをサポートするように修正した PDFGenerator クラスと全体のコードを示します。

まず、IPAフォント(ipag.ttf)をダウンロードし、Pythonスクリプトと同じフォルダに配置してください。IPAフォントは以下のリンクからダウンロードできます。
https://ipafont.ipa.go.jp/node17#jp

次に、コードを以下のように修正してください。

from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont

class PDFGenerator:
    def __init__(self, data, filename):
        self.data = data
        self.filename = filename

    def generate(self):
        pdf = canvas.Canvas(self.filename, pagesize=letter)
        pdfmetrics.registerFont(TTFont("IPA", "ipag.ttf"))
        pdf.setFont("IPA", 14)
        text_object = pdf.beginText(50, 750)
        for line in self.data:
            text_object.textLine(line)
        pdf.drawText(text_object)
        pdf.save()

上記の修正では、ipag.ttfというIPAフォントをPDFに埋め込むようにしています。この修正により、PDFファイル内の日本語文字が正しく表示されるはずです。

なお、この修正は、IPAフォントを利用することを前提としています。他の日本語フォントを使用する場合は、適切なフォントファイルをダウンロードし、TTFontクラスの初期化とsetFontメソッドでフォント名を変更してください。

Q.全てのソースを表示して頂けますでしょうか

A.もちろんです。以下に、全てのソースコードを示します。

import tkinter as tk
from tkinter import ttk
import requests
from bs4 import BeautifulSoup
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
class WebScraper:
    def __init__(self, url, keyword):
        self.url = url
        self.keyword = keyword

    def scrape(self):
        response = requests.get(self.url)
        soup = BeautifulSoup(response.text, "html.parser")
        paragraphs = soup.find_all("p")
        lines = [p.get_text() for p in paragraphs if self.keyword in p.get_text()]
        return lines
class PDFGenerator:
    def __init__(self, data, filename):
        self.data = data
        self.filename = filename

    def generate(self):
        pdf = canvas.Canvas(self.filename, pagesize=letter)
        pdfmetrics.registerFont(TTFont("IPA", "ipag.ttf"))
        pdf.setFont("IPA", 14)
        text_object = pdf.beginText(50, 750)
        for line in self.data:
            text_object.textLine(line)
        pdf.drawText(text_object)
        pdf.save()
def start_scraping():
    url = url_entry.get()
    keyword = keyword_entry.get()
    output_file = output_entry.get()
    scraper = WebScraper(url, keyword)
    lines = scraper.scrape()
    pdf_generator = PDFGenerator(lines, output_file)
    pdf_generator.generate()

root = tk.Tk()
root.title("Web Scraper")

frame = ttk.Frame(root, padding="10")
frame.grid()

url_label = ttk.Label(frame, text="URL:")
url_label.grid(column=0, row=0, sticky=tk.W)
url_entry = ttk.Entry(frame, width=50)
url_entry.grid(column=1, row=0)

keyword_label = ttk.Label(frame, text="Keyword:")
keyword_label.grid(column=0, row=1, sticky=tk.W)
keyword_entry = ttk.Entry(frame, width=50)
keyword_entry.grid(column=1, row=1)

output_label = ttk.Label(frame, text="Output PDF:")
output_label.grid(column=0, row=2, sticky=tk.W)
output_entry = ttk.Entry(frame, width=50)
output_entry.grid(column=1, row=2)

start_button = ttk.Button(frame, text="Start", command=start_scraping)
start_button.grid(column=1, row=3, sticky=tk.E)

root.mainloop()

それで実際に出来たコード動かしたら、

こんな感じで、「start」ボタン押すと実際にc:\temp\hatena3.pdfが出来て


pdfファイルを開くとこんな感じでした😊

この記事が気に入ったらサポートをしてみませんか?