2011-02-02 2 views
9

Sto provando a usare python (con pyquery e lxml) per modificare e ripulire alcuni html.Usare python per modificare html, ma lxml converte entità belle HTML in codifica strana

Eg. html = "<div><!-- word style><bleep><omgz 1,000 tags><--><p>It&#146;s a spicy meatball!</div>" 

La funzione lxml.html.clean, clean_html(), funziona bene, tranne che sostituisce le belle entità HTML come

&#146; 

con una certa stringa unicode

\xc2\x92 

Il unicode sembra strano in diversi browser (firefox e opera usando la codifica automatica, utf8, latin-1, ecc.), come una scatola vuota. Come posso impedire a lxml di convertire le entità? Come posso ottenere tutto con la codifica latin-1? Sembra strano che un modulo creato specificamente per html possa farlo.

io non posso essere sicuro di cui personaggi sono lì, quindi non posso semplicemente usare

replace("\xc2\x92","&#146;"). 

Ho provato con

clean_html(html).encode('latin-1') 

ma l'unicode persiste.

E sì, mi piacerebbe dire alla gente di smettere di usare la parola per scrivere codice HTML, ma poi mi piacerebbe sentire tutta la

"iz th Wayz i liks è u cant mak me chang hitlr".

Edit: una soluzione BeautifulSoup:

from BeautifulSoup import BeautifulSoup, Comment 
soup = BeautifulSoup(str(desc[desc_type])) 
        comments = soup.findAll(text=lambda text:isinstance(text, Comment)) 
        [comment.extract() for comment in comments] 
        print soup 
+0

Curioso: Perché non stai usando BeautifulSoup – inspectorG4dget

+2

@inspector: A quanto mi risulta, BeautifulSoup non è più in fase di sviluppo attivo, e gli utenti sono incoraggiati a cercare altre soluzioni (come lxml). –

+0

@SethJohnson: non lo sapevo. Grazie per avermi detto – inspectorG4dget

risposta

2

presumo &#146; si suppone che sia un segno di virgolette. L'oggetto str con un valore di byte 146, chr(146), decodificato con cp1252 è una virgoletta:

In [46]: print(chr(146).decode('cp1252')) 
’ 

Quindi, si potrebbe fare questo:

import lxml.html.clean as clean 
import re 

html = "<div><!-- word style><bleep><omgz 1,000 tags><--><p>It&#146;s a spicy meatball!</div>" 

html=re.sub('&#(\d+);',lambda m: chr(int(m.group(1))).decode('cp1252'),html) 
print(html) 
# <div><!-- word style><bleep><omgz 1,000 tags><--><p>It’s a spicy meatball!</div> 
print(type(html)) 
# <type 'unicode'> 
print(clean.clean_html(html)) 
# <div><p>It’s a spicy meatball!</p></div> 

Oppure,

doc=lh.fromstring(html) 
clean.clean(doc) 

Si noti che le virgolette hanno il valore del punto codice unicode 8217. Cioè, ord(chr(146).decode('cp1252')) è uguale a 8217, quindi lh.tostring restituisce:

print(lh.tostring(doc)) 
# <div><p>It&#8217;s a spicy meatball!</p></div> 

Si potrebbe ri-codificarlo in CP1252 come questo:

print(repr(lh.tostring(doc,encoding='cp1252'))) 
# '<div><p>It\x92s a spicy meatball!</p></div>' 

Non so come coassiale lxml per tornare

'<div><p>It&#146;s a spicy meatball!</p></div>' 

per abbinare l'uscita del codice BeautifulSoup , però. Beh, chiaramente si potrebbe fare con regex (invertendo ciò che ho fatto sopra), ma non so se sia necessario o consigliabile, dal momento che lxml dovrebbe già tornare html che altre applicazioni possono comprendere.

result=re.sub('&#(\d+);',lambda m: '&#{n};'.format(
    n=ord(unichr(int(m.group(1))).encode('cp1252'))), 
      lh.tostring(doc)) 
print(result) 
# <div><p>It&#146;s a spicy meatball!</p></div> 
+0

Questo è un uso pro di regex! – jathanism

8

ci sono alcune cose che - se li conosci - porteranno alla migliore soluzione più semplice /:

  • clean_html() restituisce lo stesso tipo che fornisci con: se gli date un stringa, restituirà una stringa, ma se gli dai un Element o ElementTree, restituirà un Element o ElementTree rispettivamente

  • puoi controllare il modo in cui un elemento o ElementTree viene serializzato, dando le opzioni di codifica aMetodoo il metodo write() dell'albero (lo stesso vale per xml). Puoi farlo con encoding='utf-8' per esempio.

  • qualsiasi contenuto che può essere codificato in tale codifica, verrà emesso come una stringa codificata, qualsiasi contenuto che non può essere "sfuggito" come entità. L'utilizzo di encoding="ascii" forzerà qualsiasi carattere non-ascii a entità "belle" come desideri.

Mettere insieme, questo significa: prima analizzare la stringa in un elemento (o un albero, se lo si desidera), pulirlo, e serializzare in base alle esigenze:

html = lxml.html.fromstring("<div><!-- word style><bleep><omgz 1,000 tags><--><p>It&#146;s a spicy meatball!</div>") 
html = clean_html(html) 
result = lxml.html.tostring(html, encoding="ascii") 

(e un trucco un po 'più sporco è utilizzare il parametro errors sul metodo encode() di una stringa unicode: provare a codificare una stringa unicode contenente caratteri "speciali" con s.encode('ascii', 'xmlcharrefreplace') e vedere cosa fa ...)

1

Si potrebbe anche solo convertire la stringa utf8 in ascii con caratteri xml

result = result.decode('utf-8').encode('ascii', 'xmlcharrefreplace')