API Abfrage per Requests, anschließende XML Auswertung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Glare
User
Beiträge: 7
Registriert: Samstag 11. Januar 2020, 11:57

Hallo, bin recht neu bei Python und versuche mich gerade daran eine XML Datai aus dem Internet auszulesen.

Der Aufruf per requests funktioniert soweit, nur komme ich leider nicht mit dem auslesen der XML Datei klar und würde mich über Hilfestellung freuen.

Code: Alles auswählen

import requests
from xml.etree import ElementTree as ET

starturl = "http://webservice.rakuten.de/merchants/orders/getOrders"
param = dict(key="123456789a123456789a123456789a12")
resp = requests.get(url=starturl, params=param)

tree = ET.parse(resp.text)
root = tree.getroot()
Dies ist eine Testumgebung und gibt eine Bestellung aus. Mit print(resp.text) kann ich mir nun auch die komplette XML Datai ausgeeben lassen. Aber ich verstehe einfach nicht wie ich nun an einzelne Punkte komme, z.B. Kundendaten, Artikel, ...
Habe mir einige Seiten angeschaut und daher auch nicht sicher ob ich nun ElementTree, lxml oder SAX verwenden soll/muß.

Hoffe ihr habt ein paar Tipps für mich.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

XML Dateien kannst du mit BeautifulSoup4 parsen.
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

@Jankie: mit BeautifulSoup hast Du nur einen anderen Zugriff auf die Daten. Bei XML ist normalerweise ElementTree oder das äquivalent aus lxml die erste Wahl. Dann mit Zugriff auf die Elemente per XPath.

@Glare: kannst Du näher beschreiben, wo Du genau Probleme hast, was Du lesen willst und wie die XML-Datei aufgebaut ist?
Glare
User
Beiträge: 7
Registriert: Samstag 11. Januar 2020, 11:57

Sirius3 hat geschrieben: Montag 13. Januar 2020, 08:10 @Glare: kannst Du näher beschreiben, wo Du genau Probleme hast, was Du lesen willst und wie die XML-Datei aufgebaut ist?
Hallo Sirius3,

ich habe Probleme zu verstehen wie ich mit ElementTree an die Daten aus der XML komme.
Also ein kleiner Tipp wie ich z.B. an orders->order->invoice_no komme würde mir schon sehr helfen um alles andere auszulesen.

Hier der Link zur XMLhttps://webservice.rakuten.de/merchants ... 3456789a12
Benutzeravatar
__blackjack__
User
Beiträge: 14051
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Glare: Wo genau ist denn das Problem und was hast Du schon ausprobiert, und wie wich das dann von Deinem erwarteten Ergebnis ab?
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

@Sirius3: Ist nur die Art des Zugriffes anders oder wieso ist ElementTree dafür besser geeignet? Bisher habe ich alles mit BS4 und dem lxml Parser gemacht und finde das auch eigentlich sehr übersichtlich.

Hier mal als BS4 Beispiel:

Code: Alles auswählen

from bs4 import BeautifulSoup
import requests

URL = "https://webservice.rakuten.de/merchants/orders/getOrders?key=123456789a123456789a123456789a12"
HEADERS = {"User-Agent":"Dein User Agent"}

page = requests.get(URL, headers=HEADERS)
soup = BeautifulSoup(page.content, "lxml")

invoice_number = soup.find("invoice_no").get_text()

print(invoice_number)
Benutzeravatar
__blackjack__
User
Beiträge: 14051
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Jankie: Das würde ich mal sagen ist falsch weil das so für HTML ist, also sehr lax mit dem XML umgeht und da sogar Ergebnisse bringt wenn das XML kaputt ist. BS nimmt man eigentlich wegen der ganzen Abkürzungen für HTML die mit BS gehen weil BS HTML ”kennt”. Bei XML ist der Teil aber nicht sinnvoll. Da würde man einfach die ElementTree-API aus der Standardbibliothek mit der Untermenge von XPath verwenden die das implementiert, oder `lxml` und dann XPath.

Edit: Nur "invoice_no" wäre mir zu unsicher, es gibt doch einen schönen, eindeutigen Pfad zu der Information.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Hatte vorher auch den ganzen "Pfad" genommen, also soup.find("orders").find("order").find("invoice_no").get_text(), dachte aber das wäre überflüssig wenn invoice_no sowieso im gesamten XML nur einmal vorkommt.
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

@Jankie: die vielen find sind trotzdem falsch, weil sie ja auch über alle Ebenen hinweg suchen. Man will wirklich den Pfad /oders/oder/invoice_no gehen, weil das einfach im sichersten ist.
Benutzeravatar
__blackjack__
User
Beiträge: 14051
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Jankie: Weisst Du ja nicht das es nur einmal vorkommt. Bei XML hat man ja eine viel festere Struktur bei den Dokumenten, so das ein Tag auch woanders vorkommen kann und nicht verwechselt werden kann, weil da ja strukturell dann ein anderer Pfad hin führt. Und alleine drei solche `find()`-Aufrufe zu machen spricht doch schon gegen BS und für XPath.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Glare
User
Beiträge: 7
Registriert: Samstag 11. Januar 2020, 11:57

__blackjack__ hat geschrieben: Montag 13. Januar 2020, 12:44 @Glare: Wo genau ist denn das Problem und was hast Du schon ausprobiert, und wie wich das dann von Deinem erwarteten Ergebnis ab?
ich bin nach diesem Tutorial gegangen: https://riptutorial.com/de/python/examp ... lementtree

Code: Alles auswählen

import xml.etree.ElementTree as ET
import requests

starturl = "http://webservice.rakuten.de/merchants/orders/getOrders"
param = dict(key="123456789a123456789a123456789a12")
resp = requests.get(url=starturl, params=param)

tree = ET.parse(resp.text)
root = tree.getroot()

print(root[0][1].text)
Letzte Zeile ist natürlich nicht der richtige Pfad, wäre aber schon froh irgendeinen Eintrag aus der xml Datei zu erhalten. Anstelle kommt aber folgende Fehlermeldung:
Traceback (most recent call last):
File "s:/python/xml.py", line 1, in <module>
import xml.etree.ElementTree as ET
File "s:\python\xml.py", line 1, in <module>
import xml.etree.ElementTree as ET
ModuleNotFoundError: No module named 'xml.etree'; 'xml' is not a package
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

@__blackjack__: Wäre die Umsetzung mit ET und XPath so richtig?

Code: Alles auswählen

import xml.etree.ElementTree as ET


tree = ET.parse('getOrders.xml')
root = tree.getroot()

for order in root.findall("./orders/order/invoice_no"):
    print(order.text)
Benutzeravatar
__blackjack__
User
Beiträge: 14051
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Jankie: Kann man so machen. Ich würde `order` anders nennen, denn es steht ja nicht für ein <order>- sondern für ein <invoice_no>-Element.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

@Glare: Du hast in Deinem eigenen Verzeichnis eine Datei xml.py angelegt, die das Standardpaket xml überdeckt. Nenne diese Datei um und lösche eventuell vorhandene xml.pyc-Dateien.
Glare
User
Beiträge: 7
Registriert: Samstag 11. Januar 2020, 11:57

Sirius3 hat geschrieben: Montag 13. Januar 2020, 14:59 @Glare: Du hast in Deinem eigenen Verzeichnis eine Datei xml.py angelegt, die das Standardpaket xml überdeckt. Nenne diese Datei um und lösche eventuell vorhandene xml.pyc-Dateien.
Ok, Datei umbenannt, damit wäre der Fehler schonmal weg :-)


Nun kommt folgende Fehlermeldung wenn ich
tree = ET.parse(resp.text)
verwende:
Traceback (most recent call last):
File "s:/python/blablablub.py", line 8, in <module>
tree = ET.parse(resp.text)
File "C:\Program Files\Python38\lib\xml\etree\ElementTree.py", line 1202, in parse
tree.parse(source, parser)
File "C:\Program Files\Python38\lib\xml\etree\ElementTree.py", line 584, in parse
source = open(source, "rb")
OSError: [Errno 22] Invalid argument: '<?xml ver...
Danach noch der komplette Inhalt der XML

Nehme ich eine lokale Datei, also
tree = ET.parse('getOrders.xml')
, dann funktioniert es.

Muß ich (resp.text) vorher noch bearbeiten?
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Zum Programmieren gehört auch Dokumentation zu lesen und zu verstehen. .parse() erwartet laut Dokumentation einen Dateinamen, aus dem der Inhalt geparst wird. Du hast aber keine Datei, du hast einen String. Auf der verlinkten Dokumentationsseite steht auch eine Funktion um von einem String zu parsen.
Glare
User
Beiträge: 7
Registriert: Samstag 11. Januar 2020, 11:57

sparrow hat geschrieben: Montag 13. Januar 2020, 15:44 Zum Programmieren gehört auch Dokumentation zu lesen und zu verstehen. .parse() erwartet laut Dokumentation einen Dateinamen, aus dem der Inhalt geparst wird. Du hast aber keine Datei, du hast einen String. Auf der verlinkten Dokumentationsseite steht auch eine Funktion um von einem String zu parsen.
...source is a filename or file object...
Ich habe die Doku gelesen und für mich war es ein file object. Aber da habe ich mich wohl geirrt. Trotzdem danke für den Hinweis.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es ist ja auch ein file Objekt. Oder Dateiname. Was es NICHT ist, ist XML als String. Und den hast du da reingeworfenen.
Antworten