curl и wget: скачивание и HTTP из command line
curl и wget — две главные утилиты для работы с сетью из command line. На уровне «скачать файл» они выглядят похоже, но философия разная: curl — это универсальный HTTP-клиент для скриптов и API-запросов, wget — специализированный downloader, заточенный под массовое и recursive скачивание.
Для DE это инструменты ежедневного использования: дёрнуть Airflow REST API, выкачать CSV-дамп с governmental portal, проверить, что endpoint жив, сделать webhook-вызов после ETL. В этом уроке — что и когда использовать.
curl vs wget: что выбрать
Один инструмент — для API, другой — для bulk downloads
Правило DE:
- Делаешь HTTP API-запрос (REST API, webhook) -> curl
- Качаешь файл с известного URL -> можно оба, но wget проще
- Качаешь много файлов / recursive -> wget
- Скрипт обрабатывает ответ через jq, awk -> curl
curl базовые приёмы
# Простой GET, тело в stdout
curl https://api.example.com/users
# Скачать и сохранить как локальный файл с тем же именем
curl -O https://example.com/data.csv
# data.csv появится в текущей директории
# Скачать и сохранить под другим именем
curl -o my-data.csv https://example.com/data.csv
# Следовать redirects (важно — без -L curl остановится на 301/302)
curl -L https://github.com/...
Флаги, которые встречаются часто:
Что встретите в туториалах и Stack Overflow
Анатомия HTTP-запроса: методы, заголовки, статус-коды
POST, PUT, DELETE — API-вызовы
# POST с JSON-телом
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "[email protected]"}'
# С Bearer token
curl -X POST https://api.example.com/users \
-H "Authorization: Bearer eyJhbGc..." \
-H "Content-Type: application/json" \
-d '{"name": "Alice"}'
# PUT, обновление
curl -X PUT https://api.example.com/users/123 \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
# DELETE
curl -X DELETE https://api.example.com/users/123 \
-H "Authorization: Bearer $TOKEN"
# Послать тело из файла
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
--data-binary @user.json
--data-binary @file (с символом @ перед именем файла) посылает содержимое точно как есть. Просто -d @file тоже работает, но -d удаляет переводы строк — это часто ломает JSON. Для JSON-данных всегда используйте --data-binary @.
Headers и аутентификация
# Один header
curl -H "Authorization: Bearer $TOKEN" https://api.example.com
# Несколько headers
curl -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "X-Request-ID: $(uuidgen)" \
https://api.example.com
# Basic auth (легаси, многие API уходят от него)
curl -u username:password https://api.example.com
# Эквивалент: -H "Authorization: Basic $(echo -n 'username:password' | base64)"
Сохранять токен в переменную окружения — стандарт:
# Один раз в shell
export AIRFLOW_TOKEN="eyJhbGc..."
# Использовать
curl -H "Authorization: Bearer $AIRFLOW_TOKEN" \
https://airflow.company.com/api/v1/dags
Никогда не хардкодить токены в скрипты — кладите в .env файлы или secrets manager.
Обработка ответа: статус код, заголовки, тело
Иногда нужно знать: получилось ли? Какой статус?
# Только статус код
curl -s -o /dev/null -w "%{http_code}\n" https://api.example.com
# 200
# Статус + время
curl -s -o /dev/null \
-w "status=%{http_code} time=%{time_total}s size=%{size_download}b\n" \
https://api.example.com
# Статус и тело отдельно
curl -s -w "\n%{http_code}" https://api.example.com
# {"data": ...}
# 200
В скриптах:
#!/bin/bash
RESPONSE=$(curl -s -w "\n%{http_code}" "$URL")
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" != "200" ]; then
echo "API error: $HTTP_CODE"
echo "$BODY" >&2
exit 1
fi
echo "$BODY" | jq '.data'
wget — для bulk и recursive
# Простое скачивание
wget https://example.com/data.csv
# Указать имя
wget -O backup.csv https://example.com/data.csv
# Тихо
wget -q https://example.com/data.csv
# С прогресс-баром
wget --progress=bar:force https://example.com/big-file.zip
# Resume прерванное скачивание
wget -c https://example.com/huge-file.tar.gz
Recursive download — конёк wget:
# Скачать весь сайт (или его раздел)
wget -r --no-parent https://example.com/docs/
# -r recursive
# --no-parent — не выходить «выше» по дереву URL
# Конкретные типы файлов
wget -r --no-parent -A "*.csv" https://data.gov.ru/2026/
# Ограничить глубину
wget -r -l 3 --no-parent https://example.com/
Полезные опции:
# Mirror (recursive + timestamps + no parent)
wget -m https://example.com
# С user-agent (некоторые сайты блокируют дефолтный)
wget -U "Mozilla/5.0" https://example.com
# Pause между запросами (не DDOS-ить сервер)
wget --wait=1 -r https://example.com
# Список URL из файла
wget -i urls.txt
DE-сценарии
1. Hit Airflow REST API
AIRFLOW="https://airflow.company.com/api/v1"
TOKEN="eyJhbGc..."
# Список DAGs
curl -s -H "Authorization: Bearer $TOKEN" "$AIRFLOW/dags" | jq '.dags[].dag_id'
# Триггер DAG
curl -X POST "$AIRFLOW/dags/etl_daily/dagRuns" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"conf\": {\"date\": \"2026-05-13\"}}"
# Статус последнего run
curl -s -H "Authorization: Bearer $TOKEN" \
"$AIRFLOW/dags/etl_daily/dagRuns?limit=1&order_by=-execution_date" \
| jq '.dag_runs[0].state'
2. Скачать data dump с публичного API
# Скачать CSV дамп
curl -L -o sales-2026-04.csv \
"https://data.gov.example.com/2026/sales/2026-04.csv"
# Проверить размер
ls -lh sales-2026-04.csv
# Распарсить через awk
awk -F',' 'NR > 1 {sum += $3} END {print "Total:", sum}' sales-2026-04.csv
3. Health check для monitoring
#!/bin/bash
SERVICES=("https://airflow.company.com/health"
"https://api.company.com/healthz"
"https://grafana.company.com/api/health")
for url in "${SERVICES[@]}"; do
CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$url")
if [ "$CODE" = "200" ]; then
echo "OK: $url"
else
echo "FAIL: $url (HTTP $CODE)"
fi
done
4. Webhook после ETL
# После успешной ETL — оповестить Slack
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"ETL succeeded at $(date -Iseconds)\"}" \
https://hooks.slack.com/services/T00000/B00000/XXXXXXXXX
5. Загрузить файл на S3 через presigned URL
# Получили presigned URL от backend
S3_URL="https://bucket.s3.amazonaws.com/upload?X-Amz-Algorithm=...&X-Amz-Signature=..."
# Upload через curl PUT
curl -X PUT \
-H "Content-Type: application/octet-stream" \
--data-binary @local-file.parquet \
"$S3_URL"
curl для диагностики: timing, TLS, proxy, verbose
Debugging — когда не работает
# Verbose: показать всё
curl -v https://api.example.com
# Видно: DNS resolve, TCP connect, TLS handshake, headers, body
# Trace всего I/O в файл
curl --trace trace.log https://api.example.com
# Только заголовки ответа (HEAD-запрос)
curl -I https://api.example.com
# Время выполнения детально
curl -w "@curl-timing.txt" -o /dev/null -s https://api.example.com
Файл curl-timing.txt:
time_namelookup: %{time_namelookup}\n
time_connect: %{time_connect}\n
time_appconnect: %{time_appconnect}\n
time_pretransfer: %{time_pretransfer}\n
time_starttransfer: %{time_starttransfer}\n
time_total: %{time_total}\n
Это сразу видно: где задержка — в DNS, TCP connect, TLS handshake, или собственно сервер?
Хитрые моменты
URL-encoding
Параметры URL надо encode-ить:
# Плохо — пробелы и символы могут сломать URL
curl "https://api.example.com/search?q=data engineer&date=2026-05-13"
# Хорошо — --data-urlencode
curl -G "https://api.example.com/search" \
--data-urlencode "q=data engineer" \
--data-urlencode "date=2026-05-13"
# -G делает GET вместо POST, но использует --data
Proxy
# Через корпоративный proxy
curl -x http://proxy.company.com:8080 https://api.example.com
# Или через env vars (curl уважает)
export HTTPS_PROXY=http://proxy.company.com:8080
curl https://api.example.com
Игнорировать SSL ошибки (только для debug!)
# Если сервер с самоподписанным сертификатом
curl -k https://internal.dev.company.com
# В production никогда не используйте -k!
Попробуй сам
# 1. Простой GET
curl https://api.github.com/zen
# 2. Скачать с прогрессом
curl -O https://raw.githubusercontent.com/torvalds/linux/master/README
# 3. JSON-ответ + jq
curl -s https://api.github.com/users/torvalds | jq '.public_repos, .followers'
# 4. POST с JSON
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-d '{"hello": "world"}'
# 5. Только статус код
curl -s -o /dev/null -w "%{http_code}\n" https://github.com
# 6. Время выполнения
curl -s -o /dev/null \
-w "DNS=%{time_namelookup} connect=%{time_connect} total=%{time_total}\n" \
https://github.com
# 7. Recursive wget — небольшая страница
wget --no-parent -r -l 2 -A "*.html" https://example.com/
# 8. Resume — попробуй с большим файлом
wget -c https://download.geofabrik.de/europe-latest.osm.pbf
# Прерви через Ctrl-C, потом запусти ту же команду — продолжит с того же места
Cross-link: следующий урок 02 — ssh, для удалённого доступа. Модуль 18 — jq для обработки JSON-ответов. Модуль 17 — bash-скрипты с error handling для curl-вызовов.