Конвертировать массив в CSV, включая многострочное изображение

Я конвертирую массив в CSV, чтобы быстро импортировать элементы в Shopify. Согласно Shopify, вы должны сделать следующее, чтобы добавить несколько изображений при импорте:

  1. Вставьте новые строки (по одному на картинку).
  2. Скопируйте + вставьте "ручку".
  3. Скопируйте + вставьте URL изображения.

Таким образом, первое изображение идет в первом ряду, а все последующие изображения идут в строках ниже. Пример CSV находится здесь: https://help.shopify.com/csv/product_template.csv

Таким образом, я хотел бы запрограммировать что-то, что будет проходить через массив, который выглядит следующим образом (за исключением значительно более длинных), и конвертировать его в CSV, помещая все фотографии, кроме первой, в новую строку.

var array = [
  {
    "brief": "Brief 1",
    "description": "Description 1",
    "photos": [
      "https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example.jpg?0101010101010",
      "https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example2.jpg?0101010101010",
      "https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example3.jpg?0101010101010"
    ],
    "price": "145",
    "tags": [
      "tag1",
      "tag2",
      "tag3"
    ],
    "title": "Title 1"
  },
  {
    "brief": "Brief 2",
    "description": "Description 2",
    "photos": [
      "https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example4.jpg?0101010101010",
      "https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example5.jpg?0101010101010"
    ],
    "price": "150",
    "tags": [
      "tag4",
      "tag5",
      "tag6",
      "tag7",
      "tag8",
    ],
    "title": "Title 2"
  }
]

Обычно я делаю такие вещи с json2csv, который будет выглядеть примерно так, только я не уверен, как справиться с многострочным аспектом.

json2csv -i data_in.json -f title,description,price,etc -o data_out.csv

Python также может быть хорошим вариантом, как после этого поста, но опять же многострочный аспект сбивает с толку:

import csv
import json

x = """[
  {
    "brief": "Brief 1",
    "description": "Description 1",
    "photos": [
      "https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example.jpg?0101010101010",
      "https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example2.jpg?0101010101010",
      "https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example3.jpg?0101010101010"
    ],
    "price": "145",
    "tags": [
      "tag1",
      "tag2",
      "tag3"
    ],
    "title": "Title 1"
  },
  {
    "brief": "Brief 2",
    "description": "Description 2",
    "photos": [
      "https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example4.jpg?0101010101010",
      "https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example5.jpg?0101010101010"
    ],
    "price": "150",
    "tags": [
      "tag4",
      "tag5",
      "tag6",
      "tag7",
      "tag8",
    ],
    "title": "Title 2"
  }
]"""

x = json.loads(x)

f = csv.writer(open("example.csv", "wb+"))

# Write CSV Header, If you dont need that, remove this line
f.writerow(["title", "description", "price"])

for x in x:
    f.writerow([x["title"],
                x["description"],
                x["price"])

Верхние строки выходных данных CSV должны быть следующими (необходимо "Запустить фрагмент", чтобы увидеть таблицу). Обратите внимание, я не пытаюсь создать таблицу HTML:

<style type="text/css">
.tg  {border-collapse:collapse;border-spacing:0;}
.tg td{font-family:Arial, sans-serif;font-size:14px;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;}
.tg th{font-family:Arial, sans-serif;font-size:14px;font-weight:normal;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;}
.tg .tg-yw4l{vertical-align:top}
</style>
<table class="tg">
  <tr>
    <th class="tg-yw4l">Handle</th>
    <th class="tg-yw4l">Title</th>
    <th class="tg-yw4l">Body (HTML)</th>
    <th class="tg-yw4l">Vendor</th>
    <th class="tg-yw4l">Type</th>
    <th class="tg-yw4l">Tags</th>
    <th class="tg-yw4l">Published</th>
    <th class="tg-yw4l">Option1 Name</th>
    <th class="tg-yw4l">Option1 Value</th>
    <th class="tg-yw4l">Option2 Name</th>
    <th class="tg-yw4l">Option2 Value</th>
    <th class="tg-yw4l">Option3 Name</th>
    <th class="tg-yw4l">Option3 Value</th>
    <th class="tg-yw4l">Variant SKU</th>
    <th class="tg-yw4l">Variant Grams</th>
    <th class="tg-yw4l">Variant Inventory Tracker</th>
    <th class="tg-yw4l">Variant Inventory Qty</th>
    <th class="tg-yw4l">Variant Inventory Policy</th>
    <th class="tg-yw4l">Variant Fulfillment Service</th>
    <th class="tg-yw4l">Variant Price</th>
    <th class="tg-yw4l">Variant Compare At Price</th>
    <th class="tg-yw4l">Variant Requires Shipping</th>
    <th class="tg-yw4l">Variant Taxable</th>
    <th class="tg-yw4l">Variant Barcode</th>
    <th class="tg-yw4l">Image Src</th>
    <th class="tg-yw4l">Image Alt Text</th>
    <th class="tg-yw4l">Gift Card</th>
    <th class="tg-yw4l">Google Shopping / MPN</th>
    <th class="tg-yw4l">Google Shopping / Age Group</th>
    <th class="tg-yw4l">Google Shopping / Gender</th>
    <th class="tg-yw4l">Google Shopping / Google Product Category</th>
    <th class="tg-yw4l">SEO Title</th>
    <th class="tg-yw4l">SEO Description</th>
    <th class="tg-yw4l">Google Shopping / AdWords Grouping</th>
    <th class="tg-yw4l">Google Shopping / AdWords Labels</th>
    <th class="tg-yw4l">Google Shopping / Condition</th>
    <th class="tg-yw4l">Google Shopping / Custom Product</th>
    <th class="tg-yw4l">Google Shopping / Custom Label 0</th>
    <th class="tg-yw4l">Google Shopping / Custom Label 1</th>
    <th class="tg-yw4l">Google Shopping / Custom Label 2</th>
    <th class="tg-yw4l">Google Shopping / Custom Label 3</th>
    <th class="tg-yw4l">Google Shopping / Custom Label 4</th>
    <th class="tg-yw4l">Variant Image</th>
    <th class="tg-yw4l">Variant Weight Unit</th>
    <th class="tg-yw4l"></th>
    <th class="tg-yw4l"></th>
  </tr>
  <tr>
    <td class="tg-yw4l">Title 1</td>
    <td class="tg-yw4l">Title 1</td>
    <td class="tg-yw4l">Description 1</td>
    <td class="tg-yw4l">Vendor Name</td>
    <td class="tg-yw4l">Product Type</td>
    <td class="tg-yw4l">"tag1,tag2,tag3"</td>
    <td class="tg-yw4l">TRUE</td>
    <td class="tg-yw4l">Title 1</td>
    <td class="tg-yw4l">Default Title</td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l">1</td>
    <td class="tg-yw4l">deny</td>
    <td class="tg-yw4l">manual</td>
    <td class="tg-yw4l">145</td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l">TRUE</td>
    <td class="tg-yw4l">TRUE</td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l">https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example.jpg?0101010101010</td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
  </tr>
  <tr>
    <td class="tg-yw4l">Title 1</td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l">https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example2.jpg?0101010101010</td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
  </tr>
  <tr>
    <td class="tg-yw4l">Title 1</td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l">https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example3.jpg?0101010101010</td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
    <td class="tg-yw4l"></td>
  </tr>
</table>

1 ответ

Решение

Это не особенно сложно. Ваш код почти правильный (если вы укажете, что столбцы расположены так, как вы хотите, и если вы передаете правильный JSON, потому что у вас есть лишняя запятая), вам просто нужно добавить немного:

for x in x:
    images = x["photos"]
    f.writerow([x["title"],
                x["description"],
                x["price"],
                images.pop(0) if images else None])
    while images:
        f.writerow([None, None, None, images.pop(0)])

Короче говоря, это зацикливает все изображения по порядку и печатает их в новых, иначе пустых строках. Запустив его на свой оригинальный код, example.csv в конечном итоге выглядит так:

title,description,price
Title 1,Description 1,145,https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example.jpg?0101010101010
,,,https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example2.jpg?0101010101010
,,,https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example3.jpg?0101010101010
Title 2,Description 2,150,https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example4.jpg?0101010101010
,,,https://cdn.shopify.com/s/files/1/01/01/01/files/imgs-example5.jpg?0101010101010

Должно быть довольно очевидно, как поместить вещи в разные столбцы - Noneпросто так csv.writer оставляет ячейку пустой, поэтому используйте их в качестве заполнителей для всех остальных столбцов.

Если бы это было легко изменить, я бы рекомендовал другой подход. Это похоже на одноразовый сценарий преобразования, поэтому быстрый, легкий взлом будет работать просто отлично.

Несколько заметок:

  • a if b else c, если b оценивает True, средства a; если он оценивается в False, это значит c,
  • ary.pop(n) выскакивает (удаляет и возвращает) объект по индексу n от ary,
  • Пустой массив оценивается как False (следовательно, почему я тестирую только imagesне len(images) == 0)
  • Возможно, вы также захотите обновить заголовки. Я просто их как есть, так что мои единственные изменения были до последнего while; Вы, вероятно, захотите исправить их, чтобы точно пометить столбцы.
Другие вопросы по тегам