2010-06-14 13 views
5

Pisałem niewielką funkcję, która wykorzystuje ElementTree i XPath, aby wyodrębnić zawartość tekstowych niektórych elementów w pliku XML:Python + Expat: Błąd na � podmiotów

#!/usr/bin/env python2.5 

import doctest 
from xml.etree import ElementTree 
from StringIO import StringIO 

def parse_xml_etree(sin, xpath): 
    """ 
Takes as input a stream containing XML and an XPath expression. 
Applies the XPath expression to the XML and returns a generator 
yielding the text contents of each element returned. 

>>> parse_xml_etree(
... StringIO('<test><elem1>one</elem1><elem2>two</elem2></test>'), 
... '//elem1').next() 
'one' 
>>> parse_xml_etree(
... StringIO('<test><elem1>one</elem1><elem2>two</elem2></test>'), 
... '//elem2').next() 
'two' 
>>> parse_xml_etree(
... StringIO('<test><null>&#0;</null><elem3>three</elem3></test>'), 
... '//elem2').next() 
'three' 
""" 

    tree = ElementTree.parse(sin) 
    for element in tree.findall(xpath): 
    yield element.text 

if __name__ == '__main__': 
    doctest.testmod(verbose=True) 

Trzeci test nie powiedzie się z następujących wyjątek:

ExpatError: odniesienie do nieprawidłowej liczby znaków: linia nr 1, w kolumnie 13

Czy &#0; jednostka nielegalne XML? Niezależnie od tego, czy tak jest, pliki, które chcę analizować, zawierają to, i potrzebuję jakiegoś sposobu na ich przeanalizowanie. Jakieś sugestie dla innego analizatora składni niż Expat, czy ustawienia dla Expata, które pozwoliłyby mi to zrobić?


Aktualizacja: Odkryłem BeautifulSoup właśnie teraz, parser tag zupa opisanych poniżej w komentarzu odpowiedzi, i dla zabawy Wróciłem do tego problemu i próbował użyć go jako XML-czyszczącego przed ElementTree , ale sumiennie przekonwertował &#0; na nieprawidłowy bajt zerowy. :-)

cleaned_s = StringIO(
    BeautifulStoneSoup('<test><null>&#0;</null><elem3>three</elem3></test>', 
        convertEntities=BeautifulStoneSoup.XML_ENTITIES 
).renderContents() 
) 
tree = ElementTree.parse(cleaned_s) 

... plony

xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 12 

W moim konkretnym przypadku jednak tak naprawdę nie potrzebują parsowania XPath jako takiego, mógłbym już z samego BeautifulSoup i jego dość prosty styl adresu węzła: parsed_tree.test.elem1.contents[0].

Odpowiedz

6

&#0; nie znajduje się w legal character range zdefiniowanej przez specyfikację XML. Niestety, moje umiejętności w Pythonie są dość szczątkowe, więc nie mam zbytniej pomocy.

+0

Hm, tak, specyfikacja czyni to całkiem jasne. Dziękuję za dokładne odniesienie. – clacke

+0

Rozumiem, że jest to stary wątek, ale specyfikacja mówi, co * literalne * znaki mogą pojawiać się tylko w XML. Bajtowa sekwencja � nie jest * dosłownie * znakiem pustym, ale 4-znakową sekwencją * reprezentującą * bajt zerowy. Biorąc pod uwagę to rozróżnienie, jest � legalne? Nie mogę znaleźć niczego w specyfikacji, która mówi, że * jest nielegalna. –

+1

Ważne pytanie. Ale odpowiedź jest tutaj: http://www.w3.org/TR/REC-xml/#sec-references mówi "Znaki, do których odnosi się użycie referencji do znaków MUSZĄ pasować do produkcji Char." – clacke

4

&#0; nie jest prawidłową postacią XML. Najlepiej byłoby, gdyby twórca pliku zmienił swój proces tak, aby plik nie był nieprawidłowy w ten sposób.

Jeśli musisz zaakceptować te pliki, możesz je wstępnie przetworzyć, aby zmienić &#0 w coś innego. Na przykład wybierz @ jako znak escape, zmień "@" na "@@", a "&#0;" na "@ 0".

Po uzyskaniu danych tekstowych z analizatora składni można odwrócić odwzorowanie. To tylko przykład, możesz wymyślić dowolną składnię, która ci się podoba.

+0

W moim konkretnym przypadku mogłem po prostu je usunąć. Są w nieistotnym elemencie XML. Czuje się niepewnie, używając przetwarzania tekstu do obsługi XML, ale ponieważ nie jest dobrze uformowany, myślę, że nie mam wyboru ... Używanie jakiegoś parsera zupa wydaje się przesadą. – clacke

+0

Czy jesteś pewien, że algorytm uciekający jest niezawodny? Czy nie musisz brać pod uwagę pierwszeństwo funkcji gramatyki XML? –

Powiązane problemy