Этот текст я написал для людей, которые как и я, ещё 3 месяца назад про Python только слышали. Для тех, кто неплохо знает английский, но иногда хочет простого русского «ща сделаем». Для тех, кто решил написать свой первый запрос для API Amazon и не понимает, почему ничего не работает.
Писать скрипт для Amazon SP-API — это как пытаться собрать мебель из IKEA без инструкции: сначала ты рад, что купил новинку, а потом мучаешься, пытаясь понять, как это вообще работает. Вернее, в данном случае инструкции есть, но по словам самой же поддержки Amazon она «не полностью отражает возможности сервиса».
В этой статье я расскажу, как выполнить самый простой запрос на получения токена, сформировать любой доступный отчет (зависит от вашего «статуса»), выгрузить и правильно его прочитать.
Небольшое отступление. Возможно, название некоторых переменных, функций или чего бы то ни было ещё, покажутся вам странными. Прошу отнестись с пониманием — я умею писать на C#, но опыта с Python у меня мало и он на уровне«посмотрел два четырехчасовых ролика на Youtube». Я уверен, что можно что-то оптимизировать, использовать библиотеки и так далее. Но мы рассматриваем первые шаги, которые лично мне было крайне сложно сделать, потому что желание помогать у поддержки Amazon отсутствует, как и внятная инструкция на русском языке (а часто и на английском тоже). Все обсуждают отдельные ошибки, а не работу скриптов в целом.
Подготовка
Начать, конечно, следует с инструкции и регистрации. Все подробно описано по ссылке. Тут разбирать что-то отдельно не имеет смысла, это не относится к нашему скрипту. На что стоит обратить внимание: даже если вам дали доступ с вашей личной почтой gmail, mail и т.п. зарегистрировать аккаунт надо на рабочую почту, иначе вы не пройдете одобрение от Amazon. Да, существует возможность создания приложений если вы сторонний разработчик, но там отдельные заморочки с получением ключей и доступов.
Следующий шаг — получить Restricted роли для работы с заказами. Эта роль позволит просматривать личные данные покупателя, например, ФИО и адрес. Для этого скрипта нам нужна только роль для составления отчетов (для вендоров это Brand Analytics). Но замечу, что ChatGPT неплохо справляется с ответами на анкету по ролям ?
После получения доступа картина выглядит так:
Отлично, жмем кнопку «Add new app client», заполняем название и тип API, указываем все доступные нам роли:
Из полученной записи нам понадобится несколько значений, а именно Client identifier и Client secret (они доступны, если нажать LWA credentials - View), а также Refresh Token (надо нажать стрелочку вниз рядом с кнопкой Edit App и выбрать вариант Authorise).
Шаг 1. Получение токена от Амазона
Около года назад Amazon внес некоторые изменения в свой API. Теперь нет необходимости настраивать AWS или получать дополнительный доступ — все доступно прямо из вашего кабинета Sellers или Vendors Central. Это просто великолепно. URL для получения токена для всех маркетплейсов один — https://api.amazon.com/auth/o2/token. Для начала импортируем все, что нам понадобится:
import requests
import json
import time
import gzip
import pandas as pd
Далее пишем первую функцию для запроса ключа:
def get_access_token():
url = 'https://api.amazon.com/auth/o2/token'
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
data = {
"grant_type": "refresh_token",
"refresh_token": "Atzr***", #Ваши данные из настроек
"client_id": "amzn1.***", #Ваши данные из настроек
"client_secret": "amz1.***" #Ваши данные из настроек
}
response = requests.post(url, headers=headers, data=data)
if response.status_code == 200:
return response.json()['access_token']
else:
raise Exception(f'Refresh token FAIL: {response.text}')
Ниже просто запускаем эту функцию:
access_token = get_access_token()
Можно ещё сделать print, чтобы проверить как она работает. На данном этапе в случае неудачи вы будете корректно получать сообщения об ошибке, система вполне адекватно ответит, если вы забыли какой-то ключ.
Шаг 2. Функция для запроса отчета
На Амазоне можно продавать по нескольким разным схемам. Я работаю в компании-вендоре, это B2B-сегмент. Наши отчеты сильно ограничены относительно стандартных селлеров, но всё же что-то сделать можно. Список всех отчетов тут. Я буду использовать GET_VENDOR_INVENTORY_REPORT. Подробнее про него можно почитать тут.
Приступим. Для начала нам понадобится URL, на который мы хотим отправить запрос. Их несколько, для евро-региона мы используем sellingpartnerapi-eu.amazon.com. Список доступен по ссылке.
Берем URL, добавляем к нему часть про отчеты и ID вашего маркетплейса, не забываем передать в функцию полученный токен из первого шага. Маркетплейсы можно глянуть либо в настройках, либо по ссылке.
def get_vendor_inventory_report(access_token):
url = f'https://sellingpartnerapi-eu.amazon.com/reports/2021-06-30/reports'
headers = {
'Content-Type': 'application/json',
'x-amz-access-token' : access_token
}
body = json.dumps({
'reportType': 'GET_VENDOR_INVENTORY_REPORT',
'marketplaceIds': ['A1PA6795UKMFR9'], #Здесь ваш Макретплейс ID
"reportOptions":{"reportPeriod": "DAY",
"distributorView": "MANUFACTURING",
"sellingProgram": "RETAIL"},
'dataStartTime': '2024-10-21',
'dataEndTime': '2024-10-21',
})
response = requests.post(url, headers=headers, data=body)
if response.status_code == 202:
print("Starting report preparations!")
reportId = response.json()['reportId']
return reportId
else:
print("Report FAIL", response.text)
return None
Дописываем запуск:
access_token = get_access_token()
reportId = get_vendor_inventory_report(access_token)
Обратите внимание на даты dataStartTime и dataEndTime. У Амазона есть требования к датам, например, нельзя указывать в качестве даты старта воскресенье или отчет можно запросить только за вчера. Читайте внимательно описание отчета.
Второе – тут мы только запрашиваем отчет у системы. Сам отчет мы будем получать попозже.
Отлично, теперь у нас есть reportID, который будем использовать в следующем шаге.
Шаг 3. Дожидаемся отчета
После запроса отчета из предыдущего шага, необходимо дождаться, когда он будет готов. Для этого пишем новую функцию:
def get_response_information(access_token, reportId):
url = f'https://sellingpartnerapi-eu.amazon.com/reports/2021-06-30/reports/' + reportId
headers = {
'Content-Type': 'application/json',
'x-amz-access-token' : access_token,
'x-amzn-RequestId' : reportId
}
response = requests.get(url, headers=headers)
while response.json()['processingStatus'] not in ['DONE', 'FATAL']:
print('Report in queue, stay tuned...')
print(response.json()['processingStatus'])
time.sleep(5)
response = requests.get(url, headers=headers)
status = response.json()['processingStatus']
print("Report status: ", status)
reportDocumentID = response.json()['reportDocumentId']
return reportDocumentID
В этой функции мы просто ожидаем, пока Амазон ответит нам. Возможно появление нескольких статусов, основные — это DONE и FATAL. Первый говорит о том, что наш запрос готов и его можно скачивать, второй — что что-то пошло не так. И вот тут кроется одна из проблем, которую я никак не мог решить. Дело в том, что ответ содержится в gzip архиве. В следующих шагах мы рассмотрим, как его прочитать, но надо учитывать, что ответ может быть в формате «Неправильно заданы даты» или «Запрос недействителен». Конкретную ошибку в запросе вам не напишут, только общую информацию. Добро пожаловать на гитхаб или реддит, там полно обсуждений ошибок отчетов. Но не будем о грустном, главное, что в конце мы получили ID готового документа. Дописываем выполнение и переходим к следующему шагу.
access_token = get_access_token()
reportId = get_vendor_inventory_report(access_token)
time.sleep(5)
reportDocumentID = get_response_information(access_token, reportId)
Замечу, что я тут добавил таймер сна на пять секунд, потому что можно столкнуться с ограничением в количестве запросов, отправляемых в API. Для нас он на уровне двух запросов в секунду, но я прочитал, что best practices — давать отдых в секунду после предыдущего запроса. В данном случае сделал пять.
Шаг 4. Получение ссылки на отчет
Снова отправляем запрос. На этот раз меняем ссылку, документы лежат в другом месте – reports/2021-06-30/documents:
def get_report_document(access_token, reportDocumentID):
url = f'https://sellingpartnerapi-eu.amazon.com/reports/2021-06-30/documents/' + reportDocumentID
headers = {
'Content-Type': 'application/json',
'Accept-Encoding': 'application/json',
'x-amz-access-token' : access_token,
'reportDocumentId' : reportDocumentID
}
response = requests.get(url, headers=headers)
documentUrl = response.json()['url']
return documentUrldef get_report_document(access_token, reportDocumentID):
url = f'https://sellingpartnerapi-eu.amazon.com/reports/2021-06-30/documents/' + reportDocumentID
headers = {
'Content-Type': 'application/json',
'Accept-Encoding': 'application/json',
'x-amz-access-token' : access_token,
'reportDocumentId' : reportDocumentID
}
response = requests.get(url, headers=headers)
documentUrl = response.json()['url']
return documentUrl
Независимо от статуса отчета, ссылку на документ вам отдадут. Тут я столкнулся с новый проблемой. Мы можем вывести эту ссылку в консоль, в небо или куда угодно, но при открытии ссылки будут только кривые символы. У ребят с Youtube, которых я смотрел, открывается в нормальном виде. Возможно, у них есть расширения для хрома или я просто не обладаю опытом, но без скачивания этого архива я его посмотреть не смог. Да, не забываем дописывать запуск:
access_token = get_access_token()
reportId = get_vendor_inventory_report(access_token)
time.sleep(5)
reportDocumentID = get_response_information(access_token, reportId)
time.sleep(5)
url = get_report_document(access_token, reportDocumentID)
Шаг 5. Скачиваем архив с нашими данными.
Тут обработка несложная. Мы просто открываем ссылку и пакуем всю информацию в архив. Работать с файлами пока особо не будем, просто укажем самое очевидное место:
def get_report_information(url, file):
file = "file.gzip"
try:
response = requests.get(url)
if response.status_code == 200:
with open(file, 'wb') as file:
file.write(response.content)
print(f'File ready!')
else:
print(f'Download file error: {response.status_code}')
except Exception as e:
print(f'ERROR: {e}')
Все, мы закончили. Все молодцы. А да, еще дописать запуск.
access_token = get_access_token()
reportId = get_vendor_inventory_report(access_token)
time.sleep(5)
reportDocumentID = get_response_information(access_token, reportId)
url = get_report_document(access_token, reportDocumentID)
time.sleep(5)
get_report_information(url)
Получив наконец свой долгожданный отчет, я задумался: а не стоит ли написать новый скрипт, который будет сообщать мне, когда заканчиваются запасы кофе у нас на кухне? В конце концов, если я уже прошел через ад Amazon SP-API. За этим простым скриптом стоит несколько дней изучения и плотной работы с различными источниками.
ЧаВо
Есть библиотека, почему ты не использовал ее?
Мне сложно использовать готовые библиотеки, когда я не понимаю до конца, как они работают. Готовая библиотека SP-API существует, но описанные в статье шаги она не сильно упростит.
Твой код некрасивый, ты не мог бы сделать иначе?
Мог бы и уже сделал. Но для человека, только начинающего свой путь в разработке на Питоне этого было достаточно. Но я совершенно не обижаюсь на код-ревью. Если что-то сильно бесит — обязательно напиши в комментах.
Почему не на английском?
Мой уровень позволяет обсуждать рабочие темы и вопросы с людьми из Германии, Франции и Италии. Но при подготовке этого простого скрипта я по 8 часов в день был погружен в английский. В какой-то момент я подумал, что было бы здорово иметь такую инструкцию на русском.
Ключи находятся в открытом доступе?
Для данного примера и первого запроса – да. Но в будущем все они перенесены в зашифрованный файл, мы рассматриваем простой запрос для начала.
Что дальше?
Дальше работа с файлами. Отчет можно прочитать, взять из него нужные данные по количеству и цене. Потом можно обновить все продукты другим скриптом и так далее. Все, что может позволить SP-API перед вам в инструкции.