💻
Разработка в IT
Опубликовано:
16.04.2026
Обновлено:
16.04.2026

Парсинг ВКонтакте на Python через VK API: полное руководство

Алексей Иванов

В начале года мне понадобилось проанализировать контент-стратегию конкурентов: собрать последние 500 постов из пяти тематических сообществ ВКонтакте, посчитать среднее число лайков, выявить самые вирусные публикации. Вручную это сотни кликов и копипасты. Через VK API скрипт на 60 строк собрал все данные за 3 минуты. ВКонтакте предоставляет одно из самых открытых API среди социальных сетей: сотни методов, подробная документация, несколько типов токенов доступа.

В этой статье разбираю парсинг ВКонтакте через официальный API: от создания приложения до сбора постов, комментариев, участников и статистики. Каждый шаг с кодом и объяснением лимитов.

Что можно собрать через VK API

API ВКонтакте предоставляет доступ к большинству публичных данных платформы.

  • Профили пользователей: имя, город, дата рождения, образование, место работы
  • Записи со стен пользователей и сообществ (посты, репосты, вложения)
  • Комментарии к записям
  • Список участников групп и подписчиков страниц
  • Друзья и подписки пользователей
  • Фотоальбомы и видеозаписи
  • Товары сообществ (раздел «Маркет»)
  • Лайки и репосты конкретных объектов
  • Обсуждения (топики) в группах
Метод API Что возвращает Макс. за запрос
wall.get Посты со стены 100
wall.getComments Комментарии к посту 100
groups.getMembers Участники группы 1000
users.get Профили пользователей 1000 user_ids
friends.get Список друзей 5000
likes.getList Кто поставил лайк 1000
groups.search Поиск сообществ 1000

Шаг 1: создаём приложение и получаем токен

Создание Standalone-приложения

  1. Перейдите на страницу для разработчиков: dev.vk.com.
  2. Нажмите «Мои приложения» → «Создать приложение».
  3. Выберите тип «Standalone-приложение» (рекомендуется для парсинга).
  4. Укажите название и подтвердите создание.
  5. Запишите ID приложения (client_id) со страницы настроек.

Получение access_token

Токен определяет, к каким данным у вашего скрипта есть доступ. Существуют три типа токенов:

Тип токена Rate limit Возможности
Пользовательский 3 req/s Полный доступ к данным, видимым пользователю
Сообщества 20 req/s Управление группой, рассылки, стена
Сервисный ключ 3 req/s Ограниченный набор публичных методов

Для парсинга чаще всего нужен пользовательский токен. Получение через OAuth:

# Сформируйте ссылку и откройте в браузере
CLIENT_ID = "ваш_client_id"

auth_url = (
    f"https://oauth.vk.com/authorize?"
    f"client_id={CLIENT_ID}"
    f"&redirect_uri=https://oauth.vk.com/blank.html"
    f"&display=page"
    f"&scope=wall,groups,friends,offline"
    f"&response_type=token"
    f"&v=5.199"
)
print(auth_url)
# Откройте ссылку → авторизуйтесь → скопируйте access_token из адресной строки

Параметр scope определяет права доступа: wall для чтения стен, groups для групп, friends для списков друзей, offline для бессрочного токена. После авторизации токен появится в URL после access_token=.

Шаг 2: устанавливаем библиотеку vk_api

pip install vk_api

vk_api это популярная Python-обёртка над VK API. Она упрощает авторизацию, обработку ошибок и пагинацию.

Подключение по токену

import vk_api

token = "ваш_access_token"
vk_session = vk_api.VkApi(token=token)
vk = vk_session.get_api()

# Проверка: получаем информацию о себе
me = vk.users.get(fields="city,bdate")
print(me)
# [{'id': 123456, 'first_name': 'Иван', 'last_name': 'Петров',...}]

Метод get_api() возвращает объект, через который вызываются API-методы как обычные методы Python.

Шаг 3: парсим посты со стены сообщества

import vk_api
import time
import csv

token = "ваш_access_token"
vk_session = vk_api.VkApi(token=token)
vk = vk_session.get_api()

GROUP_ID = -1  # ID сообщества (отрицательное число)


def get_all_posts(owner_id, max_posts=500):
    all_posts = []
    offset = 0
    count = 100  # максимум за запрос

    while len(all_posts) < max_posts:
        response = vk.wall.get(
            owner_id=owner_id,
            count=count,
            offset=offset
        )

        posts = response["items"]
        if not posts:
            break

        all_posts.extend(posts)
        offset += count
        print(f"Загружено: {len(all_posts)} / {response['count']}")

        time.sleep(0.35)  # 3 запроса/сек для пользовательского токена

        if len(posts) < count:
            break

    return all_posts[:max_posts]


posts = get_all_posts(GROUP_ID, max_posts=500)
print(f"Собрано {len(posts)} постов")

Параметр owner_id для сообществ указывается с минусом: -1 для публичной страницы VK, -123456 для группы с ID 123456. wall.get возвращает до 100 постов за запрос, поэтому для сбора всех нужна пагинация через offset.

Сохранение в CSV

import csv
from datetime import datetime

with open("vk_posts.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow([
        "date", "text", "likes", "comments", "reposts", "views", "attachments"
    ])

    for post in posts:
        date = datetime.fromtimestamp(post["date"]).strftime("%Y-%m-%d %H:%M")
        text = post.get("text", "").replace("\n", " ")[:500]
        likes = post.get("likes", {}).get("count", 0)
        comments = post.get("comments", {}).get("count", 0)
        reposts = post.get("reposts", {}).get("count", 0)
        views = post.get("views", {}).get("count", 0)

        attachments = []
        for att in post.get("attachments", []):
            attachments.append(att["type"])

        writer.writerow([
            date, text, likes, comments, reposts, views,
            ", ".join(attachments)
        ])

print(f"Сохранено {len(posts)} постов в vk_posts.csv")

Поле date в VK API приходит как Unix timestamp. datetime.fromtimestamp() конвертирует его в читаемую дату. Вложения (фото, видео, ссылки) хранятся в массиве attachments как объекты с полем type.

Шаг 4: парсим комментарии к постам

def get_comments(owner_id, post_id, max_comments=200):
    all_comments = []
    offset = 0
    count = 100

    while len(all_comments) < max_comments:
        try:
            response = vk.wall.getComments(
                owner_id=owner_id,
                post_id=post_id,
                count=count,
                offset=offset,
                sort="asc"
            )
        except vk_api.exceptions.ApiError as e:
            print(f"Ошибка API: {e}")
            break

        comments = response["items"]
        if not comments:
            break

        all_comments.extend(comments)
        offset += count
        time.sleep(0.35)

    return all_comments[:max_comments]


# Собираем комментарии к первым 50 постам
all_comments = []
for post in posts[:50]:
    if post["comments"]["count"] > 0:
        comments = get_comments(GROUP_ID, post["id"])
        for c in comments:
            c["_post_id"] = post["id"]
        all_comments.extend(comments)
        print(f"Пост {post['id']}: {len(comments)} комментариев")

print(f"Всего комментариев: {len(all_comments)}")

wall.getComments возвращает до 100 комментариев за запрос. Поле _post_id добавляется вручную, чтобы связать комментарий с постом в итоговом датасете.

Шаг 5: парсим участников группы

def get_group_members(group_id, fields="city,sex,bdate", max_members=10000):
    all_members = []
    offset = 0
    count = 1000  # максимум за запрос

    while len(all_members) < max_members:
        try:
            response = vk.groups.getMembers(
                group_id=group_id,
                count=count,
                offset=offset,
                fields=fields
            )
        except vk_api.exceptions.ApiError as e:
            print(f"Ошибка: {e}")
            break

        members = response["items"]
        if not members:
            break

        all_members.extend(members)
        offset += count
        print(f"Участников: {len(all_members)} / {response['count']}")

        time.sleep(0.35)

        if len(members) < count:
            break

    return all_members[:max_members]


members = get_group_members("python_scripts", fields="city,sex,bdate,last_seen")
print(f"Собрано {len(members)} участников")

groups.getMembers принимает group_id без минуса (числовой ID или короткое имя). Параметр fields определяет, какие поля профиля загружать. Каждый запрос возвращает до 1000 участников.

Аналитика: распределение по городам

from collections import Counter

cities = []
for member in members:
    city = member.get("city", {}).get("title", "Не указан")
    cities.append(city)

top_cities = Counter(cities).most_common(10)
for city, count in top_cities:
    pct = count / len(members) * 100
    print(f"{city}: {count} ({pct:.1f}%)")

Шаг 6: метод execute — 25 запросов за один

Метод execute позволяет отправить до 25 API-вызовов в одном запросе. Это критически важно для массового парсинга, потому что пользовательский токен ограничен 3 запросами в секунду.

def get_posts_fast(owner_id, total=2500):
    """Собирает до 2500 постов за один вызов execute (25 × 100)."""
    code = f"""
    var posts = [];
    var offset = 0;
    var i = 0;
    while (i < 25) {{
        var response = API.wall.get({{
            "owner_id": {owner_id},
            "count": 100,
            "offset": offset
        }});
        posts = posts + response.items;
        offset = offset + 100;
        i = i + 1;
        if (response.items.length < 100) {{
            i = 25;
        }}
    }}
    return posts;
    """

    response = vk_session.method("execute", {"code": code})
    return response


posts = get_posts_fast(-1)
print(f"Получено {len(posts)} постов за 1 запрос")

Код внутри execute написан на VKScript — подмножестве JavaScript. Каждый вызов API.wall.get() внутри execute считается отдельным запросом, но все 25 выполняются за одно обращение к серверу. Это ускоряет парсинг в 25 раз.

execute для массового сбора профилей

def get_users_batch(user_ids, fields="city,sex,bdate"):
    """Получает профили пользователей пакетами по 25 000 за вызов."""
    all_users = []
    # users.get принимает до 1000 ID, execute вмещает 25 вызовов
    chunk_size = 1000
    chunks = [user_ids[i : i + chunk_size] for i in range(0, len(user_ids), chunk_size)]

    for batch_start in range(0, len(chunks), 25):
        batch = chunks[batch_start : batch_start + 25]

        calls = []
        for chunk in batch:
            ids_str = ",".join(str(uid) for uid in chunk)
            calls.append(
                f'API.users.get({{"user_ids":"{ids_str}","fields":"{fields}"}})'
            )

        code = f"return [{','.join(calls)}];"

        try:
            response = vk_session.method("execute", {"code": code})
            for result in response:
                if result:
                    all_users.extend(result)
        except Exception as e:
            print(f"Ошибка execute: {e}")

        time.sleep(0.35)
        print(f"Обработано: {len(all_users)} профилей")

    return all_users

Один вызов execute с 25 вложенными users.get обрабатывает до 25 000 профилей. Для группы в 100 000 участников нужно всего 4 вызова execute.

Шаг 7: сбор статистики сообщества

def analyze_community(owner_id, post_count=100):
    posts = get_all_posts(owner_id, max_posts=post_count)

    total_likes = 0
    total_comments = 0
    total_reposts = 0
    total_views = 0
    best_post = None
    best_er = 0

    for post in posts:
        likes = post.get("likes", {}).get("count", 0)
        comments = post.get("comments", {}).get("count", 0)
        reposts = post.get("reposts", {}).get("count", 0)
        views = post.get("views", {}).get("count", 0)

        total_likes += likes
        total_comments += comments
        total_reposts += reposts
        total_views += views

        # Engagement Rate по охвату
        er = (likes + comments + reposts) / max(views, 1) * 100
        if er > best_er:
            best_er = er
            best_post = post

    n = len(posts)
    print(f"Постов проанализировано: {n}")
    print(f"Средние лайки: {total_likes / n:.0f}")
    print(f"Средние комментарии: {total_comments / n:.0f}")
    print(f"Средние репосты: {total_reposts / n:.0f}")
    print(f"Средние просмотры: {total_views / n:.0f}")
    print(f"Лучший пост (ER={best_er:.2f}%): {best_post['text'][:100]}")


analyze_community(-1, post_count=200)

Engagement Rate (ER) считается как отношение суммы лайков, комментариев и репостов к просмотрам. Этот показатель позволяет сравнивать вовлечённость аудитории между сообществами разного размера.

Лимиты и обработка ошибок

Rate limits

VK API ограничивает число запросов в секунду. Превышение возвращает ошибку с кодом 6 («Too many requests per second»).

Установок приложения Лимит
До 10 000 5 req/s
До 100 000 8 req/s
До 1 000 000 20 req/s
Более 1 000 000 35 req/s

Автоматический retry при ошибках

from vk_api.utils import get_random_id
import time


def safe_api_call(method, params, max_retries=5):
    for attempt in range(max_retries):
        try:
            return vk_session.method(method, params)
        except vk_api.exceptions.ApiError as e:
            error_code = e.code

            if error_code == 6:  # Too many requests
                wait = 1 + attempt * 0.5
                print(f"Rate limit, жду {wait} сек...")
                time.sleep(wait)
            elif error_code == 29:  # Rate limit reached
                print("Суточный лимит метода. Ждём 1 час.")
                time.sleep(3600)
            elif error_code == 5:  # Auth error
                print("Ошибка авторизации. Проверьте токен.")
                raise
            else:
                print(f"Ошибка API #{error_code}: {e}")
                time.sleep(2)

    print(f"Не удалось выполнить {method} после {max_retries} попыток")
    return None

Код ошибки 6 означает превышение лимита запросов. Код 29 — превышение лимита конкретного метода за сутки. Код 5 — невалидный или просроченный токен.

Альтернатива: готовые парсеры

Если писать код не хочется, существуют готовые инструменты для парсинга VK.

Инструмент Тип Возможности Стоимость
TargetHunter SaaS-сервис Участники, активности, пересечения групп Платный
Церебро Таргет SaaS-сервис Аудиторный парсинг для рекламы Платный
vk-parser (PyPI) Python-библиотека Пользователи, посты, сообщества, соцграф Бесплатный
Pepper.Ninja SaaS-сервис Комплексный парсинг аудитории Платный

Готовые сервисы работают через тот же VK API, но предоставляют визуальный интерфейс и предустановленные сценарии.

Юридические аспекты

Парсинг ВКонтакте через API легальнее прямого парсинга HTML, но не свободен от рисков.

  • Пользовательское соглашение VK не запрещает использование API, но запрещает массовый автоматизированный сбор данных без согласия платформы.
  • ФЗ-152 «О персональных данных»: Профили пользователей ВКонтакте содержат персональные данные. Их массовый сбор без согласия субъектов нарушает закон. Штрафы для физических лиц до 10 000 руб., для юридических до 300 000 руб. за обработку без согласия.
  • Ст. 272 УК РФ: Обход технических средств защиты (CAPTCHA, блокировки) может квалифицироваться как неправомерный доступ к компьютерной информации. Штраф до 200 000 руб. или лишение свободы до 2 лет.
  • Безопасный путь: Используйте официальный API, собирайте только публично доступные данные, обезличивайте результаты для аналитики, не перепродавайте персональные данные.

Неочевидные детали

Первый факт: wall.get с параметром filter=owner возвращает только посты от имени сообщества, исключая посты участников на стене. Без фильтра возвращается всё, включая предложенные записи.

Второй факт: токен с правом offline не имеет срока действия, но VK может аннулировать его при смене пароля пользователем или при подозрительной активности.

Третий факт: groups.getMembers возвращает участников в порядке вступления (от новых к старым). Для группы в 500 000 человек полный сбор займёт 500 запросов, или 20 вызовов execute (около 7 секунд).

Четвёртый факт: VK возвращает удалённых и заблокированных пользователей с полем deactivated вместо профиля. Парсер должен проверять это поле, иначе.get("city") упадёт с KeyError.

for member in members:
    if "deactivated" in member:
        continue  # пропускаем удалённые аккаунты
    city = member.get("city", {}).get("title", "")

Пятый факт: метод execute ограничен 25 вызовами API внутри, но сам код VKScript не может превышать 100 000 символов. Для сложных сценариев разбивайте логику на несколько вызовов execute.

FAQ

Можно ли парсить закрытые группы?

Только если ваш аккаунт является их участником. API возвращает данные в рамках прав текущего пользователя. Если группа закрыта и вы не состоите в ней, wall.get вернёт ошибку доступа.

Чем отличается сервисный ключ от пользовательского токена?

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

Как собрать все лайки конкретного поста?

Используйте метод likes.getList с параметрами type=post, owner_id и item_id. Метод возвращает до 1000 ID за запрос. Для постов с большим числом лайков нужна пагинация.

Можно ли собрать личные сообщения?

Нет. VK закрыл доступ к messages.get для обычных приложений. Чтение сообщений доступно только через Callback API сообществ для ботов.

Как ускорить парсинг?

Используйте метод execute для объединения до 25 запросов в один. Для токена сообщества лимит 20 req/s, что позволяет отправлять 20 execute-запросов в секунду, содержащих по 25 API-вызовов каждый (500 вызовов/сек).

Мой совет: VK API это самый удобный для парсинга API среди российских социальных сетей. Начните с пользовательского токена и wall.get для сбора постов. Добавьте execute когда упрётесь в лимиты. Для аудиторного анализа комбинируйте groups.getMembers + users.get. Храните токен в переменной окружения, не хардкодьте его в скрипте. И помните: API даёт легальный канал доступа к данным, но массовый сбор персональных данных для перепродажи остаётся нарушением закона.

Читайте также