Розробка парсера на PHP для 1С-Бітрікс
PHP — природний вибір для парсера, який працює в поєднанні з 1С-Бітрікс. Одна мова, один рантайм, прямий доступ до API інфоблоків без проміжних шарів. Але PHP-парсер має обмеження, які потрібно враховувати при проектуванні: однопоточність, лімити пам'яті, відсутність вбудованого event loop.
Архітектура PHP-парсера
Парсер складається з чотирьох компонентів:
1. Конфігурація джерел. Масив або таблиця в БД з параметрами кожного джерела: URL, тип (RSS, HTML, API), CSS-селектори для видобування даних, маппінг полів, частота оновлення.
2. HTTP-клієнт. Для простих задач — cURL через CHttpClient з ядра Бітрікс або нативний curl_multi для паралельних запитів. Для складних — Guzzle з middleware для retry, логування, ротації прокси.
3. Парсер HTML/XML. DOMDocument + DOMXPath для точної навігації по DOM. Для CSS-селекторів — бібліотека Symfony\Component\DomCrawler. Для RSS — SimpleXMLElement.
4. Імпортер. Шар запису даних в інфоблоки Бітрікс через D7 API або старий API (CIBlockElement).
Базова реалізація
Мінімальний парсер HTML-сторінки на PHP:
use Bitrix\Main\Loader;
use Bitrix\Iblock\ElementTable;
Loader::includeModule('iblock');
function parseSource(string $url, array $selectors): array
{
$html = file_get_contents($url, false, stream_context_create([
'http' => [
'timeout' => 30,
'user_agent' => 'Mozilla/5.0 (compatible; SiteBot/1.0)',
],
]));
$dom = new DOMDocument();
@$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
$xpath = new DOMXPath($dom);
$items = [];
foreach ($xpath->query($selectors['list']) as $node) {
$title = $xpath->evaluate('string(' . $selectors['title'] . ')', $node);
$link = $xpath->evaluate('string(' . $selectors['link'] . ')', $node);
$items[] = ['title' => trim($title), 'link' => $link];
}
return $items;
}
Цей каркас працює, але не готовий до продакшену. Потрібна обробка помилок, таймаути, логування, дедублікація.
Паралельні запити через curl_multi
Головне вузьке місце PHP-парсера — послідовність запитів. Завантаження 1 000 сторінок по 2 секунди кожна = 33 хвилини. З curl_multi_exec можна обробляти 10–20 запитів паралельно:
$multiHandle = curl_multi_init();
$handles = [];
foreach ($urls as $i => $url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_multi_add_handle($multiHandle, $ch);
$handles[$i] = $ch;
}
do {
$status = curl_multi_exec($multiHandle, $active);
curl_multi_select($multiHandle);
} while ($active > 0);
Обмеження: більше 50 паралельних з'єднань — і PHP почне споживати занадто багато пам'яті. Для масштабного парсингу (10 000+ URL) розбивайте на батчі по 20–30 з'єднань.
Інтеграція з ядром Бітрікс
Перевага PHP-парсера — прямий доступ до API. Не потрібен REST, не потрібна проміжна база. Імпорт в інфоблок:
$element = new CIBlockElement();
$elementId = $element->Add([
'IBLOCK_ID' => IBLOCK_CATALOG,
'NAME' => $parsedData['title'],
'XML_ID' => $parsedData['external_id'],
'ACTIVE' => 'Y',
'PREVIEW_TEXT' => $parsedData['description'],
'DETAIL_TEXT' => $parsedData['content'],
'DETAIL_TEXT_TYPE' => 'html',
'PREVIEW_PICTURE' => CFile::MakeFileArray($parsedData['image_path']),
]);
if ($elementId) {
CIBlockElement::SetPropertyValuesEx($elementId, IBLOCK_CATALOG, [
'SOURCE_URL' => $parsedData['url'],
'ARTICLE' => $parsedData['sku'],
]);
}
Важливо: при масовому імпорті вимикайте пошук та оновлення URL:
CIBlockElement::DisableEvents(); // Вимикає обробники подій
Без цього кожний Add() запускає переіндексацію пошуку, оновлення фасетного індексу та інші обробники — імпорт 10 000 товарів розтягнеться на години.
Обробка помилок та стійкість
PHP-парсер у продакшені повинен обробляти:
-
Таймаути — сервер не відповідає, з'єднання зависло. Встановлюйте
CURLOPT_TIMEOUTтаCURLOPT_CONNECTTIMEOUT. - HTTP-помилки — 403, 429, 503. Для 429 (rate limit) — збільшіть затримку. Для 403 — змініть прокси. Для 503 — повторіть пізніше.
-
Некоректний HTML —
DOMDocument::loadHTMLгенерує warnings. Придушуйте через@абоlibxml_use_internal_errors(true), але логуйте проблемні URL. -
Виснаження пам'яті — великі HTML-сторінки (5+ МБ) споживають пам'ять. Встановлюйте
memory_limitадекватно та звільняйте DOM після обробки:unset($dom).
Паттерн retry з експоненціальною затримкою:
function fetchWithRetry(string $url, int $maxRetries = 3): ?string
{
for ($i = 0; $i < $maxRetries; $i++) {
$response = @file_get_contents($url);
if ($response !== false) {
return $response;
}
sleep(pow(2, $i)); // 1, 2, 4 секунди
}
return null;
}
Логування
Без логів відладка парсера неможлива. Мінімальний набір подій для запису:
- Початок та завершення сесії парсингу (час, кількість оброблених URL).
- Кожний HTTP-запит: URL, статус ответу, час завантаження.
- Помилки парсингу: URL, тип помилки, контекст.
- Результат імпорту: створено, оновлено, пропущено (дубли), помилки.
Використовуйте \Bitrix\Main\Diag\Logger з D7 або пишіть в окрему таблицю parser_log.
Коли PHP недостатньо
PHP-парсер не підходить, якщо:
- Потрібен рендеринг JavaScript — SPA-сайти, динамічна підгрузка контенту. Тут потрібен headless-браузер (Puppeteer/Playwright), а це Node.js або Python.
- Обсяг парсингу перевищує 50 000 сторінок за сесію — PHP упирається в однопоточність та споживання пам'яті.
- Потрібна складна обробка тексту (NLP, класифікація, видобування сутностей) — екосистема Python значно багатша.
У цих випадках розглядайте гібридний підхід: Python/Node.js для збирання даних, PHP для імпорту в Бітрікс.







