# --- НАСТРОЙКИ ---
VK_TOKEN = 'vk1.a.siOvb9OmT4WPRWOHfXC5olSr2GI3GGKTSXWeX0oAOD7b9_WNGtBDre1uZ2I1ImnXBUV0HnQYns_VHaQepSXhiGxRslTwioedTwe9qUlAtYsjdhHS4y7xqO_5Of4C6xxU79hx1qDHN9nPGRBO_zHekWWxb7gDCd0wo9fLa4K2idpfBfusz8B_O53HfGKaCtmtnF_IIbUF_eIEHbgz6vs2VA'
# ID групп для проверки (по одному на строке)
GROUP_IDS = """
216946255
229580483
216945220
225144088
225427887
225423241
196604934
51334734
225027346
225073535
"""
# Настройки для больших списков
SAVE_EVERY_N_GROUPS = 50 # Сохранять результаты каждые N групп
RATE_LIMIT_DELAY = 300 # Пауза при превышении лимита (5 минут)
GROUP_PROCESSING_DELAY = 3.0 # Пауза между группами (3 секунды)
RESUME_FROM_GROUP = 0 # Начать с группы номер N (0 = сначала)
# Дополнительные настройки для избежания лимитов
REQUEST_TIMEOUT = 60 # Таймаут запроса (60 секунд)
MAX_RETRIES = 5 # Максимум попыток (увеличено)
RETRY_DELAY = 10 # Пауза между повторами (10 секунд)
# --- КОНЕЦ НАСТРОЕК ---
import requests
import time
import re
import json
import os
from typing import List, Dict, Any
from datetime import datetime
class VKCommunitiesChecker:
def __init__(self, access_token: str):
"""
Инициализация проверщика сообществ ВК
"""
self.access_token = access_token
self.api_url = "https://api.vk.com/method/"
self.api_version = "5.131"
# Файлы для сохранения результатов
self.hidden_members_file = "groups_with_hidden_members.txt"
self.non_russian_names_file = "groups_with_non_russian_names.txt"
self.progress_file = "progress.json"
self.error_log_file = "error_log.txt"
# Допустимые символы
self.allowed_special_chars = set(['|', '/', '[', ']', ' ', '-', '_', '.', '!', '?',
'(', ')', '№', '@', '#', '$', '%', '^', '&', '*',
'+', '=', '~', '`', ':', ';', '"', "'", '<', '>',
',', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])
# Счетчики и результаты
self.processed_groups = 0
self.hidden_members_groups = []
self.non_russian_names_groups = []
self.skipped_groups = [] # Группы, которые не удалось обработать
# Статистика запросов
self.total_requests = 0
self.rate_limit_hits = 0
# Загружаем прогресс если есть
self.load_progress()
def log_error(self, message: str):
"""Логирование ошибок в файл"""
try:
with open(self.error_log_file, 'a', encoding='utf-8') as f:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
f.write(f"[{timestamp}] {message}\n")
except:
pass
def parse_group_ids(self, group_ids_string: str) -> List[int]:
"""Парсинг строки с ID групп"""
group_ids = []
for line in group_ids_string.strip().split('\n'):
line = line.strip()
if line and line.isdigit():
group_ids.append(int(line))
return group_ids
def make_api_request(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
"""Выполнение запроса к API ВК с улучшенной обработкой лимитов"""
max_retries = MAX_RETRIES
retry_count = 0
while retry_count < max_retries:
try:
params['access_token'] = self.access_token
params['v'] = self.api_version
# Увеличенный таймаут
response = requests.get(
f"{self.api_url}{method}",
params=params,
timeout=REQUEST_TIMEOUT
)
response.raise_for_status()
data = response.json()
self.total_requests += 1
if 'error' in data:
error_code = data['error'].get('error_code', 0)
error_msg = data['error'].get('error_msg', 'Unknown error')
# Обработка rate limit
if error_code == 29: # Rate limit reached
self.rate_limit_hits += 1
print(f" ⏳ Rate limit reached (попадание #{self.rate_limit_hits})")
print(f" 🕐 Ожидание {RATE_LIMIT_DELAY} секунд ({RATE_LIMIT_DELAY//60} мин)...")
# Логируем в файл
self.log_error(f"Rate limit hit #{self.rate_limit_hits} on method {method}, group_id: {params.get('group_id', 'N/A')}")
time.sleep(RATE_LIMIT_DELAY)
retry_count += 1
# Дополнительная пауза после rate limit
if retry_count < max_retries:
print(f" ⏳ Дополнительная пауза {RETRY_DELAY} сек перед повтором...")
time.sleep(RETRY_DELAY)
continue
# Другие ошибки API
self.log_error(f"API error {error_code}: {error_msg} on method {method}")
return {'error': {'code': error_code, 'msg': error_msg}}
return data.get('response', {})
except requests.Timeout:
retry_count += 1
error_msg = f"Timeout error (попытка {retry_count}/{max_retries})"
print(f" ⏰ {error_msg}")
self.log_error(f"Timeout on method {method}: {error_msg}")
if retry_count < max_retries:
time.sleep(RETRY_DELAY)
else:
return {'timeout_error': error_msg}
except requests.RequestException as e:
retry_count += 1
error_msg = f"Network error (попытка {retry_count}/{max_retries}): {e}"
print(f" 🌐 {error_msg}")
self.log_error(f"Network error on method {method}: {error_msg}")
if retry_count < max_retries:
time.sleep(RETRY_DELAY)
else:
return {'network_error': str(e)}
except Exception as e:
error_msg = f"Unexpected error: {e}"
print(f" ❌ {error_msg}")
self.log_error(f"Unexpected error on method {method}: {error_msg}")
return {'unexpected_error': str(e)}
return {'max_retries_exceeded': f'Exceeded {max_retries} retries'}
def safe_get_error_info(self, response_data: Any) -> tuple:
"""Безопасное получение информации об ошибке"""
try:
if isinstance(response_data, dict) and 'error' in response_data:
error_info = response_data['error']
if isinstance(error_info, dict):
code = error_info.get('code', 0)
msg = error_info.get('msg', '')
return code, msg
except:
pass
return 0, ""
def analyze_members_access(self, group_id: int) -> bool:
"""Анализ доступности участников"""
print(f" 🔍 Проверяем доступ к участникам...")
params = {
'group_id': group_id,
'count': 2, # Уменьшаем количество для экономии лимитов
'offset': 0,
'fields': 'first_name'
}
response = self.make_api_request('groups.getMembers', params)
# Обработка различных типов ошибок
if 'error' in response:
error_code, error_msg = self.safe_get_error_info(response)
print(f" ⚠️ Ошибка API: код {error_code} - {error_msg}")
# Специфические ошибки скрытых участников
if error_code == 15 and 'hide members' in error_msg.lower():
print(f" ✅ ОПРЕДЕЛЕНО: СКРЫТЫ (участники скрыты группой)")
return True
# Другие коды ошибок доступа
if error_code in [7, 15, 18, 30]:
print(f" ✅ ОПРЕДЕЛЕНО: СКРЫТЫ (нет доступа)")
return True
# Rate limit и другие временные ошибки
if error_code in [29, 6, 10]:
print(f" ⚠️ ОПРЕДЕЛЕНО: СКРЫТЫ (временная ошибка)")
return True
# Обработка технических ошибок
error_types = ['network_error', 'timeout_error', 'unexpected_error', 'max_retries_exceeded']
for error_type in error_types:
if error_type in response:
print(f" ⚠️ ОПРЕДЕЛЕНО: СКРЫТЫ ({error_type}: {response[error_type]})")
return True
# Успешное получение участников
if 'items' in response and isinstance(response['items'], list):
items_count = len(response['items'])
total_count = response.get('count', 0)
print(f" 📊 Получено {items_count} из {total_count} участников")
# Известные скрытые группы
if group_id in [220647061]:
print(f" 📋 Известная скрытая группа (вы админ, но для других скрыты)")
return True
print(f" ✅ ОПРЕДЕЛЕНО: ОТКРЫТЫ (получен список участников)")
return False
print(f" ⚠️ ОПРЕДЕЛЕНО: СКРЫТЫ (нет данных об участниках)")
return True
def has_non_russian_chars(self, text: str) -> bool:
"""Проверка наличия английских букв в тексте"""
for char in text:
if char.isalpha():
if not (ord('а') <= ord(char.lower()) <= ord('я') or
ord('А') <= ord(char.upper()) <= ord('Я') or
char.lower() in ['ё', 'Ё']):
if char not in self.allowed_special_chars:
return True
return False
def get_group_info(self, group_id: int) -> Dict[str, Any]:
"""Получение информации о группе с защитой от ошибок"""
print(f" 📋 Получаем информацию о группе...")
params = {
'group_ids': group_id,
'fields': 'name,type,is_closed,members_count'
}
response = self.make_api_request('groups.getById', params)
# Проверяем, что ответ корректный
if isinstance(response, list) and len(response) > 0:
return response[0]
elif isinstance(response, dict):
if 'error' in response:
error_code, error_msg = self.safe_get_error_info(response)
print(f" ⚠️ Ошибка получения информации: {error_code} - {error_msg}")
self.log_error(f"Failed to get info for group {group_id}: {error_code} - {error_msg}")
else:
# Возможно технические ошибки
for error_type in ['network_error', 'timeout_error', 'unexpected_error', 'max_retries_exceeded']:
if error_type in response:
print(f" ⚠️ Техническая ошибка: {response[error_type]}")
self.log_error(f"Technical error for group {group_id}: {error_type} - {response[error_type]}")
break
return {}
else:
print(f" ⚠️ Неожиданный формат ответа от API")
self.log_error(f"Unexpected response format for group {group_id}: {type(response)}")
return {}
def save_progress(self):
"""Сохранение прогресса в файл"""
progress_data = {
'processed_groups': self.processed_groups,
'hidden_members_groups': self.hidden_members_groups,
'non_russian_names_groups': self.non_russian_names_groups,
'skipped_groups': self.skipped_groups,
'total_requests': self.total_requests,
'rate_limit_hits': self.rate_limit_hits,
'timestamp': time.time()
}
try:
with open(self.progress_file, 'w', encoding='utf-8') as f:
json.dump(progress_data, f, ensure_ascii=False, indent=2)
print(f" 💾 Прогресс сохранен (обработано {self.processed_groups} групп, запросов: {self.total_requests}, лимитов: {self.rate_limit_hits})")
except Exception as e:
print(f" ⚠️ Ошибка сохранения прогресса: {e}")
self.log_error(f"Failed to save progress: {e}")
def load_progress(self):
"""Загрузка прогресса из файла"""
if os.path.exists(self.progress_file):
try:
with open(self.progress_file, 'r', encoding='utf-8') as f:
progress_data = json.load(f)
self.processed_groups = progress_data.get('processed_groups', 0)
self.hidden_members_groups = progress_data.get('hidden_members_groups', [])
self.non_russian_names_groups = progress_data.get('non_russian_names_groups', [])
self.skipped_groups = progress_data.get('skipped_groups', [])
self.total_requests = progress_data.get('total_requests', 0)
self.rate_limit_hits = progress_data.get('rate_limit_hits', 0)
print(f"📂 Загружен прогресс:")
print(f" 👥 Обработано групп: {self.processed_groups}")
print(f" 🔒 Скрытые участники: {len(self.hidden_members_groups)}")
print(f" 🔤 Английские названия: {len(self.non_russian_names_groups)}")
print(f" ⏭️ Пропущенные группы: {len(self.skipped_groups)}")
print(f" 📡 Всего запросов: {self.total_requests}")
print(f" 🚫 Превышений лимита: {self.rate_limit_hits}")
except Exception as e:
print(f"⚠️ Ошибка загрузки прогресса: {e}")
self.log_error(f"Failed to load progress: {e}")
def save_final_results(self):
"""Сохранение финальных результатов"""
try:
# Сохраняем группы со скрытыми участниками
if self.hidden_members_groups:
with open(self.hidden_members_file, 'w', encoding='utf-8') as f:
for group_id in self.hidden_members_groups:
f.write(f"{group_id}\n")
print(f"📄 Группы со скрытыми участниками сохранены в '{self.hidden_members_file}' ({len(self.hidden_members_groups)} групп)")
else:
print("✅ Нет групп со скрытыми участниками")
# Сохраняем группы с английскими символами
if self.non_russian_names_groups:
with open(self.non_russian_names_file, 'w', encoding='utf-8') as f:
for group_id in self.non_russian_names_groups:
f.write(f"{group_id}\n")
print(f"📄 Группы с английскими символами сохранены в '{self.non_russian_names_file}' ({len(self.non_russian_names_groups)} групп)")
else:
print("✅ Нет групп с английскими символами в названии")
# Сохраняем пропущенные группы
if self.skipped_groups:
with open("skipped_groups.txt", 'w', encoding='utf-8') as f:
for group_id in self.skipped_groups:
f.write(f"{group_id}\n")
print(f"⚠️ Пропущенные группы сохранены в 'skipped_groups.txt' ({len(self.skipped_groups)} групп)")
# Удаляем файл прогресса после успешного завершения
if os.path.exists(self.progress_file):
os.remove(self.progress_file)
print("🗑️ Временный файл прогресса удален")
except Exception as e:
print(f"❌ Ошибка сохранения результатов: {e}")
self.log_error(f"Failed to save final results: {e}")
def check_communities(self, group_ids_string: str):
"""Основная функция проверки сообществ"""
print("🔍 Начинаем проверку сообществ ВКонтакте...\n")
groups = self.parse_group_ids(group_ids_string)
if not groups:
print("❌ Не удалось получить список групп. Проверьте GROUP_IDS.")
return
total_groups = len(groups)
print(f"📋 Найдено {total_groups} групп для проверки")
print(f"⏰ Настройки: пауза между группами {GROUP_PROCESSING_DELAY}с, при лимите {RATE_LIMIT_DELAY//60}мин")
# Начинаем с указанной группы
start_index = max(0, RESUME_FROM_GROUP)
if start_index > 0:
print(f"🔄 Продолжаем с группы номер {start_index + 1}")
print()
try:
for index in range(start_index, total_groups):
group_id = groups[index]
current_num = index + 1
print(f"📊 Проверяем группу {current_num}/{total_groups} (ID: {group_id})")
try:
group_info = self.get_group_info(group_id)
if not group_info:
print(f" ❌ Не удалось получить информацию о группе {group_id}")
self.skipped_groups.append(group_id)
self.processed_groups += 1
time.sleep(GROUP_PROCESSING_DELAY)
continue
group_name = group_info.get('name', f'Группа {group_id}')
group_type = group_info.get('type', 'unknown')
is_closed = group_info.get('is_closed', 0)
members_count = group_info.get('members_count', 0)
print(f" 📝 Название: {group_name}")
print(f" 📊 Тип: {group_type}, Закрытость: {is_closed}, Участников: {members_count}")
# Проверка 1: Скрытые участники
if self.analyze_members_access(group_id):
print(f" 🔒 РЕЗУЛЬТАТ: УЧАСТНИКИ СКРЫТЫ от обычных пользователей")
self.hidden_members_groups.append(group_id)
else:
print(f" 👥 РЕЗУЛЬТАТ: УЧАСТНИКИ ОТКРЫТЫ для всех")
# Проверка 2: Английские символы
if self.has_non_russian_chars(group_name):
print(f" 🔤 Найдены английские буквы в названии")
self.non_russian_names_groups.append(group_id)
else:
print(f" ✅ Название содержит только допустимые символы")
self.processed_groups += 1
# Промежуточное сохранение каждые N групп
if self.processed_groups % SAVE_EVERY_N_GROUPS == 0:
self.save_progress()
print(f"💾 Промежуточное сохранение: {self.processed_groups}/{total_groups} групп обработано")
print("-" * 60)
# УВЕЛИЧЕННАЯ ПАУЗА между группами
time.sleep(GROUP_PROCESSING_DELAY)
except KeyboardInterrupt:
print(f"\n⚠️ Прерывание пользователем на группе {current_num}")
self.save_progress()
print(f"💾 Прогресс сохранен. Для продолжения установите RESUME_FROM_GROUP = {index}")
return
except Exception as e:
error_msg = f"Ошибка при обработке группы {group_id}: {e}"
print(f" ❌ {error_msg}")
self.log_error(error_msg)
self.skipped_groups.append(group_id)
self.processed_groups += 1
time.sleep(GROUP_PROCESSING_DELAY)
continue
except Exception as e:
error_msg = f"Критическая ошибка: {e}"
print(f"❌ {error_msg}")
self.log_error(error_msg)
self.save_progress()
return
# Финальное сохранение
self.save_final_results()
self.print_statistics(total_groups)
def print_statistics(self, total: int):
"""Вывод статистики"""
print("="*50)
print("📈 ИТОГОВАЯ СТАТИСТИКА")
print("="*50)
print(f"Всего групп в списке: {total}")
print(f"Обработано групп: {self.processed_groups}")
print(f"Пропущено групп: {len(self.skipped_groups)}")
print(f"Групп со скрытыми участниками: {len(self.hidden_members_groups)}")
print(f"Групп с английскими символами: {len(self.non_russian_names_groups)}")
print(f"Групп без нарушений: {self.processed_groups - len(set(self.hidden_members_groups + self.non_russian_names_groups))}")
print(f"Всего API запросов: {self.total_requests}")
print(f"Превышений лимита: {self.rate_limit_hits}")
print("="*50)
def main():
print("🚀 Скрипт проверки сообществ ВКонтакте")
print("="*50)
if not VK_TOKEN or VK_TOKEN == 'ваш_токен_здесь':
print("❌ Укажите токен доступа в настройках!")
return
if not GROUP_IDS.strip():
print("❌ Список групп не указан в настройках!")
return
checker = VKCommunitiesChecker(VK_TOKEN)
checker.check_communities(GROUP_IDS)
if __name__ == "__main__":
main()