As alterações do formatador de strings do Python nas edições recentes quebraram o conector do MySQL?
Estou escrevendo um script simples - ou deveria ser simples - para adquirir tweets da API do Twitter (tenho chaves de desenvolvedor / aplicativo e estou usando a interface Tweepy, não scraping ou qualquer coisa desse tipo - posso abandonar Tweepy por algo mais próximo de a API moderna, mas isso quase certamente não é o que está causando esse problema aqui).
Tenho uma instância do MySQL à qual me conecto e posso consultar muito bem, até chegar a hora de inserir o tweet - que tem muitos caracteres especiais, quase inevitavelmente. Para ser claro, estou usando o driver / conector oficial do Python para MySQL.
import mysql.connector
from mysql.connector import errorcode
Agora, estou ciente de que o StackOverflow está cheio de threads em que as pessoas obtêm exatamente o meu erro - simplesmente pedindo para verificar o manual de sintaxe do MySQL. Esses threads, que não são tão antigos (e não estou usando o Python mais recente, uso 3.7.9 para compatibilidade com algumas bibliotecas de PNL) insistem que a resposta é colocar a string que contém os caracteres especiais em um antigo style format string DENTRO do método cursor.execute, para colocar espaços reservados para variáveis de string entre aspas e para passar uma tupla com um segundo valor vazio se, como no meu caso, apenas uma variável deve ser inserida. Esta também é uma solução postada como parte de uma resposta de relatório de bug no site MySQL - e ainda assim, não tive sucesso.
Aqui está o que eu tenho - seguindo as instruções em dezenas de páginas aqui e no site oficial do banco de dados:
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 é meu objeto de cursor porque sou um idiota)
resultado esperado : o formatador de string passa ao MySQL uma string de tweet modificada que pode processar e adicionar como uma linha à tabela tweets_lgbt
resultado real : a inserção falha em um erro de sintaxe para qualquer tweet
Eu tentei ir mais longe a ponto de usar regex para remover tudo, exceto alfanuméricos e espaços - o mesmo problema. Estou me perguntando se os novos recursos de formato de string das versões atuais do Python quebraram a compatibilidade com este conector. Prefiro usar o driver oficial, mas mudarei para um ORM se for necessário. (Eu tentei os recursos mais recentes, como strings F, e descobri que eles causaram o mesmo resultado.)
Respostas
É assim que você deve inserir uma linha em sua tabela,
insert_tweet = "ABCEFg 9 XYZ"
"INSERT INTO tweets_lgbt (text) VALUES ('%s');"%(insert_tweet)
"INSERT INTO tweets_lgbt (text) VALUES ('ABCEFg 9 XYZ');"
Coisas a serem observadas
Os argumentos para um formatador de string são como os argumentos para uma função. Portanto, você não pode adicionar uma vírgula no final para converter uma string em uma tupla.
Se estiver tentando inserir vários valores de uma vez, você pode usar cursor.executemany ou esta resposta .
Eu tenho estas observações:
- a
VALUES
cláusula requer parêntesesVALUES (%s)
- as aspas / escapes de valores devem ser delegadas ao
execute
método do cursor , usando marcadores de posição não citados no SQL e passando os valores como o segundo argumento:cursor.execute(sql, (tweet_text,))
oucursor.executemany(sql, [(tweet_text1,), (tweet_text2,)])
- uma vez que essas etapas são aplicadas, não há necessidade de codificação / stringificação / regex-ifying: assumindo que
twi_text
é astr
e o conjunto de caracteres / agrupamento do banco de dados suporta o intervalo UTF-8 completo (por exemplo utf8mb4), então a inserção deve ser bem-sucedida.- em particular, codificar a
str
e, em seguida, chamarstr
o resultado deve ser evitado: você acaba com"b'my original string'"
- em particular, codificar a
Esta versão modificada do código na questão funciona para mim:
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.")