

### したいこと

Pythonでのスクレイピングで、更新情報があった場合にGmailを用いてメールを送るよう設定しています。  
↑ここまではうまくいった

同じ日に３回チェックするので、同じ内容を引っ張らないよう、送ったメールの本文をログに残すやり方を提案されています。

以下記述

[code]

    log_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sent_emails.log")
    
[/code]

### 状況

  * 実行しても何も返答が返ってこない。
  * 検索してもログファイルが見つからない

下にコード全体を記載します。  
皆様どうぞよろしくお願いいたします。  
（以下、chatGPTが書いてくれました）

[code]

    import smtplib
    import ssl
    import requests
    from bs4 import BeautifulSoup
    from email.mime.text import MIMEText
    import datetime
    import time
    import pytz
    import os
    
    # 定期実行する時間帯（時:分）をJSTタイムゾーンで定義
    scheduled_times = ["9:30", "13:30", "16:30"]
    
    # 送信済みのメールの内容を保存するファイル名
    log_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sent_emails.log")
    
    # メール送信関数
    def send_email(body):
        # Gmailアカウント情報
        from_address = "威力のメアド@gmail.com"
        to_address = "威力のメアド@gmail.com"
        bcc = ""
        # 発行したアプリパスワード
        app_password = "威力のメアドパスワード"
    
        # メールデータ(MIME)の作成
        subject = "更新通知メール"
        msg = MIMEText(body, "html")
        msg["Subject"] = subject
        msg["To"] = to_address
        msg["From"] = from_address
    
        # Gmailに接続してメール送信
        context = ssl.create_default_context()
        with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
            server.login(from_address, app_password)
            server.send_message(msg)
            print("Mail sent.")
    
    # 送信済みのメールの内容をファイルから読み込む関数
    def load_sent_emails():
        try:
            with open(log_file, "r", encoding="utf-8") as f:
                return f.read().splitlines()
        except FileNotFoundError:
            # ファイルが存在しない場合は空のリストを返す
            return []
    
    # 送信済みのメールの内容をファイルに書き込む関数
    def save_sent_email(data):
        print("保存するメール情報：", data)
        print("保存するファイルパス：", log_file)
        with open(log_file, 'a') as file:
            file.write(f"To: {data['To']}\n")
            file.write(f"Subject: {data['Subject']}\n")
            file.write(f"Date: {data['Date']}\n")
            file.write(f"Check time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            file.write("\n")
    
    
    # メイン処理
    while True:
        # data変数を初期化
        data = ""
        # 現在時刻を取得
        now = datetime.datetime.now().strftime("%H:%M")
        # スケジュールされた時間帯であれば処理を実行
        if now in scheduled_times:
            url_list = [
                "チェックしたいURL",
            ]
    
            new_data = []
    
            for url in url_list:
                resp = requests.get(url)
                soup = BeautifulSoup(resp.text, "html.parser")
                update_date = soup.find("span", class_="c-topics__date")
                if update_date and update_date.get_text(strip=True) ==     datetime.datetime.now().strftime("%Y年%m月%d日"):
                    link = "https://www.env.go.jp" + soup.find("a", class_="c-topics__link").get("href")
                    text = soup.find("a", class_="c-topics__link").get_text(strip=True)
                    data = "{}\n{}".format(text, link)
                    new_data.append(data)
    
            if new_data:
                data = "\n\n".join(new_data)
                send_email(data)
    
            # 送信済みのメール内容をファイルに保存
            save_sent_email(data)
    
        # 次の実行までの待機時間を計算
        next_run = (datetime.datetime.now() + datetime.timedelta(minutes=30)).strftime("%H:%M")
        seconds_to_wait = (datetime.datetime.strptime(next_run, "%H:%M") -     datetime.datetime.strptime(now, "%H:%M")).total_seconds()
    
        # 次の実行まで待機
        time.sleep(seconds_to_wait)
    
[/code]


CC BY-SA 4.0
2023-02-23T10:27:36.817
93960
2023-02-26T13:05:20.637
2023-02-24T12:30:00.117
7347
57239
question
python
Python で Gmail 送信本文のログの残し方


>   * 実行しても何も返答が返ってこない。
>   * 検索してもログファイルが見つからない
>

この二つに対する回答は簡単です。質問のコードは「指定されている時間にのみ処理が実行される」ように作られているからです。なので、指定時間以外に実行しても何も処理せず、30分おきにその時間になっているか確認するだけという動作になります。13時30分か16時30分に実行してみてください(9時30分はバグにより動作しません)。処理が実行され、ログファイルが作成されます。それ以外の時間帯に実行しても処理は実行されません。13時30分か16時30分まで待つということもできますが、その場合はどこかの0分頃か30分頃に実行しておく必要があります。

* * *

それ以外にも、このコードには多くの問題があります。

  * 指定時間に実行するというコードがバグだらけです。 
    * 9時30は`"%H:%M"`は`"09:30"`であるため、`"9:30"`と同じになることはありません。(@metropolis さんのコメントによる指摘)
    * スリープが約30分であるため、処理が実行されるかも約30分おきになります。つまり、最初の実行時が0分頃か30分頃でない場合、`"13:30"`や`"16:30"`と同じになることはありません。
    * 23時30分以降はスリープ秒数(`seconds_to_wait`)が負になります。(`time.sleep(seconds_to_wait)`はエラーになる)
  * 「同じ内容を引っ張らない」という事が実装されていません。 
    * ログファイルを読み込む関数(`load_sent_emails()`)はありますが、どこにも使われていません。
    * ログファイルの保存(`save_sent_email()`)はメールの情報があるdictを想定しているのに渡しているのはstrであるため、たぶんエラーになります。(@metropolis さんのコメントによる指摘)
    * 結局、どうやって同じ内容を引っ張らないことを実現したいのかわかりません。
  * トピックの日付が今日の場合のみを確認するようですが、昨日の16:30以降に更新されたトピックは確認されることは無くなります。

これはひどい、というコードです。やりたいことの全体がわからないので、適切では無いかも知れませんが、見直した方が良いという所を挙げておきます。

  * 処理を指定の時刻に実行したいという場合は、既に優れた道具があります。 
    * OS標準のタスク実行の機能を使用した方が無難です。タスクスケジューラ(Windows)、Automator(macOS)、cron(Linux,UNiX)等です。
    * プログラム内でスケジューリングすることもできますが、その場合でも、[schedule](https://pypi.org/project/schedule/)のようなライブラリを使った方が良いでしょう。
  * 前回と同じことを返したくないという場合、何を保存し、何を比較するのかきちんと把握する必要があります。 
    * メール全体を残す必要はありません。メールを作るもとになったデータそのものを残し、それを比較すべきです。
    * もし、更新されたデータの時刻もわかれば、そもそも残す必要が無いかも知れません。16:30のチェックでは、13:30以降のデータだけを見れば良いのですから。
    * ずっと起動しっぱなし(タスク実行の機能を使わずに、プログラム内でスケジューリングしている)場合は、ファイルとして保存する必要はありません。前の情報はそれ用の変数に持たせておけば良いのですから。タスク実行のようにその都度プロセスを実行するのであれば、ファイルや外部DBとしどこかにデータを残す必要はあるでしょう。
    * データをファイルに保存する場合は、ログという形式よりも、[pickle](https://docs.python.org/ja/3/library/pickle.html)等でデータをシリアライズしたものを保存した方が良いです。取り出すときも簡単、かつ、確実ですから。
  * 何を持って更新しているとするかは、サイトとかがわからないので、具体的なアドバイスは難しいです。


CC BY-SA 4.0
2023-02-26T13:05:20.637
94015
2023-02-26T13:05:20.637
7347
93960
answer