2012-02-06 34 views
15

Tôi đang cố gắng phân tích cú pháp một nguồn cấp dữ liệu RSS với feedparser và chèn nó vào một bảng mySQL bằng cách sử dụng SQLAlchemy. Tôi đã thực sự có thể có được điều này chạy tốt nhưng ngày nay thức ăn có một mục với một ký tự dấu ba chấm trong mô tả và tôi nhận được lỗi sau:Làm thế nào để SQLAlchemy chèn chính xác một dấu chấm lửng unicode vào bảng mySQL?

UnicodeEncodeError: 'latin-1' codec không thể mã hóa ký tự u ' \ u2026 'ở vị trí 35: thứ tự không nằm trong phạm vi (256)

Nếu tôi thêm convert_unicode = Tùy chọn đúng vào công cụ, tôi có thể chèn đoạn mã vào nhưng dấu ba chấm không hiển thị nhân vật. Điều này có vẻ hợp lý vì theo hiểu biết tốt nhất của tôi thì không có dấu ba chấm nằm ngang trong latin-1. Ngay cả khi tôi đặt mã hóa thành utf-8 thì dường như nó không tạo ra sự khác biệt. Nếu tôi làm một chèn bằng cách sử dụng phpmyadmin và bao gồm dấu ba chấm nó đi qua tốt.

Tôi nghĩ rằng tôi không hiểu mã hóa ký tự hoặc cách để SQLAlchemy sử dụng cái tôi chỉ định. Có ai biết làm thế nào để có được các văn bản để đi vào mà không có ký tự lạ?

CẬP NHẬT

Tôi nghĩ rằng tôi đã figured này ra nhưng tôi không thực sự chắc chắn lý do tại sao nó quan trọng ...

Đây là mã:

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:[email protected]/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) 

Dưới đây là đầu ra/truy xuất lại từ chạy mã mà không có tùy chọn ký tự:

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) 

Vì vậy, có vẻ như thêm c harset đến chuỗi kết nối mysql đã làm nó. Tôi cho rằng nó mặc định là latin-1? Tôi đã thử thiết lập cờ mã hóa trên content_engine thành utf8 và điều đó không làm gì cả. Bất cứ ai biết tại sao nó sẽ sử dụng latin-1 khi các bảng và các lĩnh vực được thiết lập để utf8 unicode? Tôi cũng đã thử mã hóa mục ['description] bằng cách sử dụng .encode (' cp1252 ') trước khi gửi nó đi và làm việc cũng như không cần thêm tùy chọn bộ ký tự vào chuỗi kết nối. Điều đó không nên làm việc với latin-1 nhưng dường như nó đã làm? Tôi đã có giải pháp nhưng rất thích một câu trả lời :)

+0

Bạn có thể hiển thị mã bạn đang sử dụng để chèn mã không? Chuỗi có hình elip đến từ đâu? Có thông báo lỗi khi sử dụng utf-8 cũng nói "'latin-1' codec" không? – geoffspear

+0

Vui lòng cung cấp dữ liệu tạo ra sự cố. Nếu bạn có thể cung cấp mã mà bạn đang sử dụng thì sẽ rất có ích nếu bạn hiểu những gì bạn cố gắng làm. :) – Nilesh

+0

Tôi đã thêm mã ở trên chuỗi với dấu chấm lửng đến từ trang web hardocp.com. Dưới đây là một đoạn trích với dấu ba chấm: Microsoft đang tìm kiếm một số ít… ..tại đây. Tôi đã bao gồm mã của mình ở trên. – kvedananda

Trả lời

29

Thông báo lỗi

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

dường như chỉ ra rằng một số mã ngôn ngữ Python đang cố gắng để chuyển đổi nhân vật \u2026 thành một Latin-1 (ISO8859- 1) chuỗi, và nó không thành công. Không ngạc nhiên, nhân vật đó là U+2026 HORIZONTAL ELLIPSIS, không có ký tự tương đương nào trong ISO8859-1.

Bạn khắc phục vấn đề bằng cách thêm các truy vấn ?charset=utf8 trong SQLAlchemy bạn gọi kết nối:

import sqlalchemy 
from sqlalchemy import create_engine, MetaData, Table 

db = create_engine('mysql://user:[email protected]/db?charset=utf8') 

Phần Database Urls của tài liệu SQLAlchemy cho chúng ta biết rằng một URL bắt đầu với mysql chỉ ra một phương ngữ MySQL, sử dụng trình điều khiển mysql-python .

Phần sau, Custom DBAPI connect() arguments, cho chúng tôi biết rằng đối số truy vấn được chuyển đến DBAPI cơ bản.

Vì vậy, trình điều khiển mysql-python làm cho thông số {charset: 'utf8'} là gì? Mục Functions and attributes tài liệu của họ nói về thuộc tính charset "...Nếu có, bộ ký tự kết nối sẽ được thay đổi thành bộ ký tự này, nếu chúng không bằng nhau. "

Để tìm hiểu xem bộ ký tự kết nối có nghĩa là gì, chúng ta chuyển sang 10.1.4. Connection Character Sets and Collations của hướng dẫn tham khảo MySQL 5.6. dài câu chuyện ngắn, MySQL có thể giải thích các truy vấn đến như là một mã hóa khác với bộ ký tự của cơ sở dữ liệu, và khác với mã hóa của kết quả truy vấn được trả về. tin nhắn, tôi sẽ suy đoán rằng một cái gì đó trong SQLAlchemy hoặc mysql-python đang cố gắng để chuyển đổi các truy vấn đến một mã hóa kết nối mặc định của latin-1 trước khi gửi nó.Đây là những gì gây ra lỗi.trong các cuộc gọi connect() của bạn thay đổi mã hóa kết nối và U+2026 HORIZONTAL ELLIPSIS có thể vượt qua.

Cập nhật:. bạn cũng hỏi, "nếu tôi loại bỏ các tùy chọn charset và sau đó mã hóa .encode mô tả sử dụng ('cp1252') nó sẽ đi qua tốt như thế nào là một dấu chấm lửng có thể nhận được thông qua với cp1252 nhưng không unicode? "

encoding cp1252 has ký tự dấu ba chấm ngang theo giá trị byte \x85. Vì vậy, nó có thể mã hóa một chuỗi Unicode có chứa U+2026 HORIZONTAL ELLIPSIS vào cp1252 mà không có lỗi.

Cũng nhớ rằng trong Python, chuỗi Unicode và chuỗi byte là hai loại dữ liệu khác nhau. Đó là hợp lý để suy đoán rằng MySQLdb có thể có một chính sách gửi các chuỗi byte chỉ qua một kết nối SQL. Do đó, nó sẽ mã hóa một truy vấn nhận được dưới dạng chuỗi Unicode thành chuỗi byte, nhưng sẽ để lại một truy vấn nhận được dưới dạng chuỗi byte một mình. (Đây là suy đoán, tôi chưa xem mã nguồn.)

Trong traceback bạn đã đăng, hai dòng cuối cùng (gần nơi xảy ra lỗi) hiển thị tên phương thức literal, tiếp theo là unicode_literal. Điều đó có xu hướng hỗ trợ lý thuyết rằng MySQLdb đang mã hóa truy vấn mà nó nhận được dưới dạng chuỗi Unicode thành chuỗi byte.

Khi bạn mã hóa chuỗi truy vấn, bạn bỏ qua một phần của MySQLdb mà mã hóa này khác đi. Tuy nhiên, lưu ý rằng nếu bạn mã hóa chuỗi truy vấn khác với chuỗi ký tự kết nối MySQL, thì bạn sẽ có mã hóa không khớp và văn bản của bạn có thể sẽ được lưu trữ sai.

+0

Điều này gần như trả lời nó. Một điều là nếu tôi loại bỏ các tùy chọn ký tự và sau đó mã hóa các mô tả bằng cách sử dụng .encode ('cp1252') nó sẽ đi qua tốt. Làm thế nào là một dấu ba chấm có thể để có được thông qua với cp1252 nhưng không unicode? Tôi biết tôi đang thiếu một cái gì đó nhưng không chắc chắn nó là gì. – kvedananda

+0

Cảm ơn! Điều này bắt đầu có ý nghĩa ... – kvedananda

+0

+1 để chỉ ra "bạn đã khắc phục sự cố bằng cách thêm truy vấn? Charset = utf8 trong cuộc gọi kết nối SQLAlchemy của bạn" – btk

0

Thêm charset=utf8 trong chuỗi kết nối chắc chắn sẽ giúp, nhưng tôi gặp phải các tình huống trong Python 2.7 khi thêm convert_unicode=True vào create_engine cũng là cần thiết. Tài liệu SQLAlchemy nói nó chỉ để tăng hiệu suất, nhưng trong trường hợp của tôi nó thực sự giải quyết được vấn đề của bộ mã hóa sai đang được sử dụng.

Các vấn đề liên quan