Hallo Forum,
ich möchte einen vorher in eine MS-Access Tabelle geschriebenen json string auslesen und als rest-api-Anfrage an einen Server schicken. Wenn ich die Anfrage "direkt" an den Server schicke bekomme funktioniert es. Wenn ich denselben String erst in den Datensatz schreibe und dann 1:1 an den Server schicke bekomme ich ein authorisation failed zurück.
1. Warum möchte ich das machen:
Es sollen viele Datensätze abgeglichen werden, über eine "normale Internetverbindung" die auch gerne mal wegbrechen kann. Meine Idee ist erst den Anfragestring zu schreiben und dann die Antwort. Daran könnte ich erstens erkennen wenn es nur ein Problem mit der Verbindung war. Und zweitens habe ich nach dem Lauf in der temporären Tabelle Anfrage und Antwort zur Verfügung.
2. Mein background: In Python ist das mein erster Gehversuch. Ich kann grundsätzlich c für Mikrokontroller und habe auf Desktop-Systemen bisher nur in Visual Basic und eben C++ geschrieben. Leider habe ich überhaupt keine Erfahrung mit web requests und API Abfragen.
3. Ich habe mir den Code in Python so erstellt: Definition für Postman für die Schnittstelle runtergeladen. Postman erstellt Beispiel-Code, diesen genommen und in chatgpt anpassen lassen. Anhand der Dokumentation den code durchgearbeitet. Dann meine Problem in möglichst viele einzelne Unterschritte aufgeteilt und die Stück für Stück per code umgesetzt. Jetzt stecke ich an oben genannten Punkt fest.
4. Würde den Code gerne hier posten, aber im string und im code sind natürlich die login-Daten.... wenn ich die weg lasse verfälsche ich den string, lasse ich sie drin veröffentliche ich live-Zugangsdaten....
Wer wäre bereit mir zu helfen...
json string aus Datenbank auslesen
Du musst eben genau analysieren, wie die Daten sich veraendern zwischen den beiden Ansaetzen. Eine Vermutung waere, dass die in der Datenbank gespeicherten JSON-Daten kein JSON mehr sind. Mit der json-Bibliothek einfach mal versuchen zu parsen, und schauen, was da wie hakt.
Und um die Daten hier zu posten, einfach aus den login-Datein einen kleinen Teil umaendern, damit sie ungueltig werden. Steht da zb xABcc119, dann ersetzt das ABcc konsistent durch <removed>, damit wir das sehen koennen, was sich da *drumrum* (denn da werden die Probleme sein) geaendert hat.
Und um die Daten hier zu posten, einfach aus den login-Datein einen kleinen Teil umaendern, damit sie ungueltig werden. Steht da zb xABcc119, dann ersetzt das ABcc konsistent durch <removed>, damit wir das sehen koennen, was sich da *drumrum* (denn da werden die Probleme sein) geaendert hat.
Hallo,
da steht schon eine Menge Interna drin habe ich eben gemerkt. Also alles in einen Texteditor geladen und mit suchen/ersetzen hoffentlich alle wichtigen Daten gelöscht. Vorweg: Ich würde für Hilfe auch bezahlen wenn das hier für kostenlos zu aufwendig wird.
Der Code der mir eine Anfrage erstellt und den response ausgibt/in die DB schreibt:
Der Code der mir stattdessen nur den Anfragestring erstellt und in eine Tabelle schreibt (die Datenbank hat sich in der Zwischenzeit leicht geändert weil noch Testversion):
Das hier ist es was dann im Datensatz landet:
Und dagegen geschaut ist es das hier der json Anfrage string wenn ich Ihn per print aus der console ausgeben lasse (beim funktionierenden code):
da steht schon eine Menge Interna drin habe ich eben gemerkt. Also alles in einen Texteditor geladen und mit suchen/ersetzen hoffentlich alle wichtigen Daten gelöscht. Vorweg: Ich würde für Hilfe auch bezahlen wenn das hier für kostenlos zu aufwendig wird.
Der Code der mir eine Anfrage erstellt und den response ausgibt/in die DB schreibt:
Code: Alles auswählen
import json
import pyodbc
from itertools import islice
from datetime import datetime
import requests
# Variables
api_key = ""
customercode = "[deleted]861"
url = "https://api.[deleted]/customers/[deleted]861/inquiries"
headers = {
'Content-Type': 'application/json',
'Authorization': 'Basic MDA1OTc4NjErcmVzdEB0dmguY29tOm5UQ2VucjRBNjJ5MkUzSkZXcmdicU[deleted]==',
'Cookie': 'f5avraaaaaaaaaaaaaaaa_session_=DABML[DELETED]CDNIGLAEBGNIECFLFAFKJJJELGAMJKAKHIBFJCJDJHLKIFMIGNNJCEJBDLELKIOFJKKKKHGDAIOPLCGJFINEFOIKFIGOCGJJOKPFMEANMLIEBENHBKAGA; XSRF-TOKEN=NOT_USED'
}
# Variable to set the maximum number of articles to request
max_articles = 5000
# Variable to set the number of lines per JSON request
lines_per_request = 1
def make_request(url, headers, payload):
start_time = datetime.now()
response = requests.post(url, headers=headers, data=payload)
end_time = datetime.now()
print(f"Startzeit der Anforderung: {start_time}")
print(f"Endzeit der Anforderung: {end_time}")
print(f"Dauer der Anforderung: {end_time - start_time}")
if response.text.strip():
return response
else:
print("Leere Antwort erhalten.")
return None
def main():
try:
con_string = r'DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=C:\_python\[deleted]_api\[DELETED]_Abgleich.accdb;'
conn = pyodbc.connect(con_string)
cur = conn.cursor()
cur.execute('SELECT tbl_Bestell_Nr.Bestellnummer, First(tbl_MakeCodes.code) AS Make_Code FROM tbl_Bestell_Nr INNER JOIN tbl_MakeCodes ON tbl_Bestell_Nr.Lieferant_Marke = tbl_MakeCodes.descr GROUP BY tbl_Bestell_Nr.Bestellnummer;')
rows = cur.fetchall()
print(rows[:10])
rows = list(islice(rows, max_articles))
for i in range(0, len(rows), lines_per_request):
batch = list(islice(rows, i, i + lines_per_request))
lines = []
for index, row in enumerate(batch):
if row.Bestellnummer is not None and row.Make_Code != "#NV":
lines.append({
"lineNumber": index + 1,
"makeCode": row.Make_Code,
"partNumber": row.Bestellnummer,
"customerPartNumber": "Testanfrage Teil {}".format(index + 1),
"quantity": 1,
"text": "inquiry extra text{}".format(index + 1)
})
print(f"Angefragter Artikel: {row.Bestellnummer}, {row.Make_Code}")
payload = json.dumps({
"text": "Text abc def",
"customerquiryNumber": "Testanfrage inquiry",
"customerCode": "[deleted]861",
"customerContactName": "[deleted]",
"lines": lines
})
response = make_request(url, headers, payload)
if response is not None:
try:
response_data = response.json()
for item in response_data:
for line in item['lines']:
Preis = line.get('price')
makeCode = line.get('makeCode') # Get makeCode from the API response
if Preis is not None:
print(f"Antwort: Artikel {line['partNumber']}, makeCode: {makeCode} ,Preis: {Preis}")
cur.execute("""
INSERT INTO tbl_[deleted]_Preise (EKPreis, fid_Liefer, Datum, [DELETED]_Bestellnummer, makeCode, json_response)
VALUES (?, ?, ?, ?, ?, ?)
""", (Preis, 1, datetime.now(), line['partNumber'], makeCode, json.dumps(response_data))) # Insert makeCode into the database
conn.commit()
except json.decoder.JSONDecodeError as e:
print(f"Fehler beim Dekodieren der JSON-Antwort: {e}")
except pyodbc.Error as e:
print("Error in Connection to access database")
# Run the main function
main()
Code: Alles auswählen
import json
import pyodbc
DATABASE_PATH = r'C:\_python\[deleted]_api2\[DELETED]_Abgleich.accdb'
def get_data_from_cache(conn):
cur = conn.cursor()
cur.execute("SELECT Lieferant_Marke, Bestellnummer FROM tbl_cache")
rows = cur.fetchall()
cur.close()
return rows
def update_json_strings_in_cache(conn, updates):
cur = conn.cursor()
for lieferant_marke, bestellnummer, json_string in updates:
cur.execute("UPDATE tbl_cache SET json_string = ? WHERE Lieferant_Marke = ? AND Bestellnummer = ?", (json_string, lieferant_marke, bestellnummer))
conn.commit()
cur.close()
api_key = ""
customercode = "[deleted]861"
url = "https://api.[deleted]/customers/[deleted]861/inquiries"
con_string = f'DRIVER={{Microsoft Access Driver (*.mdb, *.accdb)}};DBQ={DATABASE_PATH};'
conn = pyodbc.connect(con_string)
data_from_cache = get_data_from_cache(conn)
updates = []
for index, row in enumerate(data_from_cache, 1):
line = {
"lineNumber": index,
"makeCode": row.Lieferant_Marke,
"partNumber": row.Bestellnummer,
"customerPartNumber": f"Testanfrage Teil {index}",
"quantity": 3,
"text": f"inquiry extra text{index}"
}
payload = {
"text": "Text abc def",
"customerInquiryNumber": "Testanfrage inquiry",
"customerCode": "[deleted]861",
"customerContactName": "[deleted]",
"lines": [line]
}
headers = {
'Content-Type': 'application/json',
'Authorization': 'Basic MDA1OTc4NjErcmVzdEB0dmguY29tOm5UQ2VucjRBNjJ5MkUzSkZXcmdicU[deleted]==',
'Cookie': 'f5avraaaaaaaaaaaaaaaa_session_=DABML[DELETED]CDNIGLAEBGNIECFLFAFKJJJELGAMJKAKHIBFJCJDJHLKIFMIGNNJCEJBDLELKIOFJKKKKHGDAIOPLCGJFINEFOIKFIGOCGJJOKPFMEANMLIEBENHBKAGA; XSRF-TOKEN=NOT_USED'
}
complete_request = {
"url": url,
"method": "POST",
"headers": headers,
"payload": payload
}
json_dump = json.dumps(complete_request, indent=4)
updates.append((row.Lieferant_Marke, row.Bestellnummer, json_dump))
# Wenn die Liste 'updates' 5000 Einträge erreicht, schreiben Sie sie in die Datenbank und leeren Sie die Liste
if len(updates) >= 5000:
update_json_strings_in_cache(conn, updates)
updates = []
# Schreiben Sie alle verbleibenden Updates in die Datenbank
if updates:
update_json_strings_in_cache(conn, updates)
conn.close()
Code: Alles auswählen
{
"url": "https://api.[deleted]/customers/[deleted]861/inquiries",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Basic MDA1OTc4NjErcmVzdEB0dmguY29tOm5UQ2VucjRBNjJ5MkUzSkZXcmdicU[deleted]==",
"Cookie": "f5avraaaaaaaaaaaaaaaa_session_=DABML[DELETED]CDNIGLAEBGNIECFLFAFKJJJELGAMJKAKHIBFJCJDJHLKIFMIGNNJCEJBDLELKIOFJKKKKHGDAIOPLCGJFINEFOIKFIGOCGJJOKPFMEANMLIEBENHBKAGA; XSRF-TOKEN=NOT_USED"
},
"payload": {
"text": "Text abc def",
"customerInquiryNumber": "Testanfrage inquiry",
"customerCode": "[deleted]861",
"customerContactName": "[deleted]",
"lines": [
{
"lineNumber": 2,
"makeCode": "400",
"partNumber": "0009249402",
"customerPartNumber": "Testanfrage Teil 2",
"quantity": 3,
"text": "inquiry extra text2"
}
]
Code: Alles auswählen
{"text": "Text abc def", "customerInquiryNumber": "Testanfrage inquiry", "customerCode": "[deleted]861", "customerContactName": "[deleted]", "lines": [{"lineNumber": 1, "makeCode": "400", "partNumber": "0009249378", "customerPartNumber": "Testanfrage Teil 1", "quantity": 3, "text":
Authorization ist hoffentlich nicht korrekt, denn da kann man noch so einiges rauslesen.
Konstanten werden komplett GROSS geschrieben.
Bei response sollte man den HTTP-Code prüfen mit `response.raise_for_status()`. Das deckt hoffentlich auch den Fall von leerem Text ab.
Über einen Index iteriert man nicht, und das was Du da mit islice machst, ist sehr gewöhnungsbedürftig.
Du willst Dir wahrscheinlich itertools.batched bzw. more_itertools.chunked anschauen.
Literale Strings wie der Wert von CustomerCode oder CustomerContactName sollten nicht mitten im Code stehen, sondern als Konstanten am Anfang der Datei.
Die Fehlerbehandlung mit None als Rückgabewert ist falsch, sondern man sollte den HTTPError abfangen.
Die except-Blöcke dienen bei Dir nur dazu, hilfreiche Informationen zum Fehler zu verschleiern, wenn Du nicht sinnvoll mit Exceptions umgehen kannst, dann lass es einfach weg.
Cookies kommen vom Server, und sollten nicht selbst gesetzt werden.
Ist ist komisch, dass Du für jede `line` den kompletten `response` in die Datenbank zu schreiben.
Das ganze könnte also so aussehen:
Warum scheibst Du in Deine temporäre Tabelle auch die Url und Headers?
Und der entscheidende Code fehlt: wie Du aus den Daten der temporären Tabelle dann den URL-Request machst.
Konstanten werden komplett GROSS geschrieben.
Bei response sollte man den HTTP-Code prüfen mit `response.raise_for_status()`. Das deckt hoffentlich auch den Fall von leerem Text ab.
Über einen Index iteriert man nicht, und das was Du da mit islice machst, ist sehr gewöhnungsbedürftig.
Du willst Dir wahrscheinlich itertools.batched bzw. more_itertools.chunked anschauen.
Literale Strings wie der Wert von CustomerCode oder CustomerContactName sollten nicht mitten im Code stehen, sondern als Konstanten am Anfang der Datei.
Die Fehlerbehandlung mit None als Rückgabewert ist falsch, sondern man sollte den HTTPError abfangen.
Die except-Blöcke dienen bei Dir nur dazu, hilfreiche Informationen zum Fehler zu verschleiern, wenn Du nicht sinnvoll mit Exceptions umgehen kannst, dann lass es einfach weg.
Cookies kommen vom Server, und sollten nicht selbst gesetzt werden.
Ist ist komisch, dass Du für jede `line` den kompletten `response` in die Datenbank zu schreiben.
Das ganze könnte also so aussehen:
Code: Alles auswählen
import json
import pyodbc
from itertools import batched
from datetime import datetime as DateTime
import requests
# Variables
API_KEY = ""
CUSTOMER_CODE = "[deleted]861"
CUSTOMER_NAME = "..."
URL = f"https://api.[deleted]/customers/{CUSTOMER_CODE}/inquiries"
AUTHORIZATION = "..."
HEADERS = {
'Authorization': f'Basic {AUTHORIZATION}',
}
MAX_ARTICLES = 5000
LINES_PER_REQUEST = 1
def make_request(url, headers, payload):
start_time = DateTime.now()
response = requests.post(url, headers=headers, json=payload)
end_time = DateTime.now()
print(f"Startzeit der Anforderung: {start_time}")
print(f"Endzeit der Anforderung: {end_time}")
print(f"Dauer der Anforderung: {end_time - start_time}")
response.raise_for_status()
return response.json()
def generate_lines(batch):
lines = []
for index, row in enumerate(batch, 1):
if row.Bestellnummer is not None and row.Make_Code != "#NV":
lines.append({
"lineNumber": index,
"makeCode": row.Make_Code,
"partNumber": row.Bestellnummer,
"customerPartNumber": f"Testanfrage Teil {index}",
"quantity": 1,
"text": f"inquiry extra text{index}",
})
print(f"Angefragter Artikel: {row.Bestellnummer}, {row.Make_Code}")
return lines
def main():
con_string = r'DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=C:\_python\[deleted]_api\[DELETED]_Abgleich.accdb;'
conn = pyodbc.connect(con_string)
cur = conn.cursor()
cur.execute('SELECT tbl_Bestell_Nr.Bestellnummer, First(tbl_MakeCodes.code) AS Make_Code FROM tbl_Bestell_Nr INNER JOIN tbl_MakeCodes ON tbl_Bestell_Nr.Lieferant_Marke = tbl_MakeCodes.descr GROUP BY tbl_Bestell_Nr.Bestellnummer;')
rows = cur.fetchall()
print(rows[:10])
for batch in batched(rows, LINES_PER_REQUEST):
payload = {
"text": "Text abc def",
"customerquiryNumber": "Testanfrage inquiry",
"customerCode": CUSTOMER_CODE,
"customerContactName": CUSTOMER_NAME,
"lines": generate_lines(batch),
}
response = make_request(URL, HEADERS, payload)
for item in response:
for line in item['lines']:
preis = line.get('price')
if preis is not None:
make_code = line.get('makeCode')
print(f"Antwort: Artikel {line['partNumber']}, makeCode: {make_code} ,Preis: {preis}")
cur.execute("""
INSERT INTO tbl_[deleted]_Preise (EKPreis, fid_Liefer, Datum, [DELETED]_Bestellnummer, makeCode, json_response)
VALUES (?, ?, ?, ?, ?, ?)
""", (preis, 1, DateTime.now(), line['partNumber'], make_code, json.dumps(response)))
conn.commit()
if __name__ == "__main__":
main()
Und der entscheidende Code fehlt: wie Du aus den Daten der temporären Tabelle dann den URL-Request machst.
Hallo,
ich habe in den cookie und authorization strings chaotisch Zeichen verändert. Wenn du sagst da kann man immer noch "relativ viel" rauslesen dann muss ich es editieren und entfernen... Bezüglich des ersten codes: Der war so lahm das ich damit versucht habe es schneller zu bekommen.
Das hier ist der Code der den string dann ausliest und damit eine Abfrage machen soll:
ich habe in den cookie und authorization strings chaotisch Zeichen verändert. Wenn du sagst da kann man immer noch "relativ viel" rauslesen dann muss ich es editieren und entfernen... Bezüglich des ersten codes: Der war so lahm das ich damit versucht habe es schneller zu bekommen.
Das hier ist der Code der den string dann ausliest und damit eine Abfrage machen soll:
Code: Alles auswählen
import pyodbc
import requests
import sys
import json
DATABASE_PATH = r'C:\_python\tvh_api2\TVH_Abgleich.accdb'
def send_request_and_update_response(start_id, end_id):
con_string = f'DRIVER={{Microsoft Access Driver (*.mdb, *.accdb)}};DBQ={DATABASE_PATH};'
conn = pyodbc.connect(con_string)
cur = conn.cursor()
# Datensätze basierend auf ID_BestellNr auslesen
cur.execute("SELECT ID_BestellNr, json_string FROM tbl_cache WHERE ID_BestellNr BETWEEN ? AND ?", (start_id, end_id))
rows = cur.fetchall()
for row in rows:
json_string = json.loads(row.json_string)
print(row.json_string)
request_data = requests.post(json_string['url'], headers=json_string['headers'], data=json_string['payload'])
print("Gesendeter Payload:", json.dumps(json_string['payload']))
response_data = request_data.text
# Antwort in die Datenbank schreiben
cur.execute("UPDATE tbl_cache SET json_response = ? WHERE ID_BestellNr = ?", (response_data, row.ID_BestellNr))
conn.commit()
cur.close()
conn.close()
if __name__ == "__main__":
#if len(sys.argv) != 3:
# print("Bitte geben Sie die Start- und End-ID_BestellNr als Parameter an.")
# sys.exit(1)
start_id = 1
end_id = 2
#start_id = int(sys.argv[1])
#end_id = int(sys.argv[2])
send_request_and_update_response(start_id, end_id)
Und warum glaubst Du, der andere Code wäre schneller? Das Langsame ist ja die Abfrage und die ändert sich ja nicht.
Cookies in der Datenbank zu speichern und mitzuschicken macht keinen Sinn, und auch die Authentication zu speichern ist eher nicht so sinnvoll.
Cookies in der Datenbank zu speichern und mitzuschicken macht keinen Sinn, und auch die Authentication zu speichern ist eher nicht so sinnvoll.