tutorial query español python mysql unicode sqlalchemy feedparser

python - query - sqlalchemy tutorial



¿Cómo obtengo SQLAlchemy para insertar correctamente una elipsis Unicode en una tabla mySQL? (2)

El mensaje de error

UnicodeEncodeError: ''latin-1'' codec can''t encode character u''/u2026'' in position 35: ordinal not in range(256)

parece indicar que algún código de lenguaje Python está tratando de convertir el carácter /u2026 en una cadena Latin-1 (ISO8859-1), y está fallando. No es sorprendente que ese personaje sea U+2026 HORIZONTAL ELLIPSIS , que no tiene un solo carácter equivalente en ISO8859-1.

?charset=utf8 el problema agregando la consulta ?charset=utf8 en su llamada de conexión SQLAlchemy:

import sqlalchemy from sqlalchemy import create_engine, MetaData, Table db = create_engine(''mysql://user:pass@localhost/db?charset=utf8'')

La sección URL de base de datos de la documentación de SQLAlchemy nos dice que una URL que comienza con mysql indica un dialecto MySQL, utilizando el controlador mysql-python .

La siguiente sección, Custom DBAPI connect () argumentos , nos dice que los argumentos de consulta se pasan al DBAPI subyacente.

Entonces, ¿qué hace el controlador mysql-python de un parámetro {charset: ''utf8''} ? Las funciones de sección y los atributos de su documentación dicen del atributo de juego de charset "... Si está presente, el conjunto de caracteres de conexión se cambiará a este juego de caracteres, si no son iguales".

Para saber qué significa el conjunto de caracteres de conexión, pasamos a 10.1.4. Conjuntos de caracteres de conexión y intercalaciones del manual de referencia de MySQL 5.6. Para abreviar, MySQL puede interpretar las consultas entrantes como una codificación diferente al juego de caracteres de la base de datos, y diferente a la codificación de los resultados de la consulta devuelta.

Dado que el mensaje de error que informa parece un mensaje de error de Python en lugar de SQL, especularé que algo en SQLAlchemy o mysql-python está intentando convertir la consulta en una codificación de conexión predeterminada de latin-1 antes de enviarla. Esto es lo que desencadena el error. Sin embargo, la cadena de consulta ?charset=utf8 en su llamada de connect() cambia la codificación de la conexión, y U+2026 HORIZONTAL ELLIPSIS puede pasar.

Actualización: también se pregunta: "si elimino la opción de juego de caracteres y luego codigo la descripción usando .encode (''cp1252''), pasará muy bien. ¿Cómo es posible obtener una elipsis con cp1252 pero no unicode?"

La codificación cp1252 tiene un carácter de puntos suspensivos horizontal en el valor de bytes /x85 . Por lo tanto, es posible codificar una cadena Unicode que contenga U+2026 HORIZONTAL ELLIPSIS en cp1252 sin error.

Recuerde también que en Python, las cadenas Unicode y de bytes son dos tipos de datos diferentes. Es razonable especular que MySQLdb podría tener una política de enviar solo cadenas de bytes a través de una conexión SQL. Por lo tanto, codificaría una consulta recibida como una cadena Unicode en una cadena de bytes, pero dejaría una consulta recibida solo como una cadena de bytes. (Esto es especulación, no he visto el código fuente).

En el rastreo publicado, las últimas dos líneas (más cercanas a donde se produce el error) muestran el nombre de los métodos literal , seguido de unicode_literal . Eso tiende a respaldar la teoría de que MySQLdb está codificando la consulta que recibe como una cadena Unicode en una cadena de bytes.

Cuando codifica la cadena de consulta usted mismo, omite la parte de MySQLdb que hace esta codificación de manera diferente. Sin embargo, tenga en cuenta que si codifica la cadena de consulta de forma diferente a la que establece el juego de caracteres MySQL, entonces tendrá una discrepancia de codificación y su texto probablemente se almacenará incorrectamente.

Estoy tratando de analizar un feed RSS con el analizador de feed e insertarlo en una tabla mySQL usando SQLAlchemy. De hecho, pude hacer que funcionara bien, pero hoy el feed tenía un elemento con un carácter de elipsis en la descripción y me aparece el siguiente error:

UnicodeEncodeError: el códec ''latin-1'' no puede codificar el carácter u ''/ u2026'' en la posición 35: ordinal no en el rango (256)

Si agrego la opción convert_unicode = True al motor, puedo hacer que pase el inserto, pero las elipsis no aparecen, solo son personajes extraños. Esto parece tener sentido ya que, a mi leal saber y entender, no hay elipsis horizontal en latin-1. Incluso si configuro la codificación para utf-8, no parece marcar la diferencia. Si hago una inserción usando phpmyadmin e incluyo las elipsis, pasa bien.

Estoy pensando que simplemente no entiendo las codificaciones de los personajes o cómo hacer que SQLAlchemy use uno que especifico. ¿Alguien sabe cómo hacer que el texto entre sin personajes extraños?

ACTUALIZAR

Creo que lo he descubierto, pero no estoy seguro de por qué es importante ...

Aquí está el código:

import sys import feedparser import sqlalchemy from sqlalchemy import create_engine, MetaData, Table COMMON_CHANNEL_PROPERTIES = [ (''Channel title:'',''title'', None), (''Channel description:'', ''description'', 100), (''Channel URL:'', ''link'', None), ] COMMON_ITEM_PROPERTIES = [ (''Item title:'', ''title'', None), (''Item description:'', ''description'', 100), (''Item URL:'', ''link'', None), ] INDENT = u'' ''*4 def feedinfo(url, output=sys.stdout): feed_data = feedparser.parse(url) channel, items = feed_data.feed, feed_data.entries #adding charset=utf8 here is what fixed the problem db = create_engine(''mysql://user:pass@localhost/db?charset=utf8'') metadata = MetaData(db) rssItems = Table(''rss_items'', metadata,autoload=True) i = rssItems.insert(); for label, prop, trunc in COMMON_CHANNEL_PROPERTIES: value = channel[prop] if trunc: value = value[:trunc] + u''...'' print >> output, label, value print >> output print >> output, "Feed items:" for item in items: i.execute({''title'':item[''title''], ''description'': item[''description''][:100]}) for label, prop, trunc in COMMON_ITEM_PROPERTIES: value = item[prop] if trunc: value = value[:trunc] + u''...'' print >> output, INDENT, label, value print >> output, INDENT, u''---'' return if __name__=="__main__": url = sys.argv[1] feedinfo(url)

Aquí está la salida / traceback de ejecutar el código sin la opción de juego de caracteres:

Channel title: [H]ardOCP News/Article Feed Channel description: News/Article Feed for [H]ardOCP... Channel URL: http://www.hardocp.com Feed items: Item title: Windows 8 UI is Dropping the ''Start'' Button Item description: After 15 years of occupying a place of honor on the desktop, the "Start" button will disappear from ... Item URL: http://www.hardocp.com/news/2012/02/05/windows_8_ui_dropping_lsquostartrsquo_button/ --- Item title: Which Crashes More&#63; Apple Apps or Android Apps Item description: A new study of smartphone apps between Android and Apple conducted over a two month period came up w... Item URL: http://www.hardocp.com/news/2012/02/05/which_crashes_more63_apple_apps_or_android/ --- Traceback (most recent call last): File "parse.py", line 47, in <module> feedinfo(url) File "parse.py", line 36, in feedinfo i.execute({''title'':item[''title''], ''description'': item[''description''][:100]}) File "/usr/local/lib/python2.7/site-packages/sqlalchemy/sql/expression.py", line 2758, in execute return e._execute_clauseelement(self, multiparams, params) File "/usr/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 2304, in _execute_clauseelement return connection._execute_clauseelement(elem, multiparams, params) File "/usr/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1538, in _execute_clauseelement compiled_sql, distilled_params File "/usr/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1639, in _execute_context context) File "/usr/local/lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 330, in do_execute cursor.execute(statement, parameters) File "build/bdist.linux-i686/egg/MySQLdb/cursors.py", line 159, in execute File "build/bdist.linux-i686/egg/MySQLdb/connections.py", line 264, in literal File "build/bdist.linux-i686/egg/MySQLdb/connections.py", line 202, in unicode_literal UnicodeEncodeError: ''latin-1'' codec can''t encode character u''/u2026'' in position 35: ordinal not in range(256)

Entonces parece que agregar el juego de caracteres a la cadena de conexión mysql lo hizo. Supongo que por defecto es latin-1? Había intentado configurar la bandera de codificación en content_engine para utf8 y eso no hizo nada. ¿Alguien sabe por qué usaría latin-1 cuando las tablas y los campos están configurados en utf8 unicode? También intenté codificar el elemento [''descripción] usando .encode ('' cp1252 '') antes de enviarlo y eso funcionó bien incluso sin agregar la opción de juego de caracteres a la cadena de conexión. Eso no debería haber funcionado con latin-1 pero aparentemente lo hizo? Tengo la solución pero me encantaría una respuesta :)


Agregar charset=utf8 en la cadena de conexión definitivamente ayuda, pero encontré situaciones en Python 2.7 cuando también era necesario agregar convert_unicode=True a create_engine . La documentación de SQLAlchemy dice que es solo para aumentar el rendimiento, pero en mi caso realmente resolvió el problema del codificador incorrecto que se utiliza.