如何让SQLAlchemy正确地将unicode省略号插入到mySQL表中?

kve*_*nda 16 python mysql unicode sqlalchemy feedparser

我正在尝试使用feedparser解析RSS提要并使用SQLAlchemy将其插入到mySQL表中.我实际上能够让这个运行得很好但是今天feed在描述中有一个带省略号字符的项目,我得到以下错误:

UnicodeEncodeError:'latin-1'编解码器无法编码位置35中的字符u'\ u2026':序数不在范围内(256)

如果我将convert_unicode = True选项添加到引擎,我可以让插件通过,但省略号不会显示它只是奇怪的字符.这似乎是有道理的,因为据我所知,拉丁语1中没有水平省略号.即使我将编码设置为utf-8,它似乎没有什么区别.如果我使用phpmyadmin进行插入并包含省略号,它会很好.

我想我只是不理解字符编码或如何让SQLAlchemy使用我指定的字符编码.有没有人知道如何让文本进入没有奇怪的字符?

UPDATE

我想我已经想出了这个,但我不确定为什么这很重要......

这是代码:

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)
Run Code Online (Sandbox Code Playgroud)

这是运行没有charset选项的代码的输出/回溯:

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? 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)
Run Code Online (Sandbox Code Playgroud)

所以看起来像将字符串添加到mysql连接字符串中就可以了.我想它默认为拉丁语1?我曾尝试将content_engine上的编码标志设置为utf8,但没有做任何事情.任何人都知道为什么当表和字段设置为utf8 unicode时它会使用latin-1?我还尝试使用.encode('cp1252')编码项目['description],然后将其发送出去,即使没有将charset选项添加到连接字符串也能正常工作.这应该不适用于拉丁语-1,但显然它确实如此?我有解决方案,但会喜欢答案:)

Jim*_*unt 32

错误消息

UnicodeEncodeError: 'latin-1' codec can't encode character u'\u2026' 
in position 35: ordinal not in range(256)
Run Code Online (Sandbox Code Playgroud)

似乎表明某些Python语言代码试图将字符\u2026转换为Latin-1(ISO8859-1)字符串,并且它失败了.不足为奇的是U+2026 HORIZONTAL ELLIPSIS,该角色在ISO8859-1中没有单一的等效角色.

您通过?charset=utf8在SQLAlchemy连接调用中添加查询来解决此问题:

import sqlalchemy
from sqlalchemy import create_engine, MetaData, Table

db = create_engine('mysql://user:pass@localhost/db?charset=utf8')
Run Code Online (Sandbox Code Playgroud)

SQLAlchemy文档的Database Urls部分告诉我们,以URL开头的URL mysql表示使用mysql-python驱动程序的MySQL方言.

以下部分,自定义DBAPI connect()参数告诉我们查询参数传递给基础DBAPI.

那么,mysql-python驱动程序对参数的作用是{charset: 'utf8'}什么?部分文档的功能和属性说明charset属性"......如果存在,连接字符集将更改为此字符集,如果它们不相等."

要找出连接字符集的含义,我们转向10.1.4.MySQL 5.6参考手册的连接字符集和排序规则.总而言之,MySQL可以将传入的查询解释为与数据库的字符集不同的编码,并且与返回的查询结果的编码不同.

由于您报告的错误消息看起来像Python而不是SQL错误消息,我将推测SQLAlchemy或mysql-python中的某些内容在尝试将查询转换为默认连接编码latin-1之前发送它.这就是触发错误的原因.但是,查询字符串?charset=utf8在你的connect()电话改变了连接编码,并且U+2026 HORIZONTAL ELLIPSIS是打不通.

更新:你还会问,"如果我删除charset选项,然后使用.encode('cp1252')对描述进行编码,它将会很好地完成.省略号如何能够通过cp1252而不是unicode?"

编码cp1252具有在字节值的水平省略号\x85.因此,可以编码包含U+2026 HORIZONTAL ELLIPSIS在cp1252中的Unicode字符串而不会出错.

还要记住,在Python中,Unicode字符串和字节字符串是两种不同的数据类型.推测MySQLdb可能具有仅通过SQL连接发送字节串的策略是合理的.因此,它会将作为Unicode字符串接收的查询编码为字节字符串,但会将查询作为字节字符串单独接收.(这是推测,我没看过源代码.)

在您发布的回溯中,最后两行(最接近错误发生的位置)显示方法名称 literal,后跟unicode_literal.这倾向于支持MySQLdb将其作为Unicode字符串接收的查询编码为字节字符串的理论.

当您自己对查询字符串进行编码时,可以绕过以不同方式执行此编码的MySQLdb部分.但请注意,如果您对查询字符串的编码方式与MySQL连接字符集要求的编码方式不同,那么您将遇到编码不匹配,并且您的文本可能存储错误.