機能の統合

機能の統合

メールからURLを抽出する

gmail_url_extractor.py
でURLを取得し
image_downloader.py
で画像を取得

ocr_list.py
で vision api でOCR

この中で
line_notify.py
を使うことで
OCRした結果をワードリストに一致したものと画像をLINEで送信

とりあえずここまで作成したらgithubで公開

残る機能は
Yolov8のモデルを作成し画像認識させること
キーワードリストを効率的に作成するため
レシートをOCRしたものから
店名
価格
商品名
日付
を取り出しCSVファイルに保存すること

CSVファイルを元にDBを作成し
在庫管理と連携するようにして無駄な買い物を減らすこと

とりあえずまずは
ocr_list.py

gmail_url_extractor.py
でURLを取得し
image_downloader.py
で画像を取得

この画像に対してOCRすればOK

ただし
そのままソースを変えるとテストできなくなるので
別のファイルを作成することにする

なお今日は
おいしい牛乳
が割引らしいのでリストに加える

{
  "keywords": [  
    "麻婆豆腐",
    "キッチンタオル",
    "ほんだし",
    "ぶなしめじ",
    "レタス",
    "キャベツ",
    "おいしい牛乳"

  ]
}

そしてOCRするファイルを作成する

touch image_ocr_notifier.py

中身は

# example_usage.py

from gmail_url_extractor import get_first_unread_email_url
from image_downloader import download_and_merge_images
from google.cloud import vision
import io
import json

def load_settings(file_path='settings.json'):
    with open(file_path, 'r', encoding='utf_8') as settings_json:
        return json.load(settings_json)

def detect_text(image_path):
    """OCRで画像からテキストを抽出"""
    client = vision.ImageAnnotatorClient()
    with io.open(image_path, 'rb') as image_file:
        content = image_file.read()

    image = vision.Image(content=content)
    response = client.document_text_detection(image=image)
    full_text_annotation = response.full_text_annotation

    if response.error.message:
        raise Exception(
            '{}\nFor more info on error messages, check: '
            'https://cloud.google.com/apis/design/errors'.format(
                response.error.message))

    return full_text_annotation.text

def search_words(text, keywords):
    """抽出したテキストからキーワードを検索"""
    hitwords = []
    for keyword in keywords:
        if keyword in text:
            hitwords.append(keyword)
    return hitwords

def main():
    # 設定を読み込む
    settings = load_settings()
    
    # GmailからURLを取得
    url = get_first_unread_email_url('【Shufoo!】お気に入り店舗新着チラシお知らせメール')  # '特売情報'はメールの件名に含まれるキーワード
    
    if url:
        print(f"Processing URL: {url}")
        # 画像をダウンロードしてOCRを実行
        output_path = download_and_merge_images('config.json', url)
        
        if output_path:
            extracted_text = detect_text(output_path)
            hitwords = search_words(extracted_text, settings["keywords"])
            
            if hitwords:
                message = "特売リスト: " + ", ".join(hitwords)
                send_line_notify(message, output_path)
            else:
                print("マッチしたキーワードはありませんでした。")
    else:
        print("未読メールが見つかりませんでした。")

if __name__ == '__main__':
    main()

実行したけど

Processing URL: https://www.shufoo.net/pntweb/shopDetail/860323/?cid=nmail_pc

となるだけ

おそらくxpathが変更になっているので確認

/html/body/div[1]/div[3]/div[1]/div/div[2]/div[3]
/html/body/div[1]/div[3]/div[1]/div/div[2]/div[3]

同じだが動作していない

image_downloader.pyの動作確認

ログの追加: image_downloader.pyの中にログを追加して、どこで処理が失敗しているかを特定
print(f'Checking image source: {src}')  # ログ追加

となるように

def get_images_from_container(driver, base_xpath):
    """指定されたXPathから画像URLを取得する"""
    image_urls = []
    try:
        container = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.XPATH, base_xpath))
        )
        images = container.find_elements(By.TAG_NAME, 'img')
        
        for img in images:
            src = img.get_attribute('src')
            print(f'Checking image source: {src}')  # ログ追加
            if 'index/img' in src:
                image_urls.append(src)
                print(f'Found image: {src}')
    except Exception as e:
        print(f'Error finding images: {e}')
    return image_urls

というように
変更

また
CR処理のデバッグ: detect_text関数において、Cloud Vision APIのレスポンスが正常であるか確認

image_ocr_notifier.py
の中に

# OCRの結果をデバッグ表示 
full_text_annotation = response.full_text_annotation print("Extracted Text:", full_text_annotation.text)

を追加するので

def detect_text(image_path):
    """OCRで画像からテキストを抽出"""
    client = vision.ImageAnnotatorClient()
    with io.open(image_path, 'rb') as image_file:
        content = image_file.read()

    image = vision.Image(content=content)
    response = client.document_text_detection(image=image)
    if response.error.message:
        raise Exception(
            '{}\nFor more info on error messages, check: '
            'https://cloud.google.com/apis/design/errors'.format(
                response.error.message))
    
    # OCRの結果をデバッグ表示
    full_text_annotation = response.full_text_annotation
    print("Extracted Text:", full_text_annotation.text)

    return full_text_annotation.text

しかし動作しない

/html/body/div[1]/div[3]/div[1]/div/div[2]/div[2]/div[2]/div[1]

ではなく

/html/body/div[1]/div[3]/div[1]/div/div[2]/div[2]/div[2]

<div id="cv_1" class="ChirashiView" style="position: absolute; left: 0px; top: -30px; z-index: 1; opacity: 1; cursor: url(&quot;https://www.shufoo.net/site/chirashi_viewer_js/js/../images/openhand_8_8.cur&quot;), default; transition: opacity 200ms ease-in-out;"><div class="ChirashiView_tempDiv" style="position: absolute; overflow: hidden; width: 750px; height: 603px; left: 0px; top: 0px; z-index: 100;"></div><div class="ChirashiContainer" style="position: absolute; left: 0px; top: 0px; width: 750px; height: 603px; z-index: 0; opacity: 1;"><div class="inDiv" style="position: absolute; left: 0px; top: 0px; z-index: 1;"><div id="-2_-2" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: -1004px; top: -977.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px; height: 512px;"></div><div id="-1_-2" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: -492px; top: -977.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px; height: 512px;"></div><div id="0_-2" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 20px; top: -977.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px; height: 512px;"></div><div id="1_-2" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 532px; top: -977.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 198px; height: 512px;"></div><div id="2_-2" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 1044px; top: -977.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; height: 512px;"></div><div id="-2_-1" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: -1004px; top: -465.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px; height: 512px;"></div><div id="-1_-1" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: -492px; top: -465.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px; height: 512px;"></div><div id="0_-1" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 20px; top: -465.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px; height: 512px;"></div><div id="1_-1" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 532px; top: -465.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 198px; height: 512px;"></div><div id="2_-1" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 1044px; top: -465.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; height: 512px;"></div><div id="-2_0" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: -1004px; top: 46.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px; height: 510px;"></div><div id="-1_0" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: -492px; top: 46.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px; height: 510px;"></div><div id="0_0" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 20px; top: 46.5px; width: 512px; height: 512px;"><img draggable="false" src="https://ipqcache2.shufoo.net/c/2024/08/08/25295137072090/index/img/0_100_0.jpg" style="border: 0px; padding: 0px; margin: 0px; width: 512px; height: 510px;"></div><div id="1_0" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 532px; top: 46.5px; width: 512px; height: 512px;"><img draggable="false" src="https://ipqcache2.shufoo.net/c/2024/08/08/25295137072090/index/img/0_100_1.jpg" style="border: 0px; padding: 0px; margin: 0px; width: 198px; height: 510px;"></div><div id="2_0" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 1044px; top: 46.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; height: 510px;"></div><div id="-2_1" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: -1004px; top: 558.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px;"></div><div id="-1_1" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: -492px; top: 558.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px;"></div><div id="0_1" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 20px; top: 558.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px;"></div><div id="1_1" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 532px; top: 558.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 198px;"></div><div id="2_1" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 1044px; top: 558.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px;"></div><div id="-2_2" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: -1004px; top: 1070.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px;"></div><div id="-1_2" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: -492px; top: 1070.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px;"></div><div id="0_2" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 20px; top: 1070.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 512px;"></div><div id="1_2" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 532px; top: 1070.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px; width: 198px;"></div><div id="2_2" style="position: absolute; opacity: 1; transition: opacity 200ms ease-out; left: 1044px; top: 1070.5px; width: 512px; height: 512px;"><img draggable="false" src="https://www.shufoo.net/site/chirashi_viewer_js/images/transparent.png" style="border: 0px; padding: 0px; margin: 0px;"></div></div><div class="linkDiv" style="position: absolute; left: 0px; top: 0px; z-index: 2;"></div></div></div>

のソース部分が正解らしい

config.jsonの中身を

  "base_xpath_images": "/html/body/div[1]/div[3]/div[1]/div/div[2]/div[2]/div[2]"

とxpathを変更することで
とりあえず画像の取得はできたが

 File "/Users/snowpool/aw10s/store_adversting_list/image_ocr_notifier.py", line 67, in <module> main() File "/Users/snowpool/aw10s/store_adversting_list/image_ocr_notifier.py", line 60, in main send_line_notify(message, output_path) NameError: name 'send_line_notify' is not defined

となる

    # # OCRの結果をデバッグ表示
    # full_text_annotation = response.full_text_annotation
    # print("Extracted Text:", full_text_annotation.text)

というようにコメントアウト

from line_notify import send_line_notify

が抜けていたので
これを追記

なぜかGPTでソースを生成すると
モジュールのインポートが削除されることがよくある

無事に処理ができたがログに

WARNING: All log messages before absl::InitializeLog() is called are written to STDERR I0000 00:00:1723322571.066745 14856910 config.cc:230] gRPC experiments enabled: call_status_override_on_cancellation, event_engine_dns, event_engine_listener, http2_stats_fix, monitoring_experiment, pick_first_new, trace_record_callops, work_serializer_clears_time_cache Sent images/combined_image_20240811_054251.jpg: 200 {"status":200,"message":"ok"} 

と出る

GPTによれば

WARNING: All log messages before absl::InitializeLog() is called are written to STDERR

このメッセージは、abseil (Absl) ライブラリが初期化される前にログメッセージが標準エラー出力(STDERR)に書き込まれることを示しています。これは、Google CloudのPythonクライアントライブラリやgRPCなどのバックエンドでAbseilライブラリが使用されている場合によく見られます。この警告は通常、以下の点で心配する必要はありません:

初期化前ログの出力: Abseilが初期化される前に生成されるログメッセージが一時的にSTDERRに出力されるという技術的な情報を示しています。
影響なし: このメッセージは一般的にはアプリケーションの動作に影響を与えないため、無視しても問題ありません

I0000 00:00:1723322571.066745 14856910 config.cc:230] gRPC experiments enabled: call_status_override_on_cancellation, event_engine_dns, event_engine_listener, http2_stats_fix, monitoring_experiment, pick_first_new, trace_record_callops, work_serializer_clears_time_cache

このメッセージは、gRPCライブラリの内部で実験的な機能が有効になっていることを示す情報ログです。これも通常、開発者が特に指定しない限り、デフォルトで有効になっている設定です。これらの実験的な機能は、以下のような効果を持ちます:
* 実験的機能の有効化: gRPCの内部機能が特定の実験的な設定で動作していることを示します。
* 通常の動作: 通常、gRPCの標準的な利用に影響はなく、情報提供のためのログです。

これらのログメッセージは、技術的な情報や警告を提供するためのものです。特にabsl::InitializeLog()やgRPCに関する警告は、ログの設定やライブラリの内部動作に関するもので、アプリケーションの正常動作には通常影響を与えません

とのこと

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です