IwataRisa
@IwataRisa (Risa Iwata)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

pythonで地震情報のWebサイトを作るために、Web APIをたたいてRDBにレコード登録する方法が知りたいです。

解決したいこと

pythonで地震情報のWebサイトを作るために、Web APIをたたいてRDBにレコード登録する方法が知りたいです。
あらかじめDBにテーブルを用意して、カラム名、データ型、を指定しています。
某AI chatに聞いてみましたが全然エラーが収まらず、解決方法を教えていただけないでしょうか。

発生している問題・エラー

データベースに接続しました。
データの挿入中にエラーが発生しました。ロールバックします。
エラー内容: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '茨城県北部', '3.6e0, '140.5e0, 36.5e0)' at line 1")

該当するソースコード

import requests
import json
import pymysql
import pandas as pd
def connect_database():
    # データベースに接続する
    connection = pymysql.connect(
        host='ホスト名',
        port=ポート名,
        user='ユーザー名',
        password='パスワード',
        db='データベース名'
    )

    # 接続したか確認する
    if connection:
        print("データベースに接続しました。")
    else:
        print("データベースへの接続に失敗しました。")
        exit()

    return connection
def insert_eq_information(connection, data):
    # カーソルを作成
    cur = connection.cursor()

    try:
        # テーブルAにデータを挿入する
        eq_information_data = [
            {
                "eq_id": item["id"],
                "eq_data": item["earthquake"]["time"],
                "name": item["earthquake"]["hypocenter"]["name"],
                "magnitude": item["earthquake"]["hypocenter"]["magnitude"],
                "longitude": item["earthquake"]["hypocenter"]["longitude"],
                "latitude": item["earthquake"]["hypocenter"]["latitude"],
            }
            for item in data
            if item["earthquake"]["hypocenter"]["magnitude"] != -1
        ]

        for item in eq_information_data:
            longitude = max(-180, min(180, item["longitude"]))  # 範囲を -180 から 180 に制限
            latitude = max(-90, min(90, item["latitude"]))  # 範囲を -90 から 90 に制限
            query = "INSERT INTO Test1 (eq_id, eq_data, name, magnitude, longitude, latitude) VALUES (%s, %s, %s, %s, %s, %s)"
            values = (item["eq_id"], item["eq_data"], item["name"], item["magnitude"], longitude, latitude)

            cur.execute(query, values)

        # トランザクションのコミット
        print("テーブルAへのデータ挿入が正常に完了しました。")
        connection.commit()
    except Exception as e:
        # エラーが発生した場合の処理
        print("テーブルAへのデータ挿入中にエラーが発生しました。ロールバックします。")
        connection.rollback()
        print("エラー内容:", str(e))
def insert_eq_intensity(connection, data):
    # カーソルを作成
    cur = connection.cursor()

    try:
        # テーブルBにデータを挿入する
        eq_intensity_data = [
            {
                "eq_id": item["id"],
                "pref": item["points"][0]["pref"],
                "scale": item["points"][0]["scale"],
            }
            for item in data
        ]  # ここでリスト内包表記を閉じる

        for item in eq_intensity_data:
            query = "INSERT INTO Test2 (eq_id, pref, scale) VALUES (%s, %s, %s)"
            values = (item["eq_id"], item["pref"], item["scale"])

            cur.execute(query, values)

        # トランザクションのコミット
        print("テーブルBへのデータ挿入が正常に完了しました。")
        connection.commit()
    except Exception as e:
        # エラーが発生した場合の処理
        print("テーブルBへのデータ挿入中にエラーが発生しました。ロールバックします。")
        connection.rollback()
        print("エラー内容:", str(e))
def main():
    # APIエンドポイント
    url = "https://api.p2pquake.net/v2/jma/quake"

    # パラメータ
    params = {
        "limit": 100,
        "offset": 0,
        "order": -1
    }

    res = requests.get(url, params=params)
    data = json.loads(res.text)

    # データベースに接続する
    connection = connect_database()

    # テーブルAにデータを挿入する
    insert_eq_information(connection, data)

    # テーブルBにデータを挿入する
    insert_eq_intensity(connection, data)

    # データベース接続をクローズする
    connection.close()

# メイン関数を実行
main()

自分で試したこと

●登録したカラムやデータ型を間違えて登録してると思い、何度か直そうと試みましたが、できませんでした。
●緯度(longitude)、経度(latitude)を登録する前に、2個のテーブル作成をDBの方で作るのではなくp、ythonで作成し、カラムを3つに絞ったときはうまく登録できました。

0

1Answer

クエリの中のプレースホルダは %s ではなく ? です。すべて ? に直せば動くと思います。

1Like

Comments

  1. @IwataRisa

    Questioner

    回答していただきありがとうございます。
    直したところ、今度はこのようなエラーが出てしまいました💦

    データベースに接続しました。
    テーブルAへのデータ挿入中にエラーが発生しました。ロールバックします。
    エラー内容: not all arguments converted during string formatting
    テーブルBへのデータ挿入中にエラーが発生しました。ロールバックします。
    エラー内容: list index out of range
    

    データベースに登録しているTBのするリーンショットはこれです...

    スクリーンショット 2023-06-08 150027.png
    スクリーンショット 2023-06-08 150000.png

  2. すみません、 PyMySQL の場合は %s を使うのが正しかったです。

  3. 発生しているエラーメッセージが

    データの挿入中にエラーが発生しました。ロールバックします。
    

    で、 print("テーブルAへのデータ挿入中にエラーが発生しました。ロールバックします。") と内容が違うようですが、エラーが発生したときのコードと質問のコードは一致していますか?

  4. @IwataRisa

    Questioner

    はい!ソースコードの3と4セル目を確認していただければと思うのですが、挿入が正しく行われなかったときにこのようなエラー文が表示されるようにtry文を使って処理しています。
    ややこしく載せてしまってすみません💦
    また、このエラー文が出ているときの変更点はプレースホルダ%s?に変更しました!

  5. @IwataRisa

    Questioner

    また、画像が上下逆になってしまったのですが、上がテーブルB下がテーブルAです!!

  6. 質問の最初にある データの挿入中にエラーが発生しました。ロールバックします。 と、 try 文で表示するエラー テーブルAへのデータ挿入中にエラーが発生しました。ロールバックします。 では文言が違うので、動かしているコードが違うのでは、という意味でうかがいました。

    (プレースホルダが ? のときの)エラー内容が エラー内容: (1064,〜 で間違いないかを確認したいです。

  7. @IwataRisa

    Questioner

    すみません、最初のエラー文が間違っていたようです…
    現在プレースホルダを戻したところこのように表示されています。
    スクリーンショット 2023-06-08 161253.png

  8. @IwataRisa

    Questioner

    データベースに接続しました。
    テーブルAへのデータ挿入中にエラーが発生しました。ロールバックします。
    エラー内容: (1062, "Duplicate entry '6480e15bf0f6de00079581a0' for key 'PRIMARY'")
    テーブルBへのデータ挿入中にエラーが発生しました。ロールバックします。
    エラー内容: list index out of range

  9. テーブルAの挿入では eq_id の値が重複する行を挿入しようとしてエラーが出ています。そのような挿入をしないようにするか、 INSERT ... ON DUPLICATE KEY UPDATE1でアップデートしてください。

    テーブルBの挿入ではどこかでリストの範囲外参照エラーが起きています。 item["points"][0] が怪しいです。値を表示してリストが空になっていないか確認してください。

    1. https://dev.mysql.com/doc/refman/8.0/ja/insert-on-duplicate.html

  10. @IwataRisa

    Questioner

    ありがとうございます!!
    何とかエラーが解消されました!
    まだまだ2か月の駆け出しで知識も全然足りず、私の説明や言葉がわかりにくかったと思います。
    そんな中親身になって回答していただき、本当にありがとうございました😊

    import requests
    import json
    import pymysql
    import pandas as pd
    
    def connect_database():
        # データベースに接続する
        connection = pymysql.connect(
            host='ホスト名',
            port=ポート名,
            user='ユーザー名',
            password='パスワード',
            db='データーベース'
        )
    
        # 接続したか確認する
        if connection:
            print("データベースに接続しました。")
        else:
            print("データベースへの接続に失敗しました。")
            exit()
    
        return connection
    
    def insert_eq_information(connection, data):
        # カーソルを作成
        cur = connection.cursor()
    
        try:
            # テーブルAにデータを挿入する
            eq_information_data = [
                {
                    "eq_id": item["id"],
                    "eq_data": item["earthquake"]["time"],
                    "name": item["earthquake"]["hypocenter"]["name"],
                    "magnitude": item["earthquake"]["hypocenter"]["magnitude"],
                    "longitude": item["earthquake"]["hypocenter"]["longitude"],
                    "latitude": item["earthquake"]["hypocenter"]["latitude"],
                }
                for item in data
                if item["earthquake"]["hypocenter"]["magnitude"] != -1
            ]
    
            for item in eq_information_data:
                longitude = max(-180, min(180, item["longitude"]))
                latitude = max(-90, min(90, item["latitude"]))
                query = "INSERT INTO Test1 (eq_id, eq_data, name, magnitude, longitude, latitude) VALUES (%s, %s, %s, %s, %s, %s) " \
                        "ON DUPLICATE KEY UPDATE eq_data = VALUES(eq_data), name = VALUES(name), magnitude = VALUES(magnitude), " \
                        "longitude = VALUES(longitude), latitude = VALUES(latitude)"
                values = (item["eq_id"], item["eq_data"], item["name"], item["magnitude"], longitude, latitude)
    
                cur.execute(query, values)
    
            # トランザクションのコミット
            print("テーブルAへのデータ挿入が正常に完了しました。")
            connection.commit()
        except Exception as e:
            # エラーが発生した場合の処理
            print("テーブルAへのデータ挿入中にエラーが発生しました。ロールバックします。")
            connection.rollback()
            print("エラー内容:", str(e))
    
    
    def insert_eq_intensity(connection, data):
        # カーソルを作成
        cur = connection.cursor()
    
        try:
            # テーブルBにデータを挿入する
            eq_intensity_data = [
                {
                    "eq_id": item["id"],
                    "pref": item["points"][0].get("pref", None),
                    "scale": item["points"][0].get("scale", None),
                }
                for item in data
                if item.get("points") and len(item["points"]) > 0
            ]
    
            for item in eq_intensity_data:
                query = "INSERT INTO Test2 (eq_id, pref, scale) VALUES (%s, %s, %s) " \
                        "ON DUPLICATE KEY UPDATE pref = VALUES(pref), scale = VALUES(scale)"
                values = (item["eq_id"], item["pref"], item["scale"])
    
                cur.execute(query, values)
    
            # トランザクションのコミット
            print("テーブルBへのデータ挿入が正常に完了しました。")
            connection.commit()
        except Exception as e:
            # エラーが発生した場合の処理
            print("テーブルBへのデータ挿入中にエラーが発生しました。ロールバックします。")
            connection.rollback()
            print("エラー内容:", str(e))
    
    
    def main():
        # APIエンドポイント
        url = "https://api.p2pquake.net/v2/jma/quake"
    
        # パラメータ
        params = {
            "limit": 100,
            "offset": 0,
            "order": -1
        }
    
        res = requests.get(url, params=params)
        data = json.loads(res.text)
    
        # データベースに接続する
        connection = connect_database()
    
        # テーブルAにデータを挿入する
        insert_eq_information(connection, data)
    
        # テーブルBにデータを挿入する
        insert_eq_intensity(connection, data)
    
        # データベース接続をクローズする
        connection.close()
    
    # メイン関数を実行
    main()
    

Your answer might help someone💌