Problem polega na tym, że dzwonisz encode na str obiekt.
str to ciąg bajtów, zwykle reprezentujący tekst zakodowany w taki sposób, jak UTF-8. Kiedy dzwonisz do encode w tym celu najpierw musi zostać zdekodowany z powrotem do tekstu, aby tekst mógł zostać ponownie zakodowany. Domyślnie Python robi to, wywołując s.decode(sys.getgetdefaultencoding()) i getdefaultencoding() zwykle zwraca 'ascii' .
Mówisz więc o tekście zakodowanym w UTF-8, dekodując go tak, jakby był to ASCII, a następnie ponownie kodując w UTF-8.
Ogólnym rozwiązaniem jest jawne wywołanie decode z właściwym kodowaniem, zamiast pozwolić Pythonowi na użycie domyślnego, a następnie encode wynik.
Ale kiedy właściwe kodowanie jest już tym, którego szukasz, łatwiejszym rozwiązaniem jest pominięcie .decode('utf-8').encode('utf-8') i po prostu użyj UTF-8 str jako UTF-8 str że już jest.
Lub, alternatywnie, jeśli twój wrapper MySQL ma funkcję pozwalającą ci określić kodowanie i odzyskać unicode wartości dla CHAR /VARCHAR /TEXT kolumny zamiast str wartości (np. w MySQLdb podajesz use_unicode=True do connect zadzwoń lub charset='UTF-8' jeśli twoja baza danych jest zbyt stara, aby ją automatycznie wykryć), po prostu to zrób. Wtedy będziesz mieć unicode obiektów i możesz wywołać .encode('utf-8') na nich.
Ogólnie rzecz biorąc, najlepszym sposobem radzenia sobie z problemami Unicode jest ten ostatni — zdekoduj wszystko tak wcześnie, jak to możliwe, wykonaj całe przetwarzanie w Unicode, a następnie zakoduj tak późno, jak to możliwe. Ale tak czy inaczej, musisz być konsekwentny. Nie dzwoń do str na czymś, co może być unicode; nie łącz str literał do unicode lub przekaż jeden do jego replace metoda; itp. Za każdym razem, gdy miksujesz i dopasujesz, Python dokona konwersji niejawnie, używając domyślnego kodowania, które prawie nigdy nie jest tym, czego chcesz.
Na marginesie, jest to jedna z wielu rzeczy, w których pomagają zmiany Unicode w Pythonie 3.x. Najpierw str jest teraz tekstem Unicode, a nie zakodowanymi bajtami. Co ważniejsze, jeśli masz zakodowane bajty, np. w bytes obiekt, wywołując encode da ci AttributeError zamiast próbować po cichu dekodować, aby mógł ponownie zakodować. Podobnie, próba mieszania i dopasowywania Unicode i bajtów da oczywisty TypeError , zamiast niejawnej konwersji, która w niektórych przypadkach się udaje i daje tajemniczą wiadomość o kodowaniu lub dekodowaniu, o które nie prosiłeś w innych.