Воскресный парсер прогноза погоды
Понадобился аккуратный набор данных: температура и осадки по моему городу за последние пять лет, по дням. Бесплатные API дают либо текущее, либо месячный history с лимитом. Сел собирать самому в воскресенье.
Идея простая
- Берём публичный архив наблюдений (rp5, синоптики и пр.).
- Скачиваем CSV постранично.
- Кладём в локальный SQLite, чтобы потом удобно
SELECT-ать. - Каждый день обновляем последний слой инкрементом.
Что получилось
import httpx, sqlite3, csv, io
from datetime import date, timedelta
DB = sqlite3.connect("weather.db")
DB.execute("""CREATE TABLE IF NOT EXISTS obs (
day TEXT PRIMARY KEY,
t_avg REAL, t_min REAL, t_max REAL,
precip REAL
)""")
def fetch(d: date) -> bytes:
url = f"https://example-archive.local/csv?date={d.isoformat()}"
return httpx.get(url, timeout=10).content
def upsert(d: date, row: dict) -> None:
DB.execute(
"INSERT INTO obs VALUES (?,?,?,?,?) "
"ON CONFLICT(day) DO UPDATE SET "
"t_avg=excluded.t_avg, t_min=excluded.t_min, "
"t_max=excluded.t_max, precip=excluded.precip",
(d.isoformat(), row["t_avg"], row["t_min"], row["t_max"], row["precip"]),
)
Грабли
- Кодировка. CSV из старых архивов часто
в
cp1251. Надёжно — открывать как байты и декодировать руками сerrors="replace". - Лимиты. Несколько источников на пятой-десятой
тысяче запросов давали 429. Помог простой
asyncio.Semaphore(2)+ случайный sleep. - Дырки в данных. Иногда нет суток вообще. Лучше сразу класть NULL и потом интерполировать в pandas, чем пытаться угадать на этапе загрузки.
Что стало возможным
Самое приятное — простые SQL-вопросы на готовых данных:
SELECT strftime('%Y-%m', day) AS month,
AVG(t_avg) AS t,
SUM(precip) AS rain
FROM obs
WHERE day >= date('now', '-5 years')
GROUP BY month
ORDER BY month;
График по результату я уже строил в обычном matplotlib. Никакой хитрости — просто полезный SQLite на 30 МБ, на котором можно проверять любую гипотезу за секунды.