# Дерево каталога: разделы, подразделы, товары

> Как обход помечает каждую страницу, что показывает /v1/crawl/{id}/tree и почему плоский список страниц отвечает не на тот вопрос

Источник: https://docs.parreq.com/crawl-tree/

Плоский список собранных страниц отвечает на вопрос «что мы скачали».
Спрашивают обычно другое: **как устроен каталог конкурента**. Что у него есть
раздел кровли, клея и красок; что у кровли внутри ещё подразделы, а у клея
товары начинаются сразу; сколько в каждом разделе страниц и все ли они забраны.

Ответ на это собирается сам, без дополнительных заходов: обход помечает каждую
страницу по ходу дела, а ручка `tree` раскладывает пометки деревом.

## Чем помечается страница

| пометка | что это |
|---|---|
| `sections` | оглавление каталога — ссылки на разделы, товаров нет |
| `subsections` | то же, но внутри раздела |
| `listing` | список товаров: на странице есть цены |
| `card` | карточка одного товара |
| `not_catalog` | подборка, фильтр или служебная страница — в дерево не идёт |
| `other` | страница вне каталога |

В готовых решениях пометка ставится по **всей разметке страницы**, а не по
форме её адреса: фильтр и подраздел по адресу неотличимы, и разбирается это
только содержимым. Как именно — на отдельной странице:
[что не каталог](https://docs.parreq.com/crawl-not-catalog).

У `/v1/crawl` с вашим собственным правилом решение принимается по двум
признакам — по форме адреса и по наличию цен в разметке. Поодиночке не хватает
ни одного: форма адреса не отличает раздел с товарами от раздела с
подразделами, а цены есть и в списке, и в карточке.

> **К сведению.**
>   Вторая и следующие страницы списка (`?page=2`) остаются списком. Карточка
>   товара постраничной не бывает, и такие адреса карточками не считаются.

## Ручка

```bash
curl https://api.parreq.com/v1/crawl/{id}/tree \
  -H "Authorization: Bearer $PARREQ_KEY"
```

```json
{
  "дерево": [
    {
      "адрес": "https://shop.example/catalog/",
      "что это": "раздел каталога",
      "цен на странице": 0,
      "разделы": [
        {
          "адрес": "https://shop.example/catalog/krovlya/",
          "что это": "подраздел каталога",
          "разделы": [
            { "адрес": ".../krovlya/skaty/", "что это": "список товаров",
              "цен на странице": 20,
              "товары": [{ "адрес": ".../skaty/plenka-alfa/",
                           "что это": "карточка товара",
                           "данные": { "name": "Плёнка Альфа", "price": 42.5 } }] }
          ]
        },
        { "адрес": "https://shop.example/catalog/klej/",
          "что это": "список товаров", "цен на странице": 18 }
      ]
    }
  ],
  "итого": { "разделов": 1, "подразделов": 1, "списков": 2, "товаров": 14,
             "не_каталог": 0 }
}
```

Ветки неравной глубины — это норма, а не сбой: у одного раздела товары
начинаются сразу, у другого под ним ещё этаж. Разложить каталог заранее по
одинаковому числу этажей нельзя, поэтому обход разбирает каждую ветку на месте.

Ручка денег не стоит: она перекладывает уже собранное и оплаченное.

## Сколько страниц осталось

У каждой страницы списка записано, сколько всего страниц у этого списка
(`pages_total`), а у карточки — сколько на ней торговых предложений (`offers`).
Оба числа видны в `/v1/crawl/{id}/pages`.

По ним же составляется итоговая заметка задания. Если объём кончился раньше
каталога, задание завершается со `status: "partial"` и говорит об этом прямо:

```json
{ "status": "partial", "code": "pages_limit",
  "note": "собрано 25 страниц, осталось необойдённых 234 — сайт больше оплаченного объёма" }
```

> **Внимание.**
>   Молчаливое «готово» на трёх страницах из двадцати пяти — худший из возможных
>   отчётов: по нему нельзя отличить обойдённый каталог от первой страницы
>   каждого раздела. Поэтому итог всегда называет числа.
