Наполнять каталог вручную дорого и долго, особенно когда у поставщика тысячи позиций, а цены и остатки меняются каждую неделю. Решение — парсер для 1С-Битрикс: программа, которая собирает товары с сайта поставщика (или из его прайса) и заливает их в ваш магазин с названиями, описаниями, фото, свойствами и, что критично, с правильно пересчитанными ценами.
В этой статье разберём, чем отличаются редакции «1С-Битрикс» с точки зрения парсинга, какие есть готовые решения, и напишем собственный парсер, который собирает данные с сайта поставщика и параллельно конвертирует валюту — причём задействуя встроенный в Битрикс модуль валют.
Почему редакция и архитектура Битрикса важны
В отличие от многих CMS, в Битриксе категорически не рекомендуется писать товары напрямую в базу: структура хранения сложная (инфоблоки, торговый каталог, цены, склады, торговые предложения), и прямой INSERT почти гарантированно сломает индексы и бизнес-логику. Правильный путь — работать через API Битрикса. Поэтому для парсера важна не столько «версия ядра», сколько редакция продукта и то, какие модули в ней доступны.
«1С-Битрикс: Управление сайтом» поставляется в пяти основных редакциях — «Старт», «Стандарт», «Малый бизнес», «Бизнес» и «Энтерпрайз». Полноценный интернет-магазин и нужные парсеру модули появляются начиная с «Малого бизнеса».
«Старт» и «Стандарт» — для контентных сайтов. Модуля «Торговый каталог» в базовой комплектации нет, полноценный магазин без доработок не собрать. Парсить сюда товары как товары каталога негде.
«Малый бизнес» — первая «магазинная» редакция. Содержит модули «Интернет-магазин» и «Торговый каталог», поддерживает интеграцию с «1С» и Яндекс.Маркет. Работает с одним типом цены и одним складом — этого достаточно для парсера среднего магазина.
«Бизнес» — расширенный магазин: несколько типов цен (опт/розница), многоскладовость, накопительные скидки, наборы и комплекты. Если вы парсите оптовые и розничные цены или раскладываете остатки по складам, парсер должен уметь это записывать — а значит, нужна именно «Бизнес».
«Энтерпрайз» — для крупных высоконагруженных проектов. Логика для парсера та же, что в «Бизнес», но добавляются требования к производительности при массовом импорте.
Отдельно стоит «1С-Битрикс24: Интернет-магазин + CRM» — это другой продукт (магазин завязан на CRM-сущности и каталог CRM). Парсер под него пишется иначе, чем под «Управление сайтом», — учитывайте это, если у вас именно Битрикс24.
Ещё несколько вещей, которые парсер обязан понимать про Битрикс:
- Инфоблоки (infoblocks) — товары хранятся как элементы инфоблока каталога, разделы инфоблока — это категории.
- Торговые предложения (SKU / offers) — варианты товара (цвет, размер) лежат в отдельном связанном инфоблоке. Если у поставщика есть вариации, парсер должен создавать и сам товар, и его торговые предложения.
- Модуль валют — в Битриксе есть встроенный модуль валют, который умеет автоматически подтягивать курсы ЦБ РФ по агенту. Это удобно: конвертацию можно делать средствами самого Битрикса, не изобретая свой источник курса.
- CommerceML — стандартный XML-формат обмена с «1С». Через него же работает штатный импорт каталога.
Практический вывод: перед разработкой зафиксируйте редакцию, ID инфоблока каталога, схему свойств и то, используются ли торговые предложения и несколько типов цен. От этого зависит вся логика импорта.
Готовые решения: что есть на рынке
Если магазин типовой, возможно, своё писать и не придётся.
Штатные средства Битрикса
В «Малом бизнесе» и «Бизнес» из коробки есть импорт каталога:
- Импорт из CommerceML — основной канал обмена с «1С»: товары, цены, остатки, свойства. Если поставщик умеет отдавать CommerceML — это самый «родной» способ.
- Импорт из CSV — «Торговый каталог → Экспорт/Импорт». Принимает подготовленный CSV с товарами, ценами и свойствами.
Сами по себе они не «ходят» по чужим сайтам — им нужен готовый файл. Поэтому их обычно ставят в пару к парсеру: парсер собирает CSV/XML, штатный импорт его заливает. Это самый безопасный для ядра вариант.
Модули из Маркетплейса 1С-Битрикс
Это расширения, которые и парсят, и грузят результат в инфоблоки прямо из админки:
- «Загрузка товаров, парсер сайтов, импорт» (
kitnet.import) — парсит сайты прямо из админки и добавляет товары в инфоблок: загрузка из всех категорий, автосоздание разделов и свойств, многопоточность, импорт CSV. Разработчики предлагают также подключение по API поставщика. - «Сотбит: Парсер контента» (
shs.parser) — модуль от Sotbit: интеграция номенклатуры поставщиков с магазином из сайта-каталога, Excel, XML, YML, CSV; загрузка в инфоблоки и highload-блоки, работа через прокси, логирование, демо-режим с ограничениями.
Совет: у большинства таких модулей есть демо-режим (обычно несколько товаров / страниц). Обязательно протестируйте его на вашем конкретном доноре до покупки — далеко не все сайты парсятся «из коробки».
Внешние программы
- Datacol — десктопная программа с десятками готовых настроек под популярные магазины. Собирает данные с сайтов, прайсов и маркетплейсов, обрабатывает (перевод, уникализация, наценка) и грузит в Битрикс полуавтоматически (через Excel + штатный импорт) или автоматически.
Когда готовое не подходит? Нестандартная вёрстка или защита донора, своя логика наценки, конвертация валют по своему курсу, привязка к вашей схеме свойств и торговых предложений, регулярная синхронизация остатков. Тогда пишут парсер под задачу — к нему и переходим.
Пример 1: парсер на PHP через API Битрикса (с конвертацией валюты)
Поскольку Битрикс написан на PHP, самый правильный парсер — это PHP-скрипт, который подключает ядро Битрикса и пишет товары через его API. Конвертацию валюты сделаем средствами встроенного модуля валют, а наценку применим сами.
Код учебный. Перед запуском на боевом магазине сделайте резервную копию, убедитесь, что парсинг донора не нарушает его правил и закон, и протестируйте на копии.
Шаг 0. Подключение ядра и модулей
Скрипт запускается из консоли или по cron. Он бутстрапит Битрикс и подключает нужные модули.
<?php
// чтобы скрипт работал из консоли/крона, а не только из веб-контекста
$_SERVER['DOCUMENT_ROOT'] = '/var/www/bitrix';
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
define('BX_NO_ACCELERATOR_RESET', true);
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
use Bitrix\Main\Loader;
Loader::includeModule('iblock'); // инфоблоки (каталог)
Loader::includeModule('catalog'); // торговый каталог (цены, остатки)
Loader::includeModule('currency'); // модуль валют (курсы ЦБ)
Шаг 1. Конвертер валюты на встроенном модуле
Битрикс уже хранит и (по агенту) обновляет курсы ЦБ РФ. Берём курс из модуля валют через CCurrencyRates::ConvertCurrency, наценку добавляем отдельно.
<?php
class CurrencyConverter
{
public function __construct(
private string $from = 'USD',
private string $to = 'RUB',
private float $markup = 1.20 // наценка магазина: +20%
) {}
/** Цена поставщика -> цена магазина по курсу Битрикса + наценка. */
public function convert(?float $amount): ?float
{
if ($amount === null) {
return null;
}
// ConvertCurrency сам берёт актуальный курс из модуля валют
$converted = \CCurrencyRates::ConvertCurrency($amount, $this->from, $this->to);
return round($converted * $this->markup, 2);
}
}
Шаг 2. Парсер карточек поставщика
Для разбора HTML используем встроенные DOMDocument + DOMXPath и cURL — без сторонних библиотек. Селекторы условные: под каждый донор их подбирают индивидуально.
<?php
class SupplierParser
{
public function __construct(
private string $baseUrl,
private CurrencyConverter $converter,
private float $delay = 1.0
) {}
private function getHtml(string $url): string
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 20,
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; CatalogImporter/1.0)',
]);
$html = curl_exec($ch);
if ($html === false) {
throw new RuntimeException('cURL: ' . curl_error($ch));
}
curl_close($ch);
return $html;
}
private function xpath(string $html): DOMXPath
{
$doc = new DOMDocument();
libxml_use_internal_errors(true);
$doc->loadHTML('<?xml encoding="UTF-8">' . $html);
libxml_clear_errors();
return new DOMXPath($doc);
}
private function absolute(string $href): string
{
return str_starts_with($href, 'http')
? $href
: rtrim($this->baseUrl, '/') . '/' . ltrim($href, '/');
}
/** Ссылки на товары со страниц каталога с пагинацией. */
public function parseCatalog(string $path, int $maxPages = 5): array
{
$urls = [];
for ($page = 1; $page <= $maxPages; $page++) {
$xp = $this->xpath($this->getHtml($this->absolute("{$path}?page={$page}")));
$links = $xp->query("//div[contains(@class,'product-card')]//a[contains(@class,'product-link')]");
if ($links->length === 0) {
break;
}
foreach ($links as $a) {
$urls[] = $this->absolute($a->getAttribute('href'));
}
usleep((int)($this->delay * 1_000_000));
}
return $urls;
}
/** Разбор одной карточки. */
public function parseProduct(string $url): array
{
$xp = $this->xpath($this->getHtml($url));
$text = fn(string $q) => trim($xp->query($q)->item(0)?->textContent ?? '');
$name = $text("//h1[contains(@class,'product-title')]") ?: 'Без названия';
$sku = $text("//*[contains(@class,'sku')]");
$descr = $text("//*[contains(@class,'product-description')]");
$priceRaw = $text("//*[contains(@class,'price')]//*[contains(@class,'value')]");
$imgNode = $xp->query("//*[contains(@class,'product-gallery')]//img")->item(0);
$imageUrl = $imgNode ? $this->absolute($imgNode->getAttribute('src')) : '';
$priceSource = null;
if ($priceRaw !== '') {
$digits = preg_replace('/[^0-9.]/', '', str_replace(',', '.', $priceRaw));
$priceSource = $digits !== '' ? (float)$digits : null;
}
usleep((int)($this->delay * 1_000_000));
return [
'name' => $name,
'sku' => $sku,
'description' => $descr,
'image_url' => $imageUrl,
'price_source' => $priceSource,
'price' => $this->converter->convert($priceSource), // конвертация
'source_url' => $url,
];
}
}
Шаг 3. Импорт в каталог через API Битрикса
Здесь — главное отличие от других CMS: пишем не в базу напрямую, а через CIBlockElement (элемент инфоблока), CCatalogProduct (карточка торгового каталога) и CPrice (цена). Картинку скачиваем через CFile::MakeFileArray. Дубли проверяем по внешнему коду (XML_ID).
<?php
class BitrixImporter
{
public function __construct(
private int $iblockId, // ID инфоблока каталога
private int $sectionId, // раздел (категория) по умолчанию
private string $priceCurrency = 'RUB'
) {}
/** Ищем товар по внешнему коду, чтобы не плодить дубли. */
private function findByXmlId(string $xmlId): ?int
{
$res = \CIBlockElement::GetList(
[],
['IBLOCK_ID' => $this->iblockId, 'XML_ID' => $xmlId],
false, false, ['ID']
);
$row = $res->Fetch();
return $row ? (int)$row['ID'] : null;
}
public function import(array $p): int
{
$el = new \CIBlockElement();
$xmlId = $p['sku'] !== '' ? $p['sku'] : md5($p['source_url']);
$fields = [
'IBLOCK_ID' => $this->iblockId,
'IBLOCK_SECTION_ID' => $this->sectionId,
'NAME' => $p['name'],
'XML_ID' => $xmlId, // внешний код = артикул донора
'DETAIL_TEXT' => $p['description'],
'DETAIL_TEXT_TYPE' => 'html',
'ACTIVE' => 'Y',
];
// картинку скачиваем и привязываем (если донор отдаёт)
if ($p['image_url'] !== '') {
$file = \CFile::MakeFileArray($p['image_url']);
if ($file) {
$fields['DETAIL_PICTURE'] = $file;
}
}
$existingId = $this->findByXmlId($xmlId);
if ($existingId) {
// товар уже есть — обновляем (без перезаписи картинки)
unset($fields['DETAIL_PICTURE'], $fields['XML_ID']);
$el->Update($existingId, $fields);
$productId = $existingId;
} else {
$productId = $el->Add($fields);
if (!$productId) {
throw new RuntimeException('CIBlockElement: ' . $el->LAST_ERROR);
}
}
// карточка торгового каталога (остаток)
\CCatalogProduct::Add([
'ID' => $productId,
'QUANTITY' => 100,
'AVAILABLE' => 'Y',
]);
// базовая цена (уже сконвертированная и с наценкой)
if ($p['price'] !== null) {
\CPrice::SetBasePrice($productId, $p['price'], $this->priceCurrency);
}
return (int)$productId;
}
}
Шаг 4. Точка входа и запуск по cron
<?php
// после блока подключения ядра (Шаг 0) и классов выше:
$converter = new CurrencyConverter('USD', 'RUB', 1.25);
$parser = new SupplierParser('https://supplier-example.com', $converter, 1.0);
$importer = new BitrixImporter(iblockId: 12, sectionId: 47, priceCurrency: 'RUB');
$urls = $parser->parseCatalog('/catalog/category-1', maxPages: 3);
echo 'Найдено товаров: ' . count($urls) . PHP_EOL;
$imported = 0;
foreach ($urls as $url) {
try {
$product = $parser->parseProduct($url);
if ($product['price'] === null) {
echo "Пропуск (нет цены): {$url}" . PHP_EOL;
continue;
}
$id = $importer->import($product);
$imported++;
printf("[%d] %s — %s USD -> %s ₽ (ID %d)%s",
$imported, $product['name'], $product['price_source'], $product['price'], $id, PHP_EOL);
} catch (Throwable $e) {
echo "Ошибка на {$url}: {$e->getMessage()}" . PHP_EOL;
}
}
echo "Готово. Импортировано/обновлено: {$imported}" . PHP_EOL;
Автозапуск раз в сутки — строка в crontab:
# каждый день в 4:00 синхронизируем каталог с поставщиком
0 4 * * * /usr/bin/php /var/www/bitrix/local/parser/run.php >> /var/www/bitrix/local/parser/parser.log 2>&1
Альтернатива cron — агент Битрикса (
CAgent::AddAgent): он запускается на хитах сайта и удобен, когда нет доступа к системному планировщику. Но для тяжёлого парсинга надёжнее именно cron, чтобы не нагружать посетителей.
Пример 2: сбор на Python → CSV → штатный импорт
Если не хочется подключать ядро и трогать боевой код, парсинг можно развязать с импортом: собрать данные любым удобным инструментом (например, на Python), сложить в CSV, а заливку доверить штатному импорту «Торговый каталог → Экспорт/Импорт». Так парсер вообще не зависит от Битрикса и его обновлений.
Здесь конвертацию валюты делаем сами — берём курс ЦБ РФ и применяем наценку (так же, как в PHP-варианте, но на стороне сборщика).
import csv
import time
import requests
import xml.etree.ElementTree as ET
from bs4 import BeautifulSoup
from urllib.parse import urljoin
from datetime import date
class CbrRate:
"""Курс ЦБ РФ с кешем на день."""
URL = "https://www.cbr.ru/scripts/XML_daily.asp"
def __init__(self, currency="USD"):
self.currency = currency
self._rate = None
self._date = None
def rate(self):
if self._rate is None or self._date != date.today():
r = requests.get(self.URL, timeout=15)
r.encoding = "windows-1251"
tree = ET.fromstring(r.text)
for v in tree.findall("Valute"):
if v.find("CharCode").text == self.currency:
nominal = int(v.find("Nominal").text)
value = float(v.find("Value").text.replace(",", "."))
self._rate = value / nominal
self._date = date.today()
break
return self._rate
def parse_and_export(base_url, catalog_path, out_csv,
currency="USD", markup=1.25, max_pages=3, delay=1.0):
cbr = CbrRate(currency)
session = requests.Session()
session.headers["User-Agent"] = "Mozilla/5.0 (compatible; CatalogImporter/1.0)"
rows = []
for page in range(1, max_pages + 1):
soup = BeautifulSoup(
session.get(f"{base_url}{catalog_path}?page={page}", timeout=20).text, "lxml"
)
cards = soup.select(".product-card a.product-link")
if not cards:
break
for a in cards:
url = urljoin(base_url, a["href"])
ps = BeautifulSoup(session.get(url, timeout=20).text, "lxml")
name = ps.select_one("h1.product-title")
sku = ps.select_one(".sku")
descr = ps.select_one(".product-description")
price_el = ps.select_one(".price .value")
img = ps.select_one(".product-gallery img")
price_src = None
if price_el:
digits = "".join(c for c in price_el.text if c.isdigit() or c == ".")
price_src = float(digits) if digits else None
# конвертация валюты: курс ЦБ * наценка
price_rub = round(price_src * cbr.rate() * markup, 2) if price_src else ""
rows.append({
"XML_ID": sku.text.strip() if sku else url,
"NAME": name.text.strip() if name else "Без названия",
"DETAIL_TEXT": descr.decode_contents().strip() if descr else "",
"PRICE": price_rub,
"CURRENCY": "RUB",
"PICTURE": urljoin(base_url, img["src"]) if img else "",
})
time.sleep(delay)
time.sleep(delay)
# CSV под штатный импорт Торгового каталога (разделитель ;)
with open(out_csv, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=rows[0].keys(), delimiter=";")
writer.writeheader()
writer.writerows(rows)
print(f"Готово. Записано строк: {len(rows)} -> {out_csv}")
if __name__ == "__main__":
parse_and_export(
base_url="https://supplier-example.com",
catalog_path="/catalog/category-1",
out_csv="import.csv",
)
Дальше CSV загружается в админке: Магазин → Настройки → Настройки модулей → Торговый каталог → Экспорт/Импорт → Импорт CSV, где колонки сопоставляются с полями товара (внешний код, название, описание, цена, валюта, картинка). Плюс подхода — парсер не зависит от ядра Битрикса; минус — заливка не «на лету», а отдельным шагом.
Что важно в реальном проекте
Учебные примеры намеренно упрощены. В боевом парсере под Битрикс дополнительно нужно предусмотреть:
- Торговые предложения (SKU) — если у товара есть вариации, создавать элементы в инфоблоке предложений и привязывать их к товару, а цену/остаток вешать на предложения, а не на товар.
- Свойства каталога — сопоставлять характеристики донора со свойствами инфоблока (
CIBlockElement::SetPropertyValues), при необходимости автосоздавать значения справочников. - Разделы — автосоздание дерева категорий по структуре донора через
CIBlockSection. - Несколько типов цен и складов — для редакции «Бизнес»/«Энтерпрайз»: оптовые/розничные цены, остатки по складам.
- Дедупликацию и синхронизацию — обновлять цену/остаток по
XML_ID, а не плодить дубли; так разовый парсинг превращается в регулярную синхронизацию. - Курс и наценку — хранить в конфиге и логировать, по какому курсу пересчитана партия, чтобы цены были воспроизводимы.
- Этичность и легальность — соблюдать
robots.txt, делать паузы между запросами и учитывать условия использования сайта-донора.
Итог
Для типового магазина часто хватает связки «парсер собирает файл + штатный импорт CommerceML/CSV» или готового модуля из Маркетплейса (kitnet.import, «Сотбит: Парсер контента», Datacol). Но как только появляется нестандартный донор, своя схема свойств и торговых предложений, несколько типов цен, конвертация валют по актуальному курсу или регулярная синхронизация остатков под вашу редакцию Битрикса — надёжнее заказать парсер под задачу, написанный через API Битрикса.
Нужен парсинг интернет-магазинов?
Если вам нужно наполнить или синхронизировать магазин на 1С-Битрикс (или любой другой платформе), собрать данные с сайтов поставщиков, настроить автоматическое обновление цен с конвертацией валют — обращайтесь к нам. Спроектируем и внедрим парсер под вашу редакцию Битрикса и ваши задачи.