Что такое API и парсинг при работе с ним
API (Application Programming Interface) — это интерфейс, через который одна программа обращается к другой и получает данные или запускает действия, не зная её внутреннего устройства. На практике под «работой с API» чаще всего понимают обращение к веб-API по протоколу HTTP: клиент отправляет запрос на определённый адрес (эндпоинт), а сервер возвращает ответ.
Под «парсингом API» обычно понимают весь цикл получения и разбора данных от внешнего сервиса:
- Сформировать и отправить корректный HTTP-запрос.
- Получить ответ сервера.
- Проверить статус ответа и заголовки.
- Разобрать тело ответа (чаще всего JSON) в объекты программы.
- Обработать ошибки, повторы, постраничную выдачу.
См. также: материал «Парсинг JSON» посвящён узкому шагу разбора самих данных — превращению JSON-строки в объекты. Настоящая статья охватывает весь процесс работы с API, частью которого этот разбор и является.
В отличие от «парсинга сайтов» (скрапинга HTML), работа с API опирается на структурированные ответы в машинно-читаемом формате, поэтому она надёжнее, стабильнее и почти всегда предпочтительнее, когда у сервиса есть официальный API.
Анатомия HTTP-запроса
Любой запрос к веб-API состоит из нескольких частей.
Метод (HTTP-глагол)
Метод описывает намерение запроса:
- GET — получить данные (не изменяет состояние).
- POST — создать новый ресурс или отправить данные.
- PUT / PATCH — обновить ресурс (целиком / частично).
- DELETE — удалить ресурс.
URL и параметры запроса
Адрес эндпоинта может содержать query-параметры для фильтрации, сортировки и пагинации:
https://api.example.com/users?role=admin&page=2&limit=50
Заголовки (headers)
Заголовки передают метаданные запроса. Самые важные при работе с API:
Authorization— данные аутентификации (токен, ключ).Content-Type— формат отправляемого тела (например,application/json).Accept— формат, в котором клиент хочет получить ответ.User-Agent— идентификатор клиента.
Тело запроса (body)
Для POST/PUT/PATCH в теле передаются данные — как правило, в виде JSON-строки, которую нужно предварительно сериализовать из объектов программы.
Анатомия ответа и его разбор
Ответ сервера состоит из статус-кода, заголовков и тела.
Статус-коды
Прежде чем разбирать тело, нужно проверить статус-код:
- 2xx — успех (
200 OK,201 Created,204 No Content). - 3xx — перенаправление.
- 4xx — ошибка на стороне клиента (
400неверный запрос,401не авторизован,403запрещено,404не найдено,429слишком много запросов). - 5xx — ошибка на стороне сервера.
Разбирать тело как успешные данные имеет смысл только при кодах 2xx. Тело ошибки тоже часто содержит полезный JSON с описанием проблемы.
Заголовки ответа
Из заголовков ответа берут важную информацию: Content-Type (формат тела), параметры пагинации, лимиты запросов (X-RateLimit-Remaining), управление кешем.
Тело ответа
Тело — это сами данные. В большинстве API это JSON, который нужно распарсить (см. «Парсинг JSON»). Реже встречаются другие текстовые форматы — XML (см. «Парсинг XML») и CSV (см. «Парсинг CSV»), — а также бинарные форматы.
Аутентификация и авторизация
Большинство API требуют подтвердить, что клиент имеет право на доступ. Распространённые способы:
- API-ключ — простая строка, передаётся в заголовке или query-параметре.
- Bearer-токен / OAuth 2.0 — токен в заголовке
Authorization: Bearer <token>; самый распространённый подход в современных API. - Basic Auth — логин и пароль в закодированном виде.
- HMAC / подпись запроса — запрос подписывается секретом, применяется в платёжных и облачных API.
Важно: ключи и токены — это секреты. Их нельзя хранить в коде и коммитить в репозиторий; используйте переменные окружения или защищённые хранилища.
Сложности при работе с API
Парсинг API редко сводится к одному запросу. Ниже — типичные сложности, к которым стоит готовиться.
Пагинация
API почти никогда не отдаёт большие коллекции целиком — данные разбиваются на страницы. Основные модели:
- Offset/limit —
?page=2&limit=50или?offset=100&limit=50. - Курсорная (cursor) — в ответе приходит указатель на следующую страницу (
next_cursor), который передаётся в следующий запрос. - Keyset — следующая страница запрашивается по значению последнего элемента (например, по
idили дате).
Чтобы собрать все данные, нужен цикл, который идёт по страницам, пока они не закончатся.
Ограничение частоты запросов (rate limiting)
Сервисы ограничивают число запросов за период. При превышении приходит код 429. Корректное решение — следить за заголовками лимитов и применять экспоненциальную задержку (backoff) с повторами.
Ненадёжность сети и повторы
Сетевые запросы могут падать по таймауту или из-за временных ошибок 5xx. Для устойчивости применяют:
- разумные таймауты;
- повторы (retry) для идемпотентных запросов с нарастающей задержкой;
- паттерн circuit breaker при систематических сбоях.
Изменчивость и валидация данных
Ответ API может прийти неполным, с null-полями или с изменённой структурой после обновления версии API. Никогда не стоит слепо доверять структуре ответа — поля нужно проверять, а данные валидировать.
Версионирование
API развиваются, и их версии меняются (/v1/, /v2/). Стоит фиксировать конкретную версию и отслеживать объявления об устаревании (deprecation).
Вложенность и разные форматы
Полезные данные нередко спрятаны глубоко во вложенной структуре (data.items[0].attributes.name). Иногда вместо JSON приходит XML (см. «Парсинг XML») или CSV (см. «Парсинг CSV»), что требует другого парсера.
Примеры реализации на разных языках
Ниже — минимальные примеры полного цикла: запрос к API, проверка статуса и разбор JSON-ответа. Для единообразия используется условный эндпоинт https://api.example.com/users/42.
Python
Популярная библиотека requests берёт на себя и запрос, и разбор JSON.
import requests
url = "https://api.example.com/users/42"
headers = {"Authorization": "Bearer YOUR_TOKEN"}
response = requests.get(url, headers=headers, timeout=10)
# Проверка статуса
response.raise_for_status() # выбросит исключение при 4xx/5xx
# Разбор JSON-ответа в словарь
user = response.json()
print(user["name"])
Пример сбора всех страниц (offset/limit):
def fetch_all_users():
users, page = [], 1
while True:
resp = requests.get(
"https://api.example.com/users",
params={"page": page, "limit": 50},
timeout=10,
)
resp.raise_for_status()
batch = resp.json()["data"]
if not batch:
break
users.extend(batch)
page += 1
return users
JavaScript (Node.js / браузер)
Встроенный fetch возвращает промис; разбор JSON — метод .json().
const url = "https://api.example.com/users/42";
const response = await fetch(url, {
headers: { Authorization: "Bearer YOUR_TOKEN" },
});
// Проверка статуса
if (!response.ok) {
throw new Error(`Ошибка API: ${response.status}`);
}
// Разбор JSON
const user = await response.json();
console.log(user.name);
Пример отправки данных (POST):
const response = await fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer YOUR_TOKEN",
},
body: JSON.stringify({ name: "Анна", role: "admin" }), // сериализация
});
const created = await response.json();
Java
В современной Java есть встроенный HttpClient, а для разбора JSON используют библиотеку (здесь — Jackson).
import java.net.URI;
import java.net.http.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
public class ApiExample {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/42"))
.header("Authorization", "Bearer YOUR_TOKEN")
.GET()
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("Ошибка API: " + response.statusCode());
}
ObjectMapper mapper = new ObjectMapper();
JsonNode user = mapper.readTree(response.body());
System.out.println(user.get("name").asText());
}
}
Go
Стандартная библиотека предоставляет и HTTP-клиент (net/http), и парсер (encoding/json).
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type User struct {
Name string `json:"name"`
}
func main() {
req, _ := http.NewRequest("GET", "https://api.example.com/users/42", nil)
req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
panic(fmt.Sprintf("Ошибка API: %d", resp.StatusCode))
}
var user User
json.NewDecoder(resp.Body).Decode(&user) // потоковый разбор тела
fmt.Println(user.Name)
}
C
В .NET используют HttpClient и встроенный System.Text.Json.
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
record User(string Name);
class Program
{
static async Task Main()
{
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_TOKEN");
var response = await client.GetAsync("https://api.example.com/users/42");
response.EnsureSuccessStatusCode();
// Разбор JSON прямо в типизированный объект
var user = await response.Content.ReadFromJsonAsync<User>();
Console.WriteLine(user?.Name);
}
}
PHP
С помощью cURL делается запрос, а json_decode разбирает ответ.
<?php
$ch = curl_init("https://api.example.com/users/42");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer YOUR_TOKEN"]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
throw new Exception("Ошибка API: $status");
}
// Разбор JSON в ассоциативный массив
$user = json_decode($body, true);
echo $user["name"];
Rust
Популярная связка — асинхронный клиент reqwest и serde для разбора.
use serde::Deserialize;
#[derive(Deserialize)]
struct User {
name: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let response = client
.get("https://api.example.com/users/42")
.header("Authorization", "Bearer YOUR_TOKEN")
.send()
.await?;
if !response.status().is_success() {
return Err(format!("Ошибка API: {}", response.status()).into());
}
// Разбор JSON в структуру
let user: User = response.json().await?;
println!("{}", user.name);
Ok(())
}
Сравнение инструментов
| Язык | HTTP-клиент | Разбор JSON |
|---|---|---|
| Python | requests / httpx |
.json() (json) |
| JavaScript | fetch / axios |
.json() (JSON) |
| Java | HttpClient |
Jackson / Gson |
| Go | net/http |
encoding/json |
| C# | HttpClient |
System.Text.Json |
| PHP | cURL / Guzzle | json_decode |
| Rust | reqwest |
serde / serde_json |
Хорошие практики
При построении надёжной работы с API стоит придерживаться нескольких принципов:
- Всегда проверяйте статус-код до разбора тела ответа.
- Оборачивайте разбор в обработку ошибок — внешние данные ненадёжны.
- Не храните секреты в коде — используйте переменные окружения.
- Уважайте лимиты запросов — применяйте задержки и backoff при 429.
- Ставьте таймауты на каждый запрос, чтобы не зависать.
- Логируйте запросы и ошибки — это упрощает отладку интеграций.
- Кешируйте редко меняющиеся данные, чтобы снизить нагрузку и не упираться в лимиты.
- Фиксируйте версию API и следите за объявлениями об устаревании.
- Валидируйте структуру ответа перед использованием полей.
Итог
Парсинг API — это полный цикл взаимодействия с внешним сервисом: формирование запроса, аутентификация, проверка статуса и заголовков, разбор тела ответа и обработка краевых случаев — пагинации, лимитов, сетевых сбоев. Технически шаг разбора данных почти всегда сводится к парсингу JSON, но устойчивая интеграция требует учитывать и всё, что его окружает.
Инструменты есть в каждом распространённом языке: где-то HTTP-клиент и парсер встроены, где-то применяются популярные библиотеки. Принцип везде один — превратить ответ удалённого сервиса в надёжные данные, с которыми безопасно работать в коде.
Подробнее о самом этапе разбора данных см. материал «Парсинг JSON», а о разборе других форматов ответа — материалы «Парсинг XML» и «Парсинг CSV».