Python: Usunięcie \ xa0 z ciągu?


241

Obecnie używam Beautiful Soup do parsowania pliku HTML i wywoływania get_text(), ale wygląda na to, że mam dużo \ xa0 znaków reprezentujących spacje. Czy istnieje skuteczny sposób na usunięcie ich wszystkich w Pythonie 2.7 i zamianę ich w spacje? Wydaje mi się, że bardziej ogólne pytanie brzmi: czy istnieje sposób na usunięcie formatowania Unicode?

Próbowałem użyć line = line.replace(u'\xa0',' '):, jak sugeruje inny wątek, ale zmieniło to \ xa0 na u, więc teraz zamiast tego mam wszędzie „u”. ):

EDIT: Problem wydaje się być rozwiązany str.replace(u'\xa0', ' ').encode('utf-8'), ale po prostu robi .encode('utf-8')bez replace()zdaje się powodować to wypluć nawet dziwacznych w znaki \ xc2 na przykład. Czy ktoś może to wyjaśnić?


próbowałem tego już, kodek 'ascii' nie może dekodować bajtu 0xa0 w pozycji 0: porządek poza zakresem (128)
zhuyxn

15
objąć Unicode. Użyj u''s zamiast ''s. :-)
jpaugh

1
próbowałem użyć str.replace (u '\ xa0', ''), ale dostałem „u” wszędzie zamiast \ xa0s: /
zhuyxn

Jeśli ciąg jest Unicode, musisz użyć u' 'zastępowania, a nie ' '. Czy oryginalny ciąg jest Unicode?
pepr

Odpowiedzi:


267

\ xa0 jest właściwie niełamiącą spacją w Latin1 (ISO 8859-1), również chr (160). Powinieneś zastąpić go spacją.

string = string.replace(u'\xa0', u' ')

Kiedy .encode ('utf-8'), koduje Unicode do utf-8, co oznacza, że ​​każda Unicode może być reprezentowana przez 1 do 4 bajtów. W tym przypadku \ xa0 jest reprezentowany przez 2 bajty \ xc2 \ xa0.

Przeczytaj na http://docs.python.org/howto/unicode.html .

Uwaga: ta odpowiedź z 2012 roku, Python przeszedł dalej, powinieneś być w stanie unicodedata.normalizeteraz korzystać


11
Nie wiem dużo o Unicode i kodowaniu znaków .. ale wydaje się, że unicodedata.normalize byłby bardziej odpowiedni niż str.replace
dbr

Twoja jest praktyczną radą dla ciągów, ale pamiętaj, że wszystkie odniesienia do tego ciągu również będą musiały zostać zastąpione. Na przykład, jeśli masz program, który otwiera pliki, a jeden z plików ma niezniszczalną przestrzeń w swojej nazwie, będziesz musiał zmienić nazwę tego pliku oprócz wykonania tej zamiany.
g33kz0r

1
U + 00a0 to nierozerwalny znak Unicode, który może być kodowany jako b'\xa0'bajt w kodowaniu Latin1, jako dwa bajty b'\xc2\xa0'w kodowaniu utf-8. Może być reprezentowany jak  w html.
jfs

3
Kiedy próbuję tego, rozumiem UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 397: ordinal not in range(128).
gwg

Utknąłem na 1 godzinę i wreszcie rozwiązałem. Wielkie dzięki.
Sadman Hasan

217

W unicodedatabibliotece Pythona znajduje się wiele przydatnych rzeczy . Jednym z nich jest .normalize()funkcja.

Próbować:

new_str = unicodedata.normalize("NFKD", unicode_str)

Zastąpienie NFKD jedną z innych metod wymienionych w powyższym linku, jeśli nie uzyskasz oczekiwanych rezultatów.


9
To jest genialne. To powinna być zaakceptowana odpowiedź.
Houman

2
Kompletnie się zgadzam. Proste, jasne, krótkie i praktyczne rozwiązanie. Kciuki w górę.
Billy Jhon,

2
Nie jestem pewien, możesz normalize('NFKD', '1º\xa0dia')zwrócić „1º dia”, ale zwraca „1o dia”
Faccion


1
ah, jeśli tekst to „KOREAN”, nie próbuj tego. 글자 가 전부 깨져 버리 네요.
Cho


15

Po wypróbowaniu kilku metod, aby to podsumować, tak to zrobiłem. Poniżej przedstawiono dwa sposoby unikania / usuwania znaków \ ​​xa0 z przeanalizowanego ciągu HTML.

Załóżmy, że mamy nieprzetworzony kod HTML w następujący sposób:

raw_html = '<p>Dear Parent, </p><p><span style="font-size: 1rem;">This is a test message, </span><span style="font-size: 1rem;">kindly ignore it. </span></p><p><span style="font-size: 1rem;">Thanks</span></p>'

Spróbujmy więc wyczyścić ten ciąg HTML:

from bs4 import BeautifulSoup
raw_html = '<p>Dear Parent, </p><p><span style="font-size: 1rem;">This is a test message, </span><span style="font-size: 1rem;">kindly ignore it. </span></p><p><span style="font-size: 1rem;">Thanks</span></p>'
text_string = BeautifulSoup(raw_html, "lxml").text
print text_string
#u'Dear Parent,\xa0This is a test message,\xa0kindly ignore it.\xa0Thanks'

Powyższy kod tworzy te znaki \ xa0 w ciągu. Aby usunąć je poprawnie, możemy użyć dwóch sposobów.

Metoda nr 1 (zalecana): pierwsza to metoda get_text firmy BeautifulSoup z argumentem strip jako True, więc nasz kod staje się:

clean_text = BeautifulSoup(raw_html, "lxml").get_text(strip=True)
print clean_text
# Dear Parent,This is a test message,kindly ignore it.Thanks

Metoda # 2: Inną opcją jest użycie biblioteki unicodedata biblioteki Pythona

import unicodedata
text_string = BeautifulSoup(raw_html, "lxml").text
clean_text = unicodedata.normalize("NFKD",text_string)
print clean_text
# u'Dear Parent,This is a test message,kindly ignore it.Thanks'

Opisałem również te metody na tym blogu, do których możesz się odnieść.


Dziękuję, metoda 1 była tym, czego szukałem.
Vasim

12

Spróbuj tego:

string.replace('\\xa0', ' ')

5
@RyanMartin: zastępuje cztery bajty : len(b'\\xa0') == 4ale len(b'\xa0') == 1. Jeśli to możliwe; powinieneś naprawić upstream, który generuje te ucieczki.
jfs

12

Natknąłem się na ten sam problem podczas pobierania danych z bazy danych sqlite3 za pomocą Pythona. Powyższe odpowiedzi nie działały dla mnie (nie wiem dlaczego), ale tak się line = line.decode('ascii', 'ignore')stało : moim celem było jednak usunięcie \ xa0, zamiast zastąpienia ich spacjami.

Otrzymałem to z tego bardzo pomocnego tutoriala o Unicode autorstwa Neda Batcheldera.


14
Usuwasz teraz wszystko, co nie jest postacią ASCII, prawdopodobnie maskujesz swój rzeczywisty problem. Używanie 'ignore'jest jak przerzucanie drążka zmiany biegów, nawet jeśli nie rozumiesz, jak działa sprzęgło.
Martijn Pieters

@MartijnPieters Połączony samouczek Unicode jest dobry, ale masz całkowitą rację - str.encode(..., 'ignore')jest odpowiednikiem obsługi Unicode try: ... except: .... Chociaż może ukryć komunikat o błędzie, rzadko rozwiązuje problem.
dbr

1
Wydaje się, że jest idealny do niektórych celów, takich jak EMAIL lub .decode('ascii', 'ignore')
adresy URL

1
Odpowiedź samwize nie zadziałała, ponieważ działa na ciągach znaków Unicode . line.decode()w twojej odpowiedzi sugeruje, że twoje wejście jest bajtowaniem (nie powinieneś wywoływać .decode()ciągu Unicode (aby go wymusić, metoda została usunięta w Pythonie 3). Nie rozumiem, w jaki sposób można zobaczyć samouczek, który masz połączone w swojej odpowiedzi i pomiń różnicę między bajtami a Unicode (nie mieszaj ich)
jfs

8

Skończyłem tutaj, przeglądając problem z postacią, której nie można wydrukować. Używam MySQL UTF-8 general_cii zajmuję się językiem polskim. W przypadku problematycznych ciągów muszę wykonać następujące czynności:

text=text.replace('\xc2\xa0', ' ')

Jest to po prostu szybkie obejście problemu i prawdopodobnie powinieneś spróbować czegoś z odpowiednią konfiguracją kodowania.


1
działa to, jeśli textjest to bajtowanie, które reprezentuje tekst zakodowany za pomocą utf-8. Jeśli pracujesz z tekstem; najpierw zdekoduj go do Unicode ( .decode('utf-8')) i zakoduj do bajtowania tylko na samym końcu (jeśli API nie obsługuje bezpośrednio Unicode np socket.). Wszystkie pośrednie operacje na tekście powinny być wykonywane w Unicode.
jfs


4

0xA0 (Unicode) to 0xC2A0 w UTF-8. .encode('utf8')po prostu weźmie twój Unicode 0xA0 i zastąpi 0xC2A0 UTF-8. Stąd pojawienie się 0xC2 ... Kodowanie nie zastępuje, jak zapewne teraz się zorientowałeś.


1
0xc2a0jest niejednoznaczny (kolejność bajtów). b'\xc2\xa0'Zamiast tego użyj literału bajtów.
jfs

3

Jest to odpowiednik znaku spacji, więc usuń go

print(string.strip()) # no more xa0

1

W Beautiful Soup możesz przekazać get_text()parametr strip, który usuwa białe znaki od początku i na końcu tekstu. Spowoduje to usunięcie \xa0lub dowolne inne białe znaki, jeśli wystąpią one na początku lub na końcu łańcucha. Piękna Zupa zastąpiła pusty sznurek \xa0i to rozwiązało problem.

mytext = soup.get_text(strip=True)

5
strip=Truedziała tylko wtedy, gdy &nbsp;jest na początku lub na końcu każdego bitu tekstu. Nie usunie spacji, jeśli znajduje się pomiędzy innymi znakami w tekście.
jfs

1

Wersja ogólna z wyrażeniem regularnym (usunie wszystkie znaki kontrolne):

import re
def remove_control_chart(s):
    return re.sub(r'\\x..', '', s)

-1

Python rozpoznaje go jako spację, więc możesz to splitzrobić bez argumentów i dołączyć normalną spacją:

line = ' '.join(line.split())
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.