Как скреативить магазины Shopify с помощью Python

Упростите извлечение данных из Shopify, используя products.json и эффективные методы скраппинга.
5 мин. чтения
How to Scrape Shopify blog image

На первый взгляд, магазины Shopify представляют собой одну из самых сложных задач при извлечении данных. Приведенный ниже продукт представляет собой типичное объявление Shopify. Данные вложены друг в друга как попало.

<div class="site-box-content product-holder"><a href="/collections/ready-to-ship/products/the-eira-straight-leg" class="product-item style--one alt color--light   with-secondary-image " data-js-product-item="">

  <div class="box--product-image primary" style="padding-top: 120.00048000192001%"><img src="//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=640" alt="The Eira - Organic Ecru" srcset="//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=360 360w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=420 420w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=480 480w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=640 640w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=840 840w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=1080 1080w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=1280 1280w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=1540 1540w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=1860 1860w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=2100 2100w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=2460 2460w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-01_91c15dbe-7412-47b6-8f76-bdb434199203.jpg?v=1731517834&amp;width=2820 2820w" sizes="(max-width: 768px) 50vw, (max-width: 1024px) and (orientation: portrait) 50vw, 25vw " loading="lazy" class="lazy lazyloaded" data-ratio="0.8" width="3200" height="4000" onload="this.classList.add('lazyloaded')"><span class="lazy-preloader " aria-hidden="true"><svg class="circular-loader" viewBox="25 25 50 50"><circle class="loader-path" cx="50" cy="50" r="20" fill="none" stroke-width="4"></circle></svg></span></div><div class="box--product-image secondary" style="padding-top: 120.00048000192001%"><img src="//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=640" alt="The Eira - Organic Ecru" srcset="//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=360 360w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=420 420w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=480 480w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=640 640w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=840 840w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=1080 1080w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=1280 1280w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=1540 1540w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=1860 1860w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=2100 2100w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=2460 2460w,//hiutdenim.co.uk/cdn/shop/files/Hiut-EiraEcru-02.jpg?v=1731517834&amp;width=2820 2820w" sizes="(max-width: 768px) 50vw, (max-width: 1024px) and (orientation: portrait) 50vw, 25vw " loading="lazy" class="lazy lazyloaded" data-ratio="0.8" width="3200" height="4000" onload="this.classList.add('lazyloaded')"></div><div class="caption">

    <div>
      <span class="title"><span class="underline-animation">The Eira - Organic Ecru</span></span>
      <span class="price text-size--smaller"><span style="display:flex;flex-direction:row">$285.00</span></span>

    </div><quick-view-product class="quick-add-to-cart">
          <div class="quick-add-to-cart-button">
            <button class="product__add-to-cart" data-href="/products/the-eira-straight-leg" tabindex="-1">
              <span class="visually-hidden">Add to cart</span>
              <span class="add-to-cart__text" style="height:26px" role="img"><svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.57058 6.64336H4.49919C3.0296 6.64336 1.81555 7.78963 1.7323 9.25573L1.00454 22.0739C0.914352 23.6625 2.17916 25 3.77143 25H18.2286C19.8208 25 21.0856 23.6625 20.9955 22.0739L20.2677 9.25573C20.1844 7.78962 18.9704 6.64336 17.5008 6.64336H15.4294M6.57058 6.64336H15.4294M6.57058 6.64336V4.69231C6.57058 2.6531 8.22494 1 10.2657 1H11.7343C13.775 1 15.4294 2.6531 15.4294 4.69231V6.64336" stroke="var(--main-text)" style="fill:none!important" stroke-width="1.75"></path><path d="M10.0801 12H12.0801V20H10.0801V12Z" fill="var(--main-text)" style="stroke:none!important"></path><path d="M15.0801 15V17L7.08008 17L7.08008 15L15.0801 15Z" fill="var(--main-text)" style="stroke:none!important"></path></svg></span><span class="lazy-preloader add-to-cart__preloader" aria-hidden="true"><svg class="circular-loader" viewBox="25 25 50 50"><circle class="loader-path" cx="50" cy="50" r="20" fill="none" stroke-width="4"></circle></svg></span></button>
          </div>
        </quick-view-product></div><div class="product-badges-holder"></div></a></div>

Извлечь данные из приведенного выше HTML несложно, но есть и более простой способ.

Посадочные страницы Shopify

На целевой странице https://hiutdenim.co.uk/ есть информация о продукте, но она довольно скудная. Прокрутите страницу вниз достаточно далеко, и вы доберетесь до нее.

Первая страница магазина Shopify

На первый взгляд кажется, что вам нужно будет отсканировать каждую ссылку на каждый раздел, а затем получить и разобрать все эти различные страницы. Магазины Shopify не следуют традиционным методам, используемым в eCommerce-скреппинге, из-за уникальной компоновки страниц. Однако есть и другой способ.

Страницы Shopify JSON

Вы правильно прочитали этот заголовок. Мы можем получить все товары магазина в виде JSON-объекта по умолчанию. Нам даже не нужны BeautifulSoup или Selenium.

Нам просто нужно добавить /products.json к нашему URL. Каждый сайт Shopify построен на основе файла products.json.

Страница Shopify JSON

Если мы можем запросить это содержимое (а мы можем это сделать), то получим все данные, которые только можно пожелать. Получив их, мы должны решить, какие данные мы хотим сохранить. Вы можете проверить это на сайте, который мы использовали , здесь.

Скрапинг Shopify в Python

Теперь, когда мы знаем, что нам нужно, эта сложная задача становится гораздо менее трудной. Поскольку мы имеем дело только с данными JSON, нам нужно установить одну зависимость – Python Requests.

pip install requests

Индивидуальные функции

Давайте посмотрим на отдельные части кода. У нас есть три отдельных фрагмента, которые составляют скребок.

Это наша самая важная функция. Она фактически выполняет логику скраппинга.

def scrape_shopify(url, retries=2):
    """scrape a shopify store"""
    json_url = f"{url}products.json"
    items = []
    success = False
    while not success and retries > 0:
        response = requests.get(json_url)
        try:
            response.raise_for_status()
            products = response.json()["products"]
            for product in products:
                product_data = {
                    "title": product["title"],
                    "tags": product["tags"],
                    "id": product["id"],
                    "variants": product["variants"],
                    "images": product["images"],
                    "options": product["options"]
                }
                items.append(product_data)            
            success = True
        except requests.RequestException as e:
            print(f"Error during request: {e}, failed to get {json_url}")
        except KeyError as key_error:
            print(f"Failed to parse json: {key_error}")
        except json.JSONDecodeError as e:
            print(f"json error: {e}")
        except Exception as e:
            print(f"Unforeseen error: {e}")
        retries-=1
        print(f"Retries left: ", retries)
    return items
  • Сначала мы добавляем файл products.json к нашему url: json_url = f"{url}products.json".
  • Мы инициализируем пустой массив items. По мере того как мы будем перебирать элементы, мы будем добавлять их в этот массив. После завершения анализа мы вернем массив разобранных элементов.
  • Пока мы получаем хороший ответ, мы извлекаем ключ "продукты", чтобы получить все наши продукты.
  • Мы извлекаем различные данные из каждого продукта, чтобы создать дикту, product_data.
  • Данные о продукте добавляются в массив.
  • Этот процесс повторяется до тех пор, пока мы не разберем все продукты со страницы.

Теперь у нас есть функция, которая выполняет поиск и возвращает массив продуктов. Теперь нам нужна функция, которая возьмет этот массив товаров и запишет его в файл. Мы могли бы использовать CSV, но эта структура становится довольно вложенной, поэтому мы будем использовать JSON. Он поддерживает более гибкие структуры данных для последующего использования и анализа.

def json2file(json_data, filename):
    """save json data to a file"""
    try:
        with open(filename, "w", encoding="utf-8") as file:
            json.dump(json_data, file, indent=4)
            print(f"Data successfully saved: {filename}")
    except Exception as e:
        print(f"failed to write json data to {filename}, ERROR: {e}")

Это фактический код, который мы будем использовать. Теперь создадим главный блок для запуска нашего скрепера.

if __name__ == "__main__":
    shop_url = "https://hiutdenim.co.uk/"
    items = scrape_shopify(shop_url)

    json2file(items, "output.json")

Собираем все вместе

Когда мы собрали все это вместе, наш скрепер выглядит так. То, что казалось сложным проектом парсинга, теперь является полнофункциональным скрепером, который занимает всего около 50 строк кода.

import requests
import json

def json2file(json_data, filename):
    """save json data to a file"""
    try:
        with open(filename, "w", encoding="utf-8") as file:
            json.dump(json_data, file, indent=4)
            print(f"Data successfully saved: {filename}")
    except Exception as e:
        print(f"failed to write json data to {filename}, ERROR: {e}")

def scrape_shopify(url, retries=2):
    """scrape a shopify store"""
    json_url = f"{url}products.json"
    items = []
    success = False
    while not success and retries > 0:
        response = requests.get(json_url)
        try:
            response.raise_for_status()
            products = response.json()["products"]
            for product in products:
                product_data = {
                    "title": product["title"],
                    "tags": product["tags"],
                    "id": product["id"],
                    "variants": product["variants"],
                    "images": product["images"],
                    "options": product["options"]
                }
                items.append(product_data)            
            success = True
        except requests.RequestException as e:
            print(f"Error during request: {e}, failed to get {json_url}")
        except KeyError as key_error:
            print(f"Failed to parse json: {key_error}")
        except json.JSONDecodeError as e:
            print(f"json error: {e}")
        except Exception as e:
            print(f"Unforeseen error: {e}")
        retries-=1
    return items


if __name__ == "__main__":
    shop_url = "https://hiutdenim.co.uk/"
    items = scrape_shopify(shop_url)

    json2file(items, "output.json")

Данные о возвращении

Наши данные возвращаются в виде массива объектов JSON. Каждый продукт содержит список вариантов и изображений. Их было бы довольно сложно точно представить в CSV. Сниппет, который вы видите ниже, – это один-единственный продукт из нашей выборки.

{
        "title": "The Valerie - Organic Denim",
        "tags": [
            "The Valerie",
            "Women"
        ],
        "id": 14874183401848,
        "variants": [
            {
                "id": 54902462808440,
                "title": "UK10-29 / 30",
                "option1": "UK10-29",
                "option2": "30",
                "option3": null,
                "sku": null,
                "requires_shipping": true,
                "taxable": true,
                "featured_image": null,
                "available": true,
                "price": "220.00",
                "grams": 0,
                "compare_at_price": null,
                "position": 1,
                "product_id": 14874183401848,
                "created_at": "2025-01-21T14:04:58+00:00",
                "updated_at": "2025-02-12T17:17:54+00:00"
            },
            {
                "id": 54902462939512,
                "title": "UK12-30 / 32",
                "option1": "UK12-30",
                "option2": "32",
                "option3": null,
                "sku": null,
                "requires_shipping": true,
                "taxable": true,
                "featured_image": null,
                "available": true,
                "price": "220.00",
                "grams": 0,
                "compare_at_price": null,
                "position": 2,
                "product_id": 14874183401848,
                "created_at": "2025-01-21T14:04:58+00:00",
                "updated_at": "2025-02-12T17:17:54+00:00"
            },
            {
                "id": 54902463070584,
                "title": "UK14-32 / 28",
                "option1": "UK14-32",
                "option2": "28",
                "option3": null,
                "sku": null,
                "requires_shipping": true,
                "taxable": true,
                "featured_image": null,
                "available": true,
                "price": "220.00",
                "grams": 0,
                "compare_at_price": null,
                "position": 3,
                "product_id": 14874183401848,
                "created_at": "2025-01-21T14:04:58+00:00",
                "updated_at": "2025-02-12T17:17:54+00:00"
            },
            {
                "id": 54902463496568,
                "title": "UK18-36 / 30",
                "option1": "UK18-36",
                "option2": "30",
                "option3": null,
                "sku": null,
                "requires_shipping": true,
                "taxable": true,
                "featured_image": null,
                "available": true,
                "price": "220.00",
                "grams": 0,
                "compare_at_price": null,
                "position": 4,
                "product_id": 14874183401848,
                "created_at": "2025-01-21T14:04:58+00:00",
                "updated_at": "2025-02-12T17:17:54+00:00"
            }
        ],
        "images": [
            {
                "id": 31828166443078,
                "created_at": "2024-06-17T12:05:49+01:00",
                "position": 1,
                "updated_at": "2024-06-17T12:05:50+01:00",
                "product_id": 14874183401848,
                "variant_ids": [],
                "src": "https://cdn.shopify.com/s/files/1/0065/4242/files/HDC_0723_JapanInd_Valerie_45_3_c547ba8a-681b-4486-8cd7-884000e43302.jpg?v=1718622350",
                "width": 4000,
                "height": 4000
            },
            {
                "id": 31828166541382,
                "created_at": "2024-06-17T12:05:49+01:00",
                "position": 2,
                "updated_at": "2024-06-17T12:05:51+01:00",
                "product_id": 14874183401848,
                "variant_ids": [],
                "src": "https://cdn.shopify.com/s/files/1/0065/4242/files/HDC_0723_JapanInd_Valerie_Back_2_5909adb3-c2ab-4810-8b66-a486e8d827a8.jpg?v=1718622351",
                "width": 4000,
                "height": 4000
            },
            {
                "id": 31828166508614,
                "created_at": "2024-06-17T12:05:49+01:00",
                "position": 3,
                "updated_at": "2024-06-17T12:05:51+01:00",
                "product_id": 14874183401848,
                "variant_ids": [],
                "src": "https://cdn.shopify.com/s/files/1/0065/4242/files/HDC_0723_JapanInd_Valerie_Front_3_4316907a-9fd8-4649-894c-4028877370e1.jpg?v=1718622351",
                "width": 4000,
                "height": 4000
            },
            {
                "id": 31828166475846,
                "created_at": "2024-06-17T12:05:49+01:00",
                "position": 4,
                "updated_at": "2024-06-17T12:05:51+01:00",
                "product_id": 14874183401848,
                "variant_ids": [],
                "src": "https://cdn.shopify.com/s/files/1/0065/4242/files/HDC_0723_JapanInd_Valerie_Side_2_ea21477b-c1ba-4c8a-b75e-75c6427b4977.jpg?v=1718622351",
                "width": 4000,
                "height": 4000
            }
        ],
        "options": [
            {
                "name": "Waist",
                "position": 1,
                "values": [
                    "UK10-29",
                    "UK12-30",
                    "UK14-32",
                    "UK18-36"
                ]
            },
            {
                "name": "Leg Length",
                "position": 2,
                "values": [
                    "30",
                    "32",
                    "28"
                ]
            }
        ]
    },

Продвинутые техники

Мир не идеален, и вполне возможно, что вы столкнетесь с трудностями при работе с приведенным выше скребком. Вам может потребоваться скрести несколько страниц, или иногда ваш скребок может быть заблокирован.

Пагинация

При работе с большими магазинами часто встречаются магазины с постраничными результатами. Чтобы справиться с пагинацией, сначала нам нужно получить максимальное количество результатов на странице. Мы можем добавить следующий параметр запроса: page=, чтобы управлять страницами результатов.

Мы можем немного модифицировать нашу функцию скраппинга, чтобы она принимала страницу в URL и номер страницы.

def scrape_shopify(url, retries=2):
    """scrape a shopify store"""
    json_url = f"{url}products.json"

Затем мы можем скорректировать нашу основную часть, чтобы отразить эти изменения.

if __name__ == "__main__":
    shop_url = "https://www.allbirds.com/"
    PAGES = 3

    for page in range(PAGES):
        items = scrape_shopify(shop_url, page=page+1)

        json2file(items, f"page{page}output.json")

Интеграция прокси-сервера

Иногда вам может понадобиться использовать прокси-сервис, чтобы предотвратить блокировку вашего скрепера. С нашими прокси-серверами Shopify это просто – создайте URL с вашими учетными данными.

PROXY_URL = "http://brd-customer-<YOUR-USERNAME>-zone-<YOUR-ZONE>:<YOUR-PASSWORD>@brd.superproxy.io:33335"
proxies = {
    "http": PROXY_URL,
    "https": PROXY_URL
}
response = requests.get(json_url, proxies=proxies, verify="brd.crt")

Другие решения от Bright Data

Bright Data предлагает мощные альтернативы “под ключ”, которые избавляют от необходимости создавать сложные скреперы с нуля. Используйте наш полностью оптимизированный Shopify Scraper для беспрепятственного извлечения данных или обратитесь к обширной библиотеке предварительно собранных наборов данных, доступных в различных форматах, чтобы немедленно приступить к реализации своих проектов.

Заключение

Скраппинг магазина Shopify не обязательно должен быть невыполнимой задачей. Просто воспользовавшись их API с products.json, вы сможете быстро собрать большое количество подробных данных о товарах. Вам даже не нужно использовать парсер HTML! При желании вы можете сократить время разработки с помощью одного из наших готовых скреперов или сразу приступить к работе с нашими наборами данных.

Все наши продукты поставляются с бесплатной пробной версией, подпишитесь прямо сейчас!