2016-07-11 2 views
2

Ich habe eine Flask-Anwendung, die ein XML-Dokument von einer URL abruft und verarbeitet. Ich verwende requests_cache mit redis, um zusätzliche Anfragen zu vermeiden, und ElementTree.iterparse, um über den gestreamten Inhalt zu iterieren. Hier ist ein Beispiel von meinem Code (gleiches Ergebnis aus tritt sowohl den Entwicklungsserver und den interaktiven Interpreter):ElementTree.iterparse mit gestreamter und zwischengespeicherter Anfrage wirft ParseError

>>> import requests, requests_cache 
>>> import xml.etree.ElementTree as ET 
>>> requests_cache.install_cache('test', backend='redis', expire_after=300) 
>>> url = 'http://myanimelist.net/malappinfo.php?u=doomcat55&status=all&type=anime' 
>>> response = requests.get(url, stream=True) 
>>> for event, node in ET.iterparse(response.raw): 
...  print(node.tag) 

den obigen Code Lauf einmal wirft einen ParseError:

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/xml/etree/ElementTree.py", line 1301, in __next__ 
    self._root = self._parser._close_and_return_root() 
    File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/xml/etree/ElementTree.py", line 1236, in _close_and_return_root 
    root = self._parser.close() 
xml.etree.ElementTree.ParseError: no element found: line 1, column 0 

jedoch genau die gleichen Lauf Code erneut, bevor der Cache abläuft, druckt tatsächlich das erwartete Ergebnis! Wie kommt es, dass die XML-Analyse nur beim ersten Mal fehlschlägt und wie kann ich das beheben?


Edit: Wenn es hilfreich ist, habe ich bemerkt, dass in einem anderen ParseError ohne die Cache-Ergebnisse mit dem gleichen Code ausgeführt wird:

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/xml/etree/ElementTree.py", line 1289, in __next__ 
    for event in self._parser.read_events(): 
    File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/xml/etree/ElementTree.py", line 1272, in read_events 
    raise event 
    File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/xml/etree/ElementTree.py", line 1230, in feed 
    self._parser.feed(data) 
xml.etree.ElementTree.ParseError: not well-formed (invalid token): line 1, column 0 
+0

Interessant, wenn Sie das tun würde 'ET.iterparse (StringIO (response.text))' statt, wäre es die ganze Zeit arbeiten, obwohl ich glaube, dass Sie eine haben Grund, '.raw' in diesem Fall zu verwenden. – alecxe

+0

@alecxe Hm, das scheint mir zu bedeuten, dass das Problem verursacht wird, weil ET versucht, ein Dokument zu analysieren, das nicht vollständig geladen ist ... Ich bin mir ziemlich sicher, dass es möglich ist, dies zu tun: http: // stackoverflow.com/questions/18308529/python-requests-package-handling-xml-response – Noah

+0

@alecxe, erste Ausführung Caching verbraucht die Daten, nicht Caching bedeutet, dass Sie gziped Daten übergeben, die Etree nicht analysieren kann –

Antwort

0

Ich kann Ihnen sagen, warum es für beiden Szenarien fehlschlägt, für die Letzteres ist es, weil die Daten gzipped das erste Mal, wenn Sie roh nennen, was auch immer passiert, wenn Sie das zweite Mal gelesen werden die Daten dekomprimiert:

Wenn Sie die Zeilen drucken:

for line in response.raw: 
    print(line) 

Sie sehen:

�=V���H�������mqn˫+i�������UȣT����F,�-§�ߓ+���G�o~�����7�C�M{�3D����೺C����ݣ�i�����SD�݌.N�&�HF�I�֎�9���J�ķ����s�*H�@$p�o���Ĕ�Y��v�����8}I,��`�cy�����gE�� �!��B� &|(^���jo�?�^,���H���^~p��a���׫��j� 

����a۱Yk<qba�RN6�����l�/�W����{/��߸�G 

X�LxH��哫 .���g(�MQ ����Y�q��:&��>s�M�d4�v|��ܓ��k��A17� 

Und dann Dekomprimieren:

import zlib 
def decomp(raw): 
    decompressor = zlib.decompressobj(zlib.MAX_WBITS | 16) 
    for line in raw: 
     yield decompressor.decompress(line) 

for line in decomp(response.raw): 
    print(line) 

Sie sehen die Dekompression funktioniert:

<?xml version="1.0" encoding="UTF-8"?> 
<myanimelist><myinfo><user_id>4731313</user_id><user_name>Doomcat55</user_name><user_watching>3</user_watching><user_completed>120</user_completed><user_onhold>8</user_onhold><user_dropped>41</user_dropped><user_plantowatch>2</user_plantowatch><user_days_spent_watching>27.83</user_days_spent_watching></myinfo><anime><series_animedb_id>64</series_animedb_id><series_title>Rozen Maiden</series_title><series_synonyms>; Rozen Maiden</series_synonyms><series_type>1</series_type><series_episodes>12</series_episodes><series_status>2</series_status><series_start>2004-10-08</series_start><series_end>2004-12-24</series_end><series_image>http://cdn.myanimelist.net/images/anime/2/15728.jpg</series_image> 
.................................. 

Jetzt nach Caching, wenn wir ein paar Bytes zu lesen:

response.raw.read(39) 

Sie sehen, wir dekomprimierte Daten zu erhalten:

<?xml version="1.0" encoding="UTF-8"?> 

vergessend Caching und das Bestehen der response.raw zu iterparse gibt:

raise e 
xml.etree.ElementTree.ParseError: not well-formed (invalid token): line 1, column 0 

Weil es nicht verarbeiten kann gzipped Daten.

Sie sich auch die folgenden auf dem ersten Lauf beim Caching:

for line in response.raw: 
    print(line) 

mich gibt:

ValueError: I/O operation on closed file. 

Das liegt daran, dass die Caching die Daten verbraucht hat, so in der Tat nichts da ist also nicht sicher, ob die Verwendung von Raw mit Caching tatsächlich möglich ist, da die Daten konsumiert werden und das Dateihandle geschlossen ist.

Wenn Sie lxml.fromstringlist:

import requests, requests_cache 
import lxml.etree as et 
requests_cache.install_cache() 

def lazy(resp): 
    for line in resp.iter_content(): 
     yield line 

url = 'http://myanimelist.net/malappinfo.php?u=doomcat55&status=all&type=anime' 

response = requests.get(url, stream=True) 

for node in et.fromstringlist(lazy(response)): 
    print(node) 
+0

Vielen Dank! Denken Sie also bei der ersten Anfrage, dass es möglich ist, die Antwort zu streamen und sie zu dekomprimieren/analysieren, ohne die gesamte Antwort auf einmal zu laden? – Noah

+0

Sie möchten den Stream zwischenspeichern und iterieren? –

+0

Ja. Das XML kann ziemlich groß werden, also versuche ich zu vermeiden, dass alles auf einmal gespeichert wird. – Noah

Verwandte Themen