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ć?

zhuyxn
źródło
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ć

samwize
źródło
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.

Jamie
źródło
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
18

Spróbuj użyć .strip () na końcu linii, która line.strip()działała dla mnie dobrze

użytkownik3590113
źródło
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ść.

Ali Raza Bhayani
źródło
Dziękuję, metoda 1 była tym, czego szukałem.
Vasim
12

Spróbuj tego:

string.replace('\\xa0', ' ')
użytkownik278064
źródło
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.

Społeczność
źródło
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.

andilabs
źródło
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
8

Wypróbuj ten kod

import re
re.sub(r'[^\x00-\x7F]+','','paste your string here').decode('utf-8','ignore').strip()
siedmiodniowa żałoba
źródło
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ś.

dda
źródło
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
8bitjunkie
źródło
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)
znak
źródło
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)
ranaFire
źródło
-1

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

line = ' '.join(line.split())
Jonhy Beebop
źródło