Python запись XML данных из API на SQL сервер (с использованием синтаксического анализа в .csv в качестве промежуточного перехода) - PullRequest
0 голосов
/ 29 мая 2020

python здесь новичок.

Попытка извлечь данные из API и вставить в SQL таблицу сервера (существующую) для использования в инструменте BI.

Исходный результат из API XML с ужасно недружелюбным для новичков форматом.

Мне удалось разобрать это (вероятно, не в стиле pythoni c и открыто для предложений) в формате .csv (3 отдельных файла из-за характера вложенности XML). Теперь, когда у меня есть их в .csv, я пытаюсь записать их в свои таблицы SQL -Server, по одной таблице на .csv, но столкнулся с проблемой. Я использую код из , этот ответ , и все выглядит нормально, кроме начальной запятой, которая создается в разделе имен столбцов запроса. Кто-нибудь поможет мне удалить эту запятую?

Это код, который я написал в настоящее время:

import json
import requests
import pandas as pd
import csv
from pandas.io.json import json_normalize
from datetime import date, timedelta

url = "https://**myAPI_URL.com/Transaction"

paramHeader = '{"Version": "1.0"'
paramHeader += ', "FromDate":"2020-05-01 00:00"'
paramHeader += ', "ToDate": "2020-05-31 00:00"'
paramHeader += ', "MerchantOrgID": null'
paramHeader += ', "CardholderOrgID": null'
paramHeader += ', "CardNumber": null'
paramHeader += ', "DriverID": null'
paramHeader += ', "VehicleID": null'
paramHeader += ', "BillingGroupID": null'
paramHeader += ', "BillingCycleID": null'
paramHeader += ', "EntryMethodID": null'
paramHeader += ', "CardTypeID": null'
paramHeader += ', "TranTypeID": null'
paramHeader += ', "TaxExemptOnly": null}'

headers = {'APIKey': '**myAPI_KEY**'
    , 'content-type': 'application/json'
    , 'Accept': 'application/json'
    , 'parameters': paramHeader}

response = requests.get(url, data='', headers=headers)
if response.status_code == 200:
    r = json.loads(response.content.decode('utf-8'))

    cleanData = pd.json_normalize(r)

    transactionDetails = pd.json_normalize(data=r, record_path='Details', meta=['ID'])

    taxes = pd.json_normalize(data=r, record_path=['Details', 'Taxes'],
                                 meta=['ID'])

    cleanData.to_csv('**filePath**/mainTransactions.csv')
    transactionDetails.to_csv('**filePath**/transactionsDetails.csv')
    taxes.to_csv('**filePath/transactionsTaxes.csv')

    import pyodbc
    connection = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=**serverIP**;PORT=1433;DATABASE=myDBName;UID=myUserID;PWD=myPWord;')

    with open('**filePath**/transactionsDetails.csv', 'r') as f:
        reader = csv.reader(f)
        columns = next(reader)
        query = 'insert into transactionDetails({0}) values ({1})'
        query = query.format(','.join(columns), ','.join('?' * (int(len(columns)))))
        print(query)   #for debug purposes
        cursor = connection.cursor()
        for data in reader:
            cursor.execute(query, data)
        cursor.commit()

Этот код приводит к следующей ошибке:

insert into transactionDetails(,RowNumber,RawProductCode,RawUnitPrice,RawAmount,Quantity,ResolvedProductID,ProductCategoryID,ProductCategory,IsFuel,ProductName,ProductCode,IsTaxProduct,ResolvedUnitPrice,ResolvedAmount,Taxes,ID) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
Traceback (most recent call last):
  File "**workingDirectory**/myProject.py", line 85, in <module>
    cursor.execute(query, data)
pyodbc.ProgrammingError: ('42000', "[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Incorrect syntax near ','. (102) (SQLExecDirectW)")

Process finished with exit code 1

Выполнение того же запроса вручную (с использованием всех единиц в качестве тестовых данных) после удаления первой запятой приводит к успешной записи в БД.

myTableName> insert into transactionDetails(RowNumber,RawProductCode,RawUnitPrice,RawAmount,Quantity,ResolvedProductID,ProductCategoryID,ProductCategory,IsFuel,ProductName,ProductCode,IsTaxProduct,ResolvedUnitPrice,ResolvedAmount,Taxes,ID) values (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)
[2020-05-28 20:08:55] 1 row affected in 188 ms

Спасибо!

Ответы [ 2 ]

0 голосов
/ 29 мая 2020

Это было решено путем удаления безымянного столбца из .csv, который мой pandas df создал с помощью найденного здесь фрагмента :

with open('**filePath**/transactionsDetails.csv', 'r') as source:
            rdr = csv.reader(source)
            with open('**filePath**/transactionsDetails2.csv', 'w') as result:
                wtr = csv.writer(result)
                for r in rdr:
                    wtr.writerow((r[1], r[3], r[4],r[5],r[6],r[7],r[8],r[9],r[10],r[11],r[12],r[13],r[14],r[15],r[16]))

Полный рабочий код ниже:

import json
import requests
import pandas as pd
import csv
from pandas.io.json import json_normalize
from datetime import date, timedelta

url = "https://**myAPI**.com/Transaction"

paramHeader = '{"Version": "1.0"'
paramHeader += ', "FromDate":"2020-05-01 00:00"'
paramHeader += ', "ToDate": "2020-05-31 00:00"'
paramHeader += ', "MerchantOrgID": null'
paramHeader += ', "CardholderOrgID": null'
paramHeader += ', "CardNumber": null'
paramHeader += ', "DriverID": null'
paramHeader += ', "VehicleID": null'
paramHeader += ', "BillingGroupID": null'
paramHeader += ', "BillingCycleID": null'
paramHeader += ', "EntryMethodID": null'
paramHeader += ', "CardTypeID": null'
paramHeader += ', "TranTypeID": null'
paramHeader += ', "TaxExemptOnly": null}'

headers = {'APIKey': '**myAPIKey**'
    , 'content-type': 'application/json'
    , 'Accept': 'application/json'
    , 'parameters': paramHeader}

response = requests.get(url, data='', headers=headers)

if response.status_code == 200:
    r = json.loads(response.content.decode('utf-8'))

    cleanData = pd.json_normalize(r)
    transactionDetails = pd.json_normalize(data=r, record_path='Details', meta=['ID'])
    #print(transactionDetails)
    taxes = pd.json_normalize(data=r, record_path=['Details', 'Taxes'],
                                 meta=['ID'])

    cleanData.to_csv('**filePath**/mainTransactions.csv')
    transactionDetails.to_csv('**filePath**/transactionsDetails.csv')
    taxes.to_csv('**filePath**/transactionsTaxes.csv')

    import pyodbc
    connection = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=**serverIP**;PORT=1433;DATABASE=myDBName;UID=myUsername;PWD=myPword;')

    with open('**filePath**/transactionsDetails.csv', 'r') as source:
        rdr = csv.reader(source)
        with open('**filePath**/transactionsDetails2.csv', 'w') as result:
            wtr = csv.writer(result)
            for r in rdr:
                wtr.writerow((r[1], r[3], r[4],r[5],r[6],r[7],r[8],r[9],r[10],r[11],r[12],r[13],r[14],r[15],r[16]))

    with open('**filePath**/transactionsDetails2.csv', 'r') as f:
        reader = csv.reader(f)
        print(reader)
        columns = next(reader)
        query = 'insert into transactionDetails({0}) values ({1})'
        query = query.format(','.join(columns), ','.join('?' * (int(len(columns)))))
        print(query)
        cursor = connection.cursor()
        for data in reader:
            cursor.execute(query, data)
        cursor.commit()
    connection.close()

Интересно, есть ли у кого-нибудь более чистый способ сделать это?

0 голосов
/ 29 мая 2020

Начальная запятая просто вставлена, потому что первый элемент из вашего columns каким-то образом преобразуется в пустую строку. Если это согласуется, вы можете просто учесть это, нарезав столбцы:

# Just take the slice starting from the 1st element
# Also, no need to use int(len()), len() already returns an integer.
query = query.format(','.join(columns[1:]), ','.join('?' * len(columns[1:])))

Еще более простой способ выполнить описанное выше - сначала выполнить срез при получении столбцов для в первый раз.

columns = next(reader)[1:]
query = 'insert into transactionDetails({0}) values ({0})'
query = query.format(','.join(columns), ','.join('?' * len(columns)))

Некоторые дополнительные Python советы

Не структурируйте свой paramHeader путем объединения нескольких строк. Это очень грязно и опасно (подвержено опечаткам). Контент должен быть объектом json, поэтому просто создайте словарь и используйте модуль json для вывода правильно отформатированного объекта json:

>>> import json
>>> my_json = {
... "this": 123,
... "that": "string"
... }
>>> json.dumps(my_json)
'{"this": 123, "that": "string"}'

dumps означает «дамп». строка ".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...