Сбор из JSON и параметры Python-сценария
ctx.http() возвращает Response. Сначала проверьте статус, затем разбирайте JSON и проверяйте ожидаемую структуру. HTML-страница ошибки не должна превращаться в пустой каталог.
def run(ctx):
url = ctx.param('api_url', '')
if not url:
ctx.abort('Укажите адрес данных')
response = ctx.http().with_headers({'Accept': 'application/json'}).get(url)
if not response.ok():
ctx.fail('Источник вернул HTTP ' + str(response.status))
payload = response.json()
if not isinstance(payload, dict) or not isinstance(payload.get('items'), list):
ctx.fail('Ожидался объект с массивом items')
for item in payload['items']:
if not isinstance(item, dict):
ctx.fail('Элемент items должен быть объектом')
sku = str(item.get('sku') or '')
if not sku:
continue
ctx.emit({
'sku': sku,
'name': str(item.get('name') or ''),
'price': ctx.text().money(item.get('price')),
}, sku)
dict.get() не отличает отсутствие ключа от явного None, если не указать отдельный маркер. Если это важно, используйте 'price' in item. Не подменяйте нулевую цену значением по умолчанию через or: ноль может быть корректным значением. У числовых строк с валютой используйте money, а не float('1 250 ₽').
ctx.json(text) разбирает JSON-строку без сети. Можно применять разрешённый модуль json, например json.dumps() для тела POST. Заголовок Content-Type: application/json задайте через with_headers, тело передайте строкой. Python-словарь сам по себе не означает JSON-тело HTTP-запроса.
Секреты
Именованный секрет считывается через ctx.credential('site_token'), только в контексте разрешённого сайта. Не храните его в исходнике, параметре URL, журнале и записях. Токен выгрузки и секрет целевого сайта имеют разные назначения. Для выгрузки настройте токен пространства и его области доступа отдельно.