# Разбор страницы в структуру

> Языковая модель достаёт из страницы заказанные поля и отдаёт их объектом

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

`extract` описывает словами, что нужно достать со страницы, а в ответ приходит
готовый объект `parsed`. Вы не пишете селекторы: вёрстка у каждого сайта своя и
меняется без предупреждения, а «Цена числом, без валюты» — то же самое и на
маркетплейсе, и в блоге.

**Цена:** +3 кредита к разметке, итого 4.

```python Python
from parreq import Parreq

client = Parreq("pr_ВАШКЛЮЧ")
res = client.fetch(
    "https://example.com/product/42",
    extract={
        "name": "Название товара",
        "price": "Цена числом, без валюты",
        "in_stock": "Есть ли в наличии",
    },
)

print(res.parsed)
print(res.metadata["extract_note"])
```

```javascript Node

const client = new Parreq({ apiKey: "pr_ВАШКЛЮЧ" });
const res = await client.fetch({
  url: "https://example.com/product/42",
  extract: {
    name: "Название товара",
    price: "Цена числом, без валюты",
    in_stock: "Есть ли в наличии",
  },
});

console.log(res.parsed);
console.log(res.metadata.extract_note);
```

```bash cURL
curl -G -H "Authorization: Bearer pr_ВАШКЛЮЧ" \
  --data-urlencode "url=https://example.com/product/42" \
  --data-urlencode 'extract={"name":"Название товара","price":"Цена числом, без валюты","in_stock":"Есть ли в наличии"}' \
  "https://api.parreq.com/v1/fetch"
```

Ответ:

```json
{
  "parsed": {
    "name": "Кофемолка Wilfa Svart",
    "price": 8990,
    "in_stock": true
  },
  "fetch_metadata": {
    "extract_note": "",
    "credits": 4,
    "credits_breakdown": { "html": 1, "extract": 3 }
  }
}
```

## Что приходит

- **`parsed`** (`object | null`):
  Заказанные поля с найденными значениями. Приходит **всегда**, если `extract`
  заказывали, — и `null`, если разбор не состоялся. Ключи те же, что вы задали,
  и в том же порядке.

- **`extract_note`** (`string`):
  Живёт в `fetch_metadata`. Почему разбор не состоялся, человеческим языком.
  Пустая строка — либо всё прошло как заказано, либо `extract` не заказывали.

Типы приходят настоящими, а не строками: число остаётся числом, «да/нет» —
`true` или `false`. Поле, которого на странице не нашлось, приходит `null` —
ключ не пропадает, чтобы разбор ответа не приходилось писать через проверку
наличия ключа.

## Как описывать поля

Имя поля — латиница, цифры и подчёркивание, начинается с буквы, до 64 символов.
Это ключи в вашем коде, и они должны переживать переход в любой язык без
экранирования.

| ограничение | значение | переменная |
|---|---|---|
| полей в запросе | 25 | `PARREQ_EXTRACT_MAX_FIELDS` |
| длина описания | 200 символов | `PARREQ_EXTRACT_MAX_HINT` |

Описание — обычная фраза, а не шаблон. Чем точнее сказано про форму значения,
тем ближе результат к тому, что вы ждёте: «Цена числом, без валюты» даёт
`8990`, а просто «Цена» может дать `"8 990 ₽"`.

> **Примечание.**
>   У GET `extract` передаётся строкой JSON — в адресе объекту взяться неоткуда.
>   У POST это обычный объект. Клиентские библиотеки ходят POST, и заботиться об
>   этом не приходится.

## Чего разбор не гарантирует

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

Обещать здесь больше, чем есть, было бы нечестно. Если вам нужна ровно та
величина ровно из того блока и каждый раз одинаково — берите
[подрезку блоками](https://docs.parreq.com/fetch-tuning): селектор либо совпал, либо нет, и это видно
по `matched`.

> **Внимание.**
>   Не стройте на `parsed` расчёты, которые нельзя перепроверить. Разбор хорош
>   там, где сайтов много и вёрстка у всех разная, — и плох там, где цена ошибки
>   выше цены ручной проверки.

## Неудача не оплачивается

Разбор не списывается, если он не состоялся: модель не ответила, вернула не
JSON или не нашла ни одного из заказанных полей. Тогда в ответе `parsed: null`,
в `extract_note` — что случилось, а в счёте остаётся только разметка:

```json
{
  "parsed": null,
  "fetch_metadata": {
    "extract_note": "модель не нашла ни одного из заказанных полей",
    "credits": 1,
    "credits_breakdown": { "html": 1 }
  }
}
```

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

## Отказы

Ошибка в описании полей — `400 invalid_request`: неправильное имя, слишком
длинное описание, больше 25 полей, `extract` не объектом. Проверяется до
списания и до похода в браузер, так что опечатка ничего не стоит.

Если разбор на сервере не настроен вовсе, приходит `503 extract_unavailable` —
тоже до списания. Это не «попробуйте позже с другими параметрами», а «этой
возможности здесь сейчас нет».

## Дальше

**[Тонкая настройка разметки](https://docs.parreq.com/fetch-tuning)**
    Селекторы, когда нужен предсказуемый результат, а не догадка.

**[Надстройки Fetch](https://docs.parreq.com/fetch-extras)**
    Что ещё можно добавить к разметке и сколько это стоит.

**[Кредиты и права](https://docs.parreq.com/pricing)**
    Весь прайс и как подключаются платные позиции.

**[Справочник](https://docs.parreq.com/api-reference/fetch)**
    Все параметры `/v1/fetch` и коды ответов.
