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

В начале года мне понадобилось проанализировать контент-стратегию конкурентов: собрать последние 500 постов из пяти тематических сообществ ВКонтакте, посчитать среднее число лайков, выявить самые вирусные публикации. Вручную это сотни кликов и копипасты. Через VK API скрипт на 60 строк собрал все данные за 3 минуты. ВКонтакте предоставляет одно из самых открытых API среди социальных сетей: сотни методов, подробная документация, несколько типов токенов доступа.
В этой статье разбираю парсинг ВКонтакте через официальный API: от создания приложения до сбора постов, комментариев, участников и статистики. Каждый шаг с кодом и объяснением лимитов.
Что можно собрать через VK API
API ВКонтакте предоставляет доступ к большинству публичных данных платформы.
- Профили пользователей: имя, город, дата рождения, образование, место работы
- Записи со стен пользователей и сообществ (посты, репосты, вложения)
- Комментарии к записям
- Список участников групп и подписчиков страниц
- Друзья и подписки пользователей
- Фотоальбомы и видеозаписи
- Товары сообществ (раздел «Маркет»)
- Лайки и репосты конкретных объектов
- Обсуждения (топики) в группах
Шаг 1: создаём приложение и получаем токен
Создание Standalone-приложения
- Перейдите на страницу для разработчиков: dev.vk.com.
- Нажмите «Мои приложения» → «Создать приложение».
- Выберите тип «Standalone-приложение» (рекомендуется для парсинга).
- Укажите название и подтвердите создание.
- Запишите ID приложения (client_id) со страницы настроек.
Получение access_token
Токен определяет, к каким данным у вашего скрипта есть доступ. Существуют три типа токенов:
Для парсинга чаще всего нужен пользовательский токен. Получение через 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»).
Автоматический 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.
Готовые сервисы работают через тот же 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 даёт легальный канал доступа к данным, но массовый сбор персональных данных для перепродажи остаётся нарушением закона.
