最近のエディションでのPythonの文字列フォーマッタの変更により、MySQLコネクタが壊れましたか?

Nov 28 2020

TwitterのAPIからツイートを取得するための単純な(または単純なはずの)スクリプトを書いています(開発者/アプリキーがあり、Tweepyインターフェースを使用しており、スクレイピングなどではありません-Tweepyを捨ててもっと近いものにすることができます最新のAPIですが、それがこの問題の原因ではないことはほぼ間違いありません)。

ツイートを挿入するときまで、接続して問題なくクエリできるMySQLインスタンスがあります。これには、ほとんど必然的に、多くの特殊文字が含まれています。明確にするために、私はMySQL用の公式Pythonドライバー/コネクターを使用しています。

import mysql.connector
from mysql.connector import errorcode

今、私はStackOverflowが私の正確なエラーを受け取るスレッドでリッターされていることを知っています-単にMySQL構文マニュアルをチェックするように述べています。これらのスレッドはそれほど古くはありません(最新のPythonを使用していないため、一部のNLPライブラリとの互換性のために3.7.9を使用しています)。答えは、特殊文字を含む文字列を古いものに配置することです。 cursor.executeメソッド内のスタイルフォーマット文字列。文字列変数のプレースホルダーを引用符で囲み、私の場合のように1つの変数のみを挿入する場合は、2番目の値が空のタプルを渡します。これは、MySQL Webサイトにバグレポート応答の一部として投稿されたソリューションでもありますが、それでも成功しません。

これが私が持っているものです-ここの数十ページと公式データベースウェブサイトの指示に従ってください:

for tweet in tweepy.Cursor(twilek.search, q=keyword, tweet_mode='extended').items():
            twi_tweet = tweet.full_text
            print(twi_tweet)
            twi_tweet = twi_tweet.encode('utf8')
            requests_total+=1
            os.environ['TWITTER_REQUESTS'] = str(requests_total)
            requests_total = int(os.environ.get('TWITTER_REQUESTS'))
            # insert the archived tweet text into the database table
            sql = 'USE hate_tweets'
            ms_cur.execute(sql)
            twi_tweet = str(twi_tweet)
            insert_tweet = re.sub(r'[^A-Za-z0-9 ]+', '', twi_tweet)
            ms_cur.execute("INSERT INTO tweets_lgbt (text) VALUES %s" % (insert_tweet,))
            cnx.commit()
            print(ms_cur.rowcount, "record inserted.")

(私はドークなので、twilekは私のカーソルオブジェクトです)

期待される結果:文字列フォーマッタは、MySQLに変更されたツイート文字列を渡します。この文字列を処理して、tweets_lgbtテーブルに行として追加できます。

実際の結果:ツイートの構文エラーで挿入が失敗する

正規表現を使用して、英数字とスペース以外のすべてを削除しようとしましたが、同じ問題です。現在のPythonバージョンの新しい文字列形式の機能がこのコネクタとの互換性を壊したのではないかと思いますか?私は公式ドライバーを使用することを好みますが、必要に応じてORMに切り替えます。(F文字列などの新しい機能を試しましたが、同じ結果になることがわかりました。)

回答

2 CodeIt Nov 28 2020 at 13:53

これは、テーブルに行を挿入する方法です。

insert_tweet = "ABCEFg 9 XYZ"
"INSERT INTO tweets_lgbt (text) VALUES ('%s');"%(insert_tweet)
"INSERT INTO tweets_lgbt (text) VALUES ('ABCEFg 9 XYZ');"

注意事項

  1. 文字列フォーマッタへの引数は、関数への引数と同じです。したがって、最後にコンマを追加して、文字列をタプルに変換することはできません。

  2. 一度に複数の値を挿入しようとしている場合は、cursor.executemanyまたはこの回答を使用できます。

2 snakecharmerb Nov 28 2020 at 14:30

私はこれらの観察を持っています:

  • VALUES句は必要で括弧をVALUES (%s)
  • executeSQLで引用符で囲まれていないプレースホルダーを使用し、値を2番目の引数として渡すことにより、値の引用符/エスケープをカーソルのメソッドに委任する必要があります。cursor.execute(sql, (tweet_text,))またはcursor.executemany(sql, [(tweet_text1,), (tweet_text2,)])
  • これらの手順を適用すると、エンコード/文字列化/正規表現化の必要はありません。twi_textがaでstrあり、データベースの文字セット/照合順序がUTF-8の全範囲(utf8mb4など)をサポートしていると仮定すると、挿入は成功するはずです。
    • 特に、をエンコードしてから結果をstr呼び出すstrことは避けてください。"b'my original string'"

質問のコードのこの修正されたバージョンは私のために働きます:

import mysql.connector

DDL1 = """DROP TABLE IF EXISTS tweets_lgbt"""
DDL2 = """\
CREATE TABLE tweets_lgbt (
    `text` VARCHAR (256))
"""

# From https://twitter.com/AlisonMitchell/status/1332567013701500928?s=20
insert_tweet = """\
Particularly pleased to see @SarahStylesAU
 quoted in this piece for the work she did
👌

Thrive like a girl: Why women's cricket in Australia is setting the standard
"""

# Older connector releases don't support with... 
with mysql.connector.connect(database='test') as cnx:
    with cnx.cursor() as ms_cur:

        ms_cur.execute(DDL1)
        ms_cur.execute(DDL2)

        ms_cur.execute("INSERT INTO tweets_lgbt (`text`) VALUES (%s)",  (insert_tweet,))
        cnx.commit()
        print(ms_cur.rowcount, "record inserted.")