2014-04-20 10 views
9

È risaputo che certain character ranges non è consentito nei documenti XML. Sono a conoscenza di soluzioni per filtrare questi caratteri (come [1], [2]).Modo centrale per filtrare i caratteri Unicode non validi in lxml?

Andando con il principio Non Repeat Yourself, preferirei di implementare una di queste soluzioni in un unico punto centrale - in questo momento, devo disinfettare qualsiasi testo potenzialmente non sicuro prima di essere inviato al lxml. C'è un modo per raggiungere questo obiettivo, ad es. sottoclassando una classe di filtro lxml, rilevando alcune eccezioni o impostando uno switch di configurazione?


Modifica: Per spera di chiarire la questione un po ', ecco un esempio di codice:

from lxml import etree 

root = etree.Element("root") 
root.text = u'\uffff' 
root.text += u'\ud800' 

print(etree.tostring(root)) 

root.text += '\x02'.decode("utf-8") 

L'esecuzione di questo dà il risultato

<root>&#65535;&#55296;</root> 

Traceback (most recent call last): 
    File "[…]", line 9, in <module> 
    root.text += u'\u0002' 
    File "lxml.etree.pyx", line 953, in lxml.etree._Element.text.__set__ (src/lxml/lxml.etree.c:44956) 
    File "apihelpers.pxi", line 677, in lxml.etree._setNodeText (src/lxml/lxml.etree.c:20273) 
    File "apihelpers.pxi", line 1395, in lxml.etree._utf8 (src/lxml/lxml.etree.c:26485) 
ValueError: All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control characters 

Come si vede, viene generata un'eccezione per il 2 byte, ma lxml sfugge felicemente agli altri due caratteri fuori intervallo. Il vero guaio è che

s = "<root>&#65535;&#55296;</root>" 
root = etree.fromstring(s) 

tiri anche un'eccezione. Questo comportamento è un po 'snervante a mio parere, soprattutto perché produce documenti XML non validi.


scopre che questo potrebbe essere un 2 vs 3 problema. Con python3.4, il codice precedente genera l'eccezione

Traceback (most recent call last): 
    File "[…]", line 5, in <module> 
    root.text += u'\ud800' 
    File "lxml.etree.pyx", line 953, in lxml.etree._Element.text.__set__ (src/lxml/lxml.etree.c:44971) 
    File "apihelpers.pxi", line 677, in lxml.etree._setNodeText (src/lxml/lxml.etree.c:20273) 
    File "apihelpers.pxi", line 1387, in lxml.etree._utf8 (src/lxml/lxml.etree.c:26380) 
UnicodeEncodeError: 'utf-8' codec can't encode character '\ud800' in position 1: surrogates not allowed 

L'unico problema e 'il carattere \uffff, che lxml ancora accetta felicemente.

+2

Forse questo dovrebbe essere risolto in lxml stesso. Hai inviato un bug al progetto lxml? – oefe

+1

@oefe non l'ho fatto. Ma sembra che questo sia un problema di 'libxml' (per il quale lxml è solo un wrapper) dato che PHP' DOMDocument' (un altro wrapper) sfugge anche caratteri fuori dall'intervallo e ha problemi a caricare tali documenti in seguito, quindi forse un bug la relazione dovrebbe essere meglio compilata lì. –

+1

come soluzione temporanea si potrebbe usare 'soupparser' che è fornito da lxml ' da lxml.html.soupparser import fromstring' e mangerà " & # 65535; & # 55296;" senza problemi. si basa sul parser di libxml2 – Urban48

risposta

1

Basta filtrare la stringa prima di analizzarla in LXML: cleaning invalid characters from XML (gist by lawlesst).

L'ho provato con il tuo codice; sembra funzionare, salvo il fatto che è necessario modificare la sostanza per importare con e sys!

from lxml import etree 
from cleaner import invalid_xml_remove 

root = etree.Element("root") 
root.text = u'\uffff' 
root.text += u'\ud800' 

print(etree.tostring(root)) 

root.text += invalid_xml_remove('\x02'.decode("utf-8")) 
Problemi correlati