Files
Zilant2025/vk_load.py
T
2025-09-25 20:43:09 +03:00

249 lines
9.8 KiB
Python

import mysql.connector
import requests
import json
import os
import time
import random
from datetime import datetime
from mysql.connector import errorcode
from dotenv import load_dotenv
from ensure_db import ensure_database_structure
# Загрузка переменных окружения из .env файла
load_dotenv()
# Получение конфигурации из переменных окружения
VK_GROUP_ID = int(os.getenv('VK_GROUP_ID', -3342146))
VK_ACCESS_TOKEN = os.getenv('VK_ACCESS_TOKEN', '')
VK_API_VERSION = os.getenv('VK_API_VERSION', '5.131')
IS_EVENT = os.getenv('IS_EVENT', 'false').lower() in ('true', '1', 'yes', 'on')
AUTO_PUBLISH = os.getenv('AUTO_PUBLISH', 'false').lower() in ('true', '1', 'yes', 'on')
LOG_FILE = os.getenv('LOG_FILE', 'vk_loader.log') # Путь к лог-файлу
LOG_PREFIX = "VK_loader" # Уникальный префикс для идентификации скрипта
# Конфигурация MariaDB из .env
DB_CONFIG = {
'host': os.getenv('MDB_HOST', 'localhost'),
'user': os.getenv('MDB_USER', 'root'),
'password': os.getenv('MDB_PW', ''),
'database': os.getenv('MDBASE', 'vk_posts')
}
def log_message(message, max_retries=5, retry_delay=0.1):
"""
Записывает сообщение в лог-файл с обработкой блокировок
и идентификатором скрипта
"""
if not LOG_FILE:
return
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# Добавляем идентификатор скрипта к сообщению
log_line = f"[{timestamp}] [{LOG_PREFIX}] {message}\n"
for attempt in range(max_retries):
try:
with open(LOG_FILE, 'a', encoding='utf-8') as log:
log.write(log_line)
return True
except (IOError, OSError) as e:
if "locked" in str(e).lower() and attempt < max_retries - 1:
# Случайная задержка для уменьшения коллизий
sleep_time = retry_delay * (1 + random.random() * 0.5)
time.sleep(sleep_time)
else:
# Если не удалось записать после всех попыток
print(f"Ошибка записи в лог: {e}")
print(f"Сообщение для лога: {log_line.strip()}")
return False
def get_vk_posts():
"""Получает последние 10 постов из VK группы"""
url = 'https://api.vk.ru/method/wall.get'
params = {
'owner_id': VK_GROUP_ID,
'count': 10,
'access_token': VK_ACCESS_TOKEN,
'v': VK_API_VERSION,
'extended': 1
}
try:
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
if 'error' in data:
error_msg = f"VK API Error: {data['error']['error_msg']}"
log_message(error_msg)
raise Exception(error_msg)
return data['response']['items']
except requests.exceptions.RequestException as e:
error_msg = f"Ошибка подключения к VK API: {e}"
log_message(error_msg)
raise Exception(error_msg)
def process_post(post):
"""Извлекает необходимые данные из поста VK"""
post_id = post['id']
vk_post_url = f"https://vk.ru/wall{VK_GROUP_ID}_{post_id}"
published_at = datetime.utcfromtimestamp(post['date']).strftime('%Y-%m-%d %H:%M:%S')
# Инициализация полей опроса
is_poll = 0
poll_question = None
poll_options = None
poll_multiple = False
poll_end_date = None
# Инициализация поля изображения
image_url = None
# Обработка вложений
for attachment in post.get('attachments', []):
if attachment['type'] == 'poll':
is_poll = 1
poll = attachment['poll']
poll_question = poll['question']
poll_options = json.dumps([answer['text'] for answer in poll['answers']])
poll_multiple = bool(poll.get('multiple', False))
if poll.get('end_date', 0) > 0:
poll_end_date = datetime.utcfromtimestamp(poll['end_date']).strftime('%Y-%m-%d %H:%M:%S')
elif attachment['type'] == 'photo' and not image_url:
# Берем только первое изображение
photo = attachment['photo']
sizes = photo.get('sizes', [])
if sizes:
# Выбираем изображение максимального качества
max_size = max(sizes, key=lambda s: s.get('width', 0) * s.get('height', 0))
image_url = max_size['url']
return {
'vk_post_id': post_id,
'text': post['text'],
'image_url': image_url,
'vk_post_url': vk_post_url,
'published_at': published_at,
'is_poll': is_poll,
'poll_question': poll_question,
'poll_options': poll_options,
'poll_multiple': poll_multiple,
'poll_end_date': poll_end_date
}
def save_to_database(posts):
"""Сохраняет новые посты в MariaDB с учетом настроек публикации"""
try:
conn = mysql.connector.connect(**DB_CONFIG)
cursor = conn.cursor()
new_posts_count = 0
for post in posts:
try:
# Добавляем поля is_event, marked_for_publication, shortname и action_number
cursor.execute('''
INSERT INTO posts (
vk_post_id, text, image_url, vk_post_url, published_at,
is_poll, poll_question, poll_options, poll_multiple, poll_end_date,
is_event, marked_for_publication, shortname, action_number
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE vk_post_id = VALUES(vk_post_id)
''', (
post['vk_post_id'],
post['text'],
post['image_url'],
post['vk_post_url'],
post['published_at'],
post['is_poll'],
post['poll_question'],
post['poll_options'],
int(post['poll_multiple']),
post['poll_end_date'],
IS_EVENT,
int(AUTO_PUBLISH), # Значение из .env
None, # shortname - пока не используется, устанавливаем NULL
None # action_number - пока не используется, устанавливаем NULL
))
if cursor.rowcount > 0:
new_posts_count += 1
# Логируем добавление нового поста
log_message(f"Добавлен новый пост: ID {post['vk_post_id']}, Дата: {post['published_at']}, Автопубликация: {'Да' if AUTO_PUBLISH else 'Нет'}")
except mysql.connector.Error as err:
error_msg = f"Ошибка при вставке поста {post['vk_post_id']}: {err}"
log_message(error_msg)
continue
conn.commit()
return new_posts_count
except mysql.connector.Error as err:
error_msg = f"Ошибка подключения к базе данных: {err}"
log_message(error_msg)
return 0
finally:
if 'conn' in locals() and conn.is_connected():
cursor.close()
conn.close()
def vk_load_10():
"""
Экспортируемая функция для загрузки 10 последних постов из VK
Возвращает количество добавленных постов
"""
# Стартовая информация
start_msg = f"Старт скрипта | База: {DB_CONFIG['database']}@{DB_CONFIG['host']} | IS_EVENT: {IS_EVENT} | AUTO_PUBLISH: {AUTO_PUBLISH}"
print(start_msg)
log_message(start_msg)
# Обеспечиваем структуру БД
try:
ensure_database_structure()
except Exception as e:
error_msg = f"Не удалось создать структуру базы данных: {e}"
print(error_msg)
log_message(error_msg)
return 0
# Получаем посты из VK
try:
vk_posts = get_vk_posts()
status_msg = f"Получено {len(vk_posts)} постов из VK"
print(status_msg)
log_message(status_msg)
except Exception as e:
error_msg = f"Ошибка при получении данных из VK: {e}"
print(error_msg)
log_message(error_msg)
return 0
# Обрабатываем и сохраняем посты
processed_posts = [process_post(post) for post in vk_posts]
new_count = save_to_database(processed_posts)
# Фиксируем результат
if new_count > 0:
result_msg = f"Добавлено {new_count} новых постов в базу данных"
else:
result_msg = "Новые посты не обнаружены"
print(result_msg)
log_message(result_msg)
# Завершение работы
end_msg = "Работа скрипта завершена"
print(end_msg)
log_message(end_msg)
return new_count
def main():
"""Основная функция для запуска скрипта из командной строки"""
vk_load_10()
if __name__ == "__main__":
main()