[Python]Les accents contre-attaquent (sur mysql)

Les accents contre-attaquent (sur mysql) [Python] - Python - Programmation

Marsh Posté le 13-10-2008 à 10:05:11    

Kikoo,
 
Python et les acents c'est la folie!! J'essaie de transférer des données depuis un fichier XML vers une BDD mysql avec MysqlDB, mais ça foire. Voici le code:

Code :
  1. for member in root:
  2. querystr ="INSERT INTO member VALUES ('%s','%s','%s');"%(unicode(u'%s'%member[0].text),unicode(u'%s'%member[1].text),member[2].text)
  3. print querystr
  4. conn.query(querystr.encode('ISO-8859-1'))
  5. conn.commit()


 
Avec cette version, les accents contenu dans querystr s'affiche bien avec le print mais ils deviennent des thêta grec dans ma BDD. Je pense que c'est dû à l'encode(iso), j'ai donc virer l'encode:
 

Code :
  1. for member in root:
  2. querystr ="INSERT INTO member VALUES ('%s','%s','%s');"%(unicode(u'%s'%member[0].text),unicode(u'%s'%member[1].text),member[2].text)
  3. print querystr
  4. conn.query(querystr)
  5. conn.commit()


 
Avec cette version du code, les accents contenu dans querystr s'affiche toujours bien avec le print mais j'ai alors une erreur:

Code :
  1. Traceback (most recent call last):
  2.   File "profils.py", line 44, in <module>
  3.     conn.query(querystr.decode(fichierEncodXML))
  4. UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 40:
  5. ordinal not in range(128)


 
Donc si j'encode pas il me met un thêta et si j'encode pas (ou que je decode), il me met une erreur de codec.
 
Quelqu'un pourrait-il m'aider svp?
 
P.S.: j'utilise les query/commit plutôt que les execute du cursor car ces derniers n'enregistraient pas les données dans ma BDD
 

Reply

Marsh Posté le 13-10-2008 à 10:05:11   

Reply

Marsh Posté le 13-10-2008 à 15:26:06    

1.

Code :
  1. unicode(u'%s'%member[0].text),unicode(u'%s'%member[1].text)


c'est du grand n'importe quoi
2. "j'utilise les query/commit plutôt que les execute du cursor car ces derniers n'enregistraient pas les données dans ma BDD" t'as essayé de commit() après execute/executemany?
3. Spécifie un charset dans MySQLdb.open, sinon tu vas soit avoir une erreur d'encoding (cas 2, mysqldb tente d'encoder en ascii par défaut je présume) soit directement des données binaires/mal encodées en DB (cas 1)

 

Cf doc mysqldb http://mysql-python.sourceforge.net/MySQLdb.html

Message cité 1 fois
Message édité par masklinn le 13-10-2008 à 15:27:56

---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 13-10-2008 à 16:35:55    

masklinn a écrit :

1.

Code :
  1. unicode(u'%s'%member[0].text),unicode(u'%s'%member[1].text)


c'est du grand n'importe quoi
2. "j'utilise les query/commit plutôt que les execute du cursor car ces derniers n'enregistraient pas les données dans ma BDD" t'as essayé de commit() après execute/executemany?
3. Spécifie un charset dans MySQLdb.open, sinon tu vas soit avoir une erreur d'encoding (cas 2, mysqldb tente d'encoder en ascii par défaut je présume) soit directement des données binaires/mal encodées en DB (cas 1)

 

Cf doc mysqldb http://mysql-python.sourceforge.net/MySQLdb.html

 

2. J'avais essayé mais le cursor n'a pas de membre de membre commit(), par contre je viens de penser que je pouvais toujours commiter la connexion, ce que je viens de faire et ça fonctionne  :D

 

Grâce à tes remarques, j'ai modif pendant 30 minutes et suite à divers modif, je suis arrivé à ça:

Code :
  1. conn = MySQLdb.connect(host,user,passwd,dbName)
  2. cursor = conn.cursor()
  3. cursor.execute("""CREATE TABLE member(pseudo CHAR(50) NOT NULL, classe CHAR (50) NOT NULL , dateInscr DATETIME, PRIMARY KEY(pseudo));""" )
  4. conn.commit()
  5. for member in root:
  6.    querystr =u"INSERT INTO member VALUES ('%s','%s','%s');"%(member[0].text,member[1].text,member[2].text)
  7.    print querystr
  8.    cursor.execute(querystr.encode(sys.stdout.encoding))
  9.    conn.commit()
 

Qui marche très bien  :)

 

Merci beaucoup!!!

Message cité 1 fois
Message édité par Shadew le 13-10-2008 à 16:36:30
Reply

Marsh Posté le 13-10-2008 à 16:59:45    

Shadew a écrit :

Qui marche très bien  :)


Non, ça marche pas du tout
Pour commencer, où est la spécification de charset que j'ai demandé? elle est à faire dans MySQLdb.connect, tu n'as pas à encoder manuellement quoi que ce soit.
 
Secundo,

Shadew a écrit :

Code :
  1. querystr =u"INSERT INTO member VALUES ('%s','%s','%s');"%(member[0].text,member[1].text,member[2].text)



c'est dangereux. Si un de tes objets contient un caractère (') tout pête, et si jamais ce sont des données qui viennent d'utilisateurs quelconques ils peuvent faire ce qu'ils veulent avec ta db, l'intérêt d'utiliser cursor.execute c'est justement qu'il prépare tes requêtes pour qu'elles soient tout le temps propres sans ce genre de trucs
 
La version correcte, c'est:

Code :
  1. querystr = u"INSERT INTO member VALUES ('%s','%s','%s');"
  2. cursor.execute(querystr, (member[0].text,member[1].text,member[2].text))


là mysqldb va se charger de tout rendre propre (note: pas besoin de créer querystr ici, sauf pour le logger).
 
Tertio, plutôt que d'itérer manuellement et de commiter après chaque insert, je suggérerais d'utiliser executemany et de transformer tes données d'entrée en quelque chose qu'il peut consommer, avec une list comprehension par exemple:

Code :
  1. querystr =u"INSERT INTO member VALUES ('%s','%s','%s');"
  2. data = [(member0.text, member1.text, member2.text) for member0, member1, member2 in root]
  3. cursor.executemany(querystr, data)
  4. con.commit()


(note: idem, pas besoin de créer data, tu peux générer tes données directement dans executemany
De cette manière, ton code est plus clair, tu as une seule transaction, il n'y a pas d'itération manuelle et si mysqldb implémente un batch insert (chose dont je n'ai aucune idée) ça lui permet de l'utiliser.
(note finale: je suis sûr que tu peux trouver de meilleurs noms que "member0", "member1" et "member2" histoire de rendre leur rôle clair)


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 13-10-2008 à 23:31:09    

Voilà, j'ai adapté avec le executemany. J'ai encore des problèmes à l'encodage. Voici le code actuel:

Code :
  1. conn = MySQLdb.connect(host,user,passwd,dbName,charset=sys.stdout.encoding)
  2. cursor = conn.cursor()
  3. cursor.execute("""CREATE TABLE member(pseudo CHAR(50) NOT NULL, classe CHAR (50) NOT NULL , dateInscr DATETIME, PRIMARY KEY(pseudo));""" )
  4. querystr =u"INSERT INTO member VALUES (%s,%s,%s);"
  5. data = [(pseudo.text, classe.text, dateInscr.text) for pseudo, classe, dateInscr in root]
  6. cursor.executemany(querystr, data)
  7. conn.commit()


 
Et là j'ai droit à l'erreur :

Code :
  1. _mysql_exceptions.OperationalError: (2019, "Can't initialize character set cp437
  2. (path: C:\\mysql\\\\share\\charsets\\)" )


 
Donc apparement, il ne supporte pas, lors de la connexion, l'encodage nécessaire pour pouvoir encoder sans accents (le cp437). Limite je pourrais l'ouvrir en latin1 (ça il supporte, il supporte pas non plus l'ISO) et décoder la chaine pour la ré-encoder en latin1 mais c'est bourrin et ça revient à encoder manuellement (donc on revient au point de départ).J'ai aussi essayer le use_unicode = True, mais là encore mes accents ne passent pas (je me retrouve avec des thêta dans la BDD). J'ai essayé un autre truc et ça ça marche:  

Code :
  1. conn = MySQLdb.connect(host,user,passwd,dbName)
  2. cursor = conn.cursor()
  3. cursor.execute("""CREATE TABLE member(pseudo CHAR(50) NOT NULL, classe CHAR (50) NOT NULL , dateInscr DATETIME, PRIMARY KEY(pseudo));""" )
  4. querystr =u"INSERT INTO member VALUES (%s,%s,%s);"
  5. data = [(pseudo.text.encode(sys.stdout.encoding), classe.text.encode(sys.stdout.encoding), dateInscr.text.encode(sys.stdout.encoding)) for pseudo, classe, dateInscr in root]
  6. cursor.executemany(querystr, data)
  7. conn.commit()


Donc je ré-encode les éléments à ajouter dans la base de données.  
 
Le problème quand je fais ça c'est que tu m'as dit que

Citation :

tu n'as pas à encoder manuellement quoi que ce soit

Il y a une raison de portabilité ou de sécurité à cela?
 
En tout cas merci pour ton aide, j'avais complétement oublier que le ' pouvait tout faire foirer et c'est écrit bien plus proprement comme ça!!
 
P.S.: tout à l'heure, j'avais déjà essayé le charset mais ça ne marchait pas mieux que maintenant, c'est pour ça que je m'était contenté de les encoder manuellement.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed