Отслеживание7 мин чтения
Как выгрузить подписчиков Instagram в CSV на Python
Выгрузка подписчиков публичного аккаунта Instagram в CSV на Python: готовый скрипт, пагинация по курсору, продолжение после обрыва, ошибки 429 и 503.
Содержание
- Как выгрузить подписчиков Instagram в CSV на Python?
- Что понадобится
- Полный скрипт
- Запуск: сначала несколько страниц
- Колонки CSV
- Как работает постраничная загрузка
- Лимиты и ошибки
- Закрытые аккаунты
- Список подписок (following)
- Как открыть файл в Excel или Google Таблицах
- Как сравнить две выгрузки и узнать, кто отписался
- Вариант без кода
- Важно
- Частые вопросы
Как выгрузить подписчиков Instagram в CSV на Python?
Вызывайте эндпоинт /v1/user/followers в цикле, передавайте next_cursor из каждой страницы как cursor в следующем запросе и записывайте пользователей в файл через csv.DictWriter. Скрипт ниже делает именно это, нужна одна библиотека (requests). Он сохраняет место остановки после каждой страницы, поэтому его можно продолжить, а на ответы 429 и 503 ждёт и повторяет запрос. Работает только с публичными аккаунтами.
StoriesFly — это наш API, поэтому мы используем его здесь. Аккаунт Instagram, логин и куки не нужны, нужен только API-ключ.
Что понадобится
- Python 3.10 или новее (в скрипте есть
str | None). - Библиотека
requests:pip install requests. - API-ключ. Войдите через Telegram на /developers/dashboard и создайте ключ, он выглядит как
sf_live_.... Новый аккаунт получает 100 бесплатных запросов (один раз на Telegram-аккаунт), этого хватит, чтобы проверить всё из статьи.
Ключ лучше положить в переменную окружения, а не в код:
export STORIESFLY_KEY=sf_live_your_key_hereВ Windows PowerShell: $env:STORIESFLY_KEY = "sf_live_your_key_here".
Описание эндпоинта: /developers/docs/user-followers (документация на английском).
Полный скрипт
Сохраните как export_followers.py:
"""Export the followers of an Instagram account to a CSV file.
Usage: STORIESFLY_KEY=sf_live_... python export_followers.py nasa --max-pages 5
Each page is one request. The script saves the cursor after every page, so if
it stops (network, empty balance) the next run continues where it left off.
"""
import argparse
import csv
import json
import os
import time
from pathlib import Path
import requests
API = "https://storiesfly.com/api/v1"
KEY = os.environ["STORIESFLY_KEY"]
FIELDS = ["id", "username", "full_name", "verified", "is_private", "profile_pic_url"]
def fetch_page(username: str, cursor: str | None) -> dict:
params = {"username": username}
if cursor:
params["cursor"] = cursor
while True:
r = requests.get(
f"{API}/user/followers",
params=params,
headers={"X-Api-Key": KEY},
timeout=60,
)
if r.status_code == 429: # too fast: wait as told, costs nothing
time.sleep(int(r.headers.get("Retry-After", "1")))
continue
if r.status_code == 503: # failed on their side, refunded: try again
time.sleep(5)
continue
if r.status_code != 200:
err = r.json()
raise SystemExit(f"{r.status_code} {err['code']}: {err['error']}")
return r.json()
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("username")
ap.add_argument("--max-pages", type=int, default=0, help="0 = no limit")
args = ap.parse_args()
username = args.username.lstrip("@")
out = Path(f"{username}_followers.csv")
state = Path(f"{username}_followers.state.json")
cursor = None
if state.exists():
cursor = json.loads(state.read_text())["cursor"]
print("Resuming from the saved cursor")
new_file = not out.exists()
pages = rows = 0
with open(out, "a", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=FIELDS, extrasaction="ignore")
if new_file:
writer.writeheader()
while True:
page = fetch_page(username, cursor)
writer.writerows(page["users"])
f.flush()
pages += 1
rows += len(page["users"])
print(f"page {pages}: +{len(page['users'])} (total {rows})")
if not page["has_more"]:
state.unlink(missing_ok=True)
print("Done: the list has ended")
break
cursor = page["next_cursor"]
state.write_text(json.dumps({"cursor": cursor}))
if args.max_pages and pages >= args.max_pages:
print("Stopped at --max-pages; run again to continue")
break
print(f"{rows} rows written to {out}, {pages} requests spent")
if __name__ == "__main__":
main()Запуск: сначала несколько страниц
У крупных аккаунтов очень много подписчиков, а каждая страница стоит один запрос. Для первого запуска ограничьте число страниц через --max-pages:
python export_followers.py nasa --max-pages 3Вывод выглядит так (значения примерные):
page 1: +50 (total 50)
page 2: +50 (total 100)
page 3: +50 (total 150)
Stopped at --max-pages; run again to continue
150 rows written to nasa_followers.csv, 3 requests spentОткройте nasa_followers.csv и проверьте колонки. Если всё в порядке, запустите ту же команду без --max-pages. Скрипт помнит курсор, поэтому полный запуск продолжится после уже скачанных страниц.
Сколько это стоит
Одна страница это один запрос. Размер страницы зависит от того, что отдаёт Instagram, и меняется. Чтобы оценить расход, выгрузите 3-5 страниц, посчитайте среднее число строк на страницу и поделите на него число подписчиков аккаунта. Получится примерное число запросов на полную выгрузку. Умножьте его на цену запроса со страницы /developers/pricing.
Колонки CSV
| Колонка | Что это |
|---|---|
id | Числовой ID пользователя в Instagram. Не меняется при смене имени |
username | Текущий username |
full_name | Отображаемое имя |
verified | Есть ли галочка верификации |
is_private | Закрыт ли аккаунт самого подписчика |
profile_pic_url | Ссылка на аватар |
Ссылки в profile_pic_url ведут на CDN Instagram и со временем перестают работать. Если нужны сами картинки, скачивайте их сразу. Для сравнения списков во времени берите id, а не username.
Как работает постраничная загрузка
Эндпоинт возвращает страницу пользователей и два поля: has_more и next_cursor. Чтобы получить следующую страницу, отправьте тот же запрос с cursor=<next_cursor>. Цикл в main() делает именно это и останавливается, когда has_more равно false.
После каждой страницы скрипт записывает новый курсор в файл nasa_followers.state.json. Если запуск прервался (ноутбук заснул, пропала сеть, закончился баланс), запустите ту же команду ещё раз: курсор прочитается из файла и загрузка продолжится. CSV открывается в режиме добавления, заголовок пишется один раз, дубликатов нет. Когда список закончился, файл состояния удаляется.
Чтобы сделать новую выгрузку того же аккаунта, удалите nasa_followers.csv и nasa_followers.state.json или переименуйте старый CSV (см. раздел про сравнение ниже).
Лимиты и ошибки
Два случая скрипт обрабатывает сам:
- 429
rate_limited. Вы шлёте запросы слишком часто. Лимит: 1 запрос в секунду до первого пополнения и 3 в секунду на уровне Start. Скрипт ждёт столько секунд, сколько указано в заголовкеRetry-After, и повторяет вызов. За 429 деньги не списываются. - 503
upstream_unavailable. Сбой на нашей стороне. Запрос возвращается на баланс, скрипт ждёт 5 секунд и пробует снова.
Остальные ответы останавливают скрипт с кодом статуса, кодом ошибки и сообщением. Ошибки приходят в JSON вида {"error": "...", "code": "..."}. Какие бывают:
| Статус | Код | Списывается? |
|---|---|---|
| 400 | bad_request | Нет |
| 401 | unauthorized | Нет |
| 402 | credits_exhausted | Нет |
| 403 | private_account | Да |
| 404 | not_found | Да |
| 500 | internal_error | Возвращается |
В каждом ответе есть заголовки, например X-Units-Charged и X-Balance-Remaining. Их можно выводить, чтобы следить за балансом при долгой выгрузке.
Закрытые аккаунты
Подписчиков закрытого аккаунта выгрузить нельзя. API отвечает 403 private_account и данных не отдаёт. Так задумано, это не ошибка скрипта.
Список подписок (following)
Чтобы выгрузить, на кого подписан аккаунт, используйте тот же скрипт с другим эндпоинтом. В fetch_page замените строку:
f"{API}/user/followers",на:
f"{API}/user/following",Заодно поменяйте имена файлов, чтобы две выгрузки не смешались: замените _followers на _following в двух строках Path(...) в main(). Колонки те же. Описание: /developers/docs/user-following.
Как открыть файл в Excel или Google Таблицах
Файл в кодировке UTF-8.
- Google Таблицы: Файл, Импорт, Загрузка, выберите файл. Кодировка определяется правильно.
- Excel: используйте Данные, Из текстового/CSV-файла и проверьте, что кодировка файла UTF-8. Если открыть файл двойным щелчком, Excel может неправильно показать имена с кириллицей и другими не латинскими буквами. Через диалог импорта этого не происходит.
Длинные числовые ID в Excel могут превратиться в запись с экспонентой. При импорте задайте для колонки id тип «Текст», чтобы сохранить все цифры.
Как сравнить две выгрузки и узнать, кто отписался
Сделайте выгрузку сейчас, переименуйте файл и повторите через неделю. Например, переименуйте первый файл в nasa_followers_monday.csv и запустите скрипт снова (он создаст новый nasa_followers.csv). Затем сравните колонки id через множества:
import csv
def ids(path):
with open(path, newline="", encoding="utf-8") as f:
return {row["id"] for row in csv.DictReader(f)}
old, new = ids("nasa_followers_monday.csv"), ids("nasa_followers.csv")
print("Unfollowed:", len(old - new))
print("New followers:", len(new - old))old - new это ID, которые были в первом файле и пропали во втором, new - old наоборот. Чтобы получить имена, найдите эти ID в CSV. Учтите, что удалённый или заблокированный аккаунт тоже пропадает из списка.
Вариант без кода
Если программировать не нужно, у StoriesFly есть выгрузка подписчиков для обычных пользователей: /instagram-followers-export. API из статьи нужен, когда вы хотите запускать выгрузку сами, по расписанию или встроить её в свой инструмент.
Важно
Речь о публичных данных. Соблюдайте правила Instagram и законы вашей страны и не используйте выгруженные списки для спама или травли.
Частые вопросы
Можно ли выгрузить подписчиков закрытого аккаунта Instagram?
Нет. Для закрытого аккаунта API возвращает 403 private_account и список не отдаёт. Выгружать можно только публичные аккаунты.
Сколько запросов уходит на полную выгрузку подписчиков?
Один запрос на страницу. Размер страницы меняется, поэтому запустите 3-5 страниц с --max-pages, поделите строки на страницы, затем число подписчиков на это значение, и получите оценку.
Что будет, если скрипт остановится посередине?
Запустите ту же команду снова. Курсор сохраняется в файл состояния после каждой страницы, скрипт продолжит с этого места и допишет тот же CSV.
Нужен ли аккаунт Instagram или куки?
Нет. Скрипт отправляет только ваш ключ StoriesFly в заголовке X-Api-Key. Входа в Instagram в коде нет.
Списываются ли деньги за неудачные запросы?
Ответы 429 (лимит скорости), 400 (плохой запрос), 401 (неверный ключ) и 402 (пустой баланс) бесплатны. За 503 и 500 деньги возвращаются. Ответы с данными, not_found и private_account оплачиваются.
Можно ли выгрузить и список подписок?
Да. Замените /user/followers на /user/following в скрипте и поменяйте имена файлов. Колонки те же.
