Czy zmiany w programie do formatowania ciągów w Pythonie w ostatnich wydaniach spowodowały uszkodzenie łącznika MySQL?

Nov 28 2020

Piszę prosty - lub powinien być prosty - skrypt do pobierania tweetów z API Twittera (mam klucze programisty / aplikacji i używam interfejsu Tweepy, nie skrobam ani nic w tym rodzaju - mogę porzucić Tweepy dla czegoś bliższego nowoczesny interfejs API, ale prawie na pewno nie jest to przyczyną tego problemu).

Mam instancję MySQL, z którą się łączę i mogę przesyłać zapytania dobrze, dopóki nie nadejdzie czas wstawienia tweeta - który ma wiele znaków specjalnych, prawie nieuchronnie. Żeby było jasne, używam oficjalnego sterownika / złącza Pythona dla MySQL.

import mysql.connector
from mysql.connector import errorcode

Teraz zdaję sobie sprawę, że StackOverflow jest ZAŁADOWANY wątkami, w których ludzie otrzymują dokładny błąd - po prostu stwierdzając, że sprawdzają instrukcję składni MySQL. Te wątki, które nie są aż tak stare (i nie używam najnowszego Pythona, używam wersji 3.7.9 ze względu na zgodność z niektórymi bibliotekami NLP) nalegają, aby odpowiedź polegała na umieszczeniu ciągu znaków ze znakami specjalnymi w starym style string format WITHIN the cursor.execute metoda, aby umieścić symbole zastępcze zmiennych łańcuchowych w cudzysłowach i przekazać krotkę z pustą drugą wartością, jeśli, tak jak w moim przypadku, ma zostać wstawiona tylko jedna zmienna. Jest to również rozwiązanie opublikowane w ramach odpowiedzi na zgłoszenie błędu w witrynie MySQL - ale nie udało mi się.

Oto, co mam - postępując zgodnie ze wskazówkami na dziesiątkach stron tutaj i oficjalnej stronie bazy danych:

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 jest moim obiektem kursora, ponieważ jestem idiotą)

oczekiwany wynik : program formatujący ciąg przekazuje MySQL zmodyfikowany ciąg tweeta, który może przetworzyć i dodać jako wiersz do tabeli tweets_lgbt

rzeczywisty wynik : wstawianie nie powiodło się z powodu błędu składni dowolnego tweeta

Próbowałem posunąć się do tego, aby użyć wyrażenia regularnego, aby usunąć wszystko oprócz znaków alfanumerycznych i spacji - ten sam problem. Zastanawiam się, czy nowe funkcje formatu ciągów w obecnych wersjach Pythona zepsuły kompatybilność z tym łącznikiem? Wolę używać oficjalnego sterownika, ale jeśli będę musiał, przełączę się na ORM. (Wypróbowałem nowsze funkcje, takie jak ciągi F i stwierdziłem, że powodują ten sam wynik).

Odpowiedzi

2 CodeIt Nov 28 2020 at 13:53

Oto jak powinieneś wstawić wiersz do swojej tabeli,

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

Rzeczy do zapamiętania

  1. Argumenty programu formatującego ciągi są takie same, jak argumenty funkcji. Nie możesz więc dodać przecinka na końcu, aby przekształcić tam ciąg znaków w krotkę.

  2. Jeśli próbujesz wstawić wiele wartości naraz, możesz użyć opcji kursor.executemany lub tej odpowiedzi .

2 snakecharmerb Nov 28 2020 at 14:30

Mam takie spostrzeżenia:

  • VALUESwarunek wymaga nawiasówVALUES (%s)
  • cytowanie / unikanie wartości powinno być delegowane do metody kursora execute, używając niecytowanych symboli zastępczych w SQL i przekazując wartości jako drugi argument: cursor.execute(sql, (tweet_text,))lubcursor.executemany(sql, [(tweet_text1,), (tweet_text2,)])
  • po wykonaniu tych kroków nie ma potrzeby kodowania / określania łańcuchów / regex-ifying: zakładając, że twi_textjest to a, stra zestaw znaków / sortowania bazy danych obsługuje pełny zakres UTF-8 (na przykład utf8mb4), wtedy wstawianie powinno się powieść.
    • w szczególności należy unikać kodowania a, stra następnie wywoływania strwyniku: kończy się to"b'my original string'"

Ta zmodyfikowana wersja kodu w pytaniu działa dla mnie:

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.")