Czy zmiany w programie do formatowania ciągów w Pythonie w ostatnich wydaniach spowodowały uszkodzenie łącznika MySQL?
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
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
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ę.
Jeśli próbujesz wstawić wiele wartości naraz, możesz użyć opcji kursor.executemany lub tej odpowiedzi .
Mam takie spostrzeżenia:
VALUES
warunek 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_text
jest to a,str
a 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,
str
a następnie wywoływaniastr
wyniku: kończy się to"b'my original string'"
- w szczególności należy unikać kodowania a,
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.")