2012-01-15 24 views
5

Próbowałem przeanalizować kilka ogromnych plików XML, których nie ma w LXML, więc jestem zmuszony je przeanalizować przy pomocy xml.sax.Jak zwrócić dane z parsera Python SAX?

class SpamExtractor(sax.ContentHandler): 
    def startElement(self, name, attrs): 
     if name == "spam": 
      print("We found a spam!") 
      # now what? 

Problemem jest to, że nie rozumiem, jak to faktycznie return, albo lepiej, yield, rzeczy, że uchwyt ten znajdzie się rozmówcy, nie czekając na całego pliku do analizowany. Do tej pory robiłem sobie z tym problem z threading.Thread i Queue.Queue, ale to prowadzi do różnego rodzaju problemów z wątkami, które naprawdę odciągają mnie od rzeczywistego problemu, który próbuję rozwiązać.

Wiem, że mógłbym uruchomić parser SAX w oddzielnym procesie, ale uważam, że musi istnieć prostszy sposób na wyodrębnienie danych. Jest tu?

Odpowiedz

6

Myślałam daję to jako inną odpowiedź spowodowane to jest zupełnie inne podejście.

Możesz chcieć sprawdzić xml.etree.ElementTree.iterparse jak wydaje się zrobić więcej, co chcesz:

Analizuje sekcję XML do drzewa elementów stopniowo, i informuje, co się dzieje na użytkownika. source to nazwa pliku lub pliku zawierającego dane XML. events to lista zdarzeń do zgłoś. Jeśli zostanie pominięty, zgłaszane są tylko zdarzenia "końca". parser jest opcjonalną instancją analizatora składni. Jeśli nie podano, używany jest standardowy analizator składni XMLParser. Zwraca iterator zapewniający pary (zdarzenia, elemy).

Można wtedy napisać generator, który wykonuje iterator, robiąc to, co chcesz i uzyskując wartości, których potrzebujesz.

np:

def find_spam(xml): 
    for event, element in xml.etree.ElementTree.iterparse(xml): 
     if element.tag == "spam": 
      print("We found a spam!") 
      # Potentially do something 
      yield element 

Różnica polega w dużej mierze o tym, co chcesz. W przypadku iteratora ElementTree chodzi raczej o zbieranie danych, podczas gdy podejście SAX polega bardziej na działaniu na nim.

+2

+1, ale dodam: (1) użyj 'cElementTree', a nie' ElementTree' (2) 'lxml' ma również' iterparse', który zapewnia taką samą lub lepszą funkcjonalność (3), o której musisz wspomnieć usunięcie węzły po wyodrębnieniu wymaganych informacji (4) AFAICT (nigdy nie próbowałem) generator powinien działać OK –

+0

Śruba SAX, idę z 'iterparse'! Dzięki stosy! –

+0

@JohnMachin Nie wiedziałem, że cElementTree istnieje - oczywiście, tam, gdzie potrzebna jest szybkość, byłby to dobry wybór - ale nie widzę żadnego powodu, by sugerować, że jest to miejsce, gdzie prędkość nie jest priorytetem. Co do usuwania węzłów, nie widzę, gdzie to jest potrzebne, czy możesz wyjaśnić? - Wyjaśnione kilka sekund później przez larsmanów. –

0

Moje zrozumienie to parser SAX przeznaczony do wykonywania pracy, a nie tylko przekazywania danych z powrotem do łańcucha pokarmowego.

np:

class SpamExtractor(sax.ContentHandler): 
    def __init__(self, canning_machine): 
     self.canning_machine = canning_machine 

    def startElement(self, name, attrs): 
     if name == "spam": 
      print("We found a spam!") 
      self.canning_machine.can(name, attrs) 
+0

To jest właściwie ustawienie, które mam teraz, z 'Kolejką' zastępującą' canning_machine'. Ale tak naprawdę byłbym sposobem na umieszczenie znalezionych przedmiotów w generatorze, unikając wątków. (Zbieranie pozycji na liście nie jest opcją, lista nie mieści się w pamięci). –

+0

Chodzi mi o to, że tam gdzie masz pętlę przechodzącą przez generator, przenieś robione rzeczy w pętli do parsera SAX. Nie można utworzyć generatora, ponieważ wymagałoby to zmiany funkcji '' xml.sax.parse''. Zgadzam się, generator byłby pythonic podejście, ale myślę, że jesteś ograniczony przez '' xml.sax''. Zauważ, że nie zrobiłem zbyt wiele z SAX w Pythonie, więc być może brakuje mi świetnej sztuczki. –

0

Zasadniczo istnieją trzy sposoby analizowania XML:

  1. SAX, podejście: jest realizacja odwiedzający, chodzi o to, że wydarzenia są wypychane do kodu.
  2. StAX, podejście: gdzie można wyciągnąć następny element, tak długo, jak jesteś gotowy (przydatne do częściowego analizowania, czyli czytanie tylko nagłówek SOAP)
  3. DOM, podejścia, gdzie można załadować wszystko pod drzewem w pamięci

Wydaje się, że potrzebujesz drugiego, ale nie jestem pewien, czy jest on gdzieś w standardowej bibliotece.

+0

Aby wyjaśnić, dla SAX-a nie są to "zdarzenia" w sensie asynchronicznego zdarzenia/wiadomości, są to tylko metody klasy nadrzędnej, którą można zaimplementować i która będzie wywoływana przez nią podczas jej analizy. Można użyć parser SAX do wysyłania zdarzeń do kolejki. –

5

David Beazley demonstrates, jak "plastyczności" wynika z sax ContentHandler wykorzystujące współprogram:

cosax.py:

import xml.sax 

class EventHandler(xml.sax.ContentHandler): 
    def __init__(self,target): 
     self.target = target 
    def startElement(self,name,attrs): 
     self.target.send(('start',(name,attrs._attrs))) 
    def characters(self,text): 
     self.target.send(('text',text)) 
    def endElement(self,name): 
     self.target.send(('end',name)) 

def coroutine(func): 
    def start(*args,**kwargs): 
     cr = func(*args,**kwargs) 
     cr.next() 
     return cr 
    return start 

# example use 
if __name__ == '__main__': 
    @coroutine 
    def printer(): 
     while True: 
      event = (yield) 
      print event 

    xml.sax.parse("allroutes.xml", 
        EventHandler(printer())) 

Nad każdym razem self.target.send nazywany jest kod wewnątrz printer biegnie począwszy od event = (yield). event jest przypisany do argumentów self.target.send, a kod w printer jest wykonywany do następnego (yield) osiągnięty, podobnie jak działa generator.

Podczas gdy generator jest zwykle napędzany przez for-loop, współprogram (na przykład printer) jest sterowany przez wywołania send.