Загрузка данных


from playwright.sync_api import sync_playwright
from urllib.parse import urlparse
import time

MAX_CANDIDATES = 300
MAX_ONLINE = 100

# Системные страницы VK, которые нам нахрен не нужны
BLACKLIST = {
    "", "feed", "im", "friends", "groups", "video",
    "clips", "search", "settings", "login", "join",
    "photos", "audios", "apps", "market", "stories",
    "notifications", "bookmarks", "support"
}


def is_profile_link(url):
    try:
        parsed = urlparse(url)

        if parsed.netloc not in ("vk.com", "www.vk.com"):
            return False

        path = parsed.path.strip("/")

        # Профиль должен быть одной частью:
        # vk.com/id123 или vk.com/username
        if not path or "/" in path:
            return False

        low = path.lower()

        if low in BLACKLIST:
            return False

        # отсекаем всякую служебную хрень
        bad_prefixes = (
            "photo", "video", "wall", "album",
            "topic", "club", "public", "event",
            "app"
        )

        if low.startswith(bad_prefixes):
            return False

        return True

    except:
        return False


def currently_online(page):
    """
    Пытаемся определить статус online по видимому интерфейсу VK.
    Никакого API.
    """

    try:
        # aria-label / title часто используются для индикатора статуса
        selectors = [
            '[aria-label*="онлайн" i]',
            '[title*="онлайн" i]',
            '[aria-label*="online" i]',
            '[title*="online" i]',
        ]

        for selector in selectors:
            if page.locator(selector).count() > 0:
                return True

        # Проверяем видимый текст страницы
        text = page.locator("body").inner_text().lower()

        lines = [
            line.strip()
            for line in text.splitlines()
            if line.strip()
        ]

        # Ищем именно короткую строку статуса,
        # а не любое случайное слово "онлайн" на странице
        for line in lines:

            if line in (
                "онлайн",
                "online",
                "в сети",
                "online с мобильного"
            ):
                return True

        return False

    except:
        return False


with sync_playwright() as p:

    # Папка сохраняет твою сессию VK
    context = p.chromium.launch_persistent_context(
        "vk_browser_data",
        headless=False,
        viewport={
            "width": 1400,
            "height": 900
        }
    )

    page = context.pages[0]

    print("Открываю VK...")

    page.goto(
        "https://vk.com/search",
        wait_until="domcontentloaded",
        timeout=60000
    )

    print()
    print("=" * 60)
    print("1. Войди в VK, если потребуется.")
    print("2. Открой поиск -> Люди.")
    print("3. Выставь:")
    print("   Пол: мужской")
    print("   Возраст: 50-60")
    print("   Город: какой тебе нужен")
    print("4. После появления результатов вернись сюда.")
    print("=" * 60)

    input("\nНажми ENTER, когда фильтры выставлены...")

    print("\nСобираю профили...")

    profiles = set()
    no_new = 0

    while len(profiles) < MAX_CANDIDATES and no_new < 10:

        old_count = len(profiles)

        links = page.locator("a[href]").evaluate_all(
            """
            elements => elements.map(a => a.href)
            """
        )

        for link in links:

            if is_profile_link(link):
                profiles.add(link.split("?")[0])

            if len(profiles) >= MAX_CANDIDATES:
                break

        print(
            f"\rСобрано кандидатов: {len(profiles)}",
            end="",
            flush=True
        )

        if len(profiles) == old_count:
            no_new += 1
        else:
            no_new = 0

        # скроллим вниз
        page.evaluate(
            "window.scrollTo(0, document.body.scrollHeight)"
        )

        time.sleep(1.5)


    print()
    print()
    print(f"Кандидатов собрано: {len(profiles)}")
    print("Теперь проверяю online...")
    print()

    online_users = []

    checker = context.new_page()

    for number, profile in enumerate(profiles, 1):

        if len(online_users) >= MAX_ONLINE:
            break

        print(
            f"[{number}/{len(profiles)}] Проверяю {profile}",
            end=""
        )

        try:

            checker.goto(
                profile,
                wait_until="domcontentloaded",
                timeout=20000
            )

            time.sleep(0.7)

            # Если VK решил кинуть капчу
            body = checker.locator("body").inner_text().lower()

            if "captcha" in body or "капча" in body:
                print(" -> КАПЧА")

                input(
                    "Реши капчу вручную в браузере "
                    "и нажми ENTER..."
                )

            if currently_online(checker):

                online_users.append(profile)

                print(
                    f" -> ONLINE [{len(online_users)}]"
                )

            else:
                print(" -> offline")

        except Exception as e:
            print(" -> ошибка")

        time.sleep(0.5)


    with open(
        "vk_online_users.txt",
        "w",
        encoding="utf-8"
    ) as f:

        for i, profile in enumerate(
            online_users,
            1
        ):
            f.write(
                f"{i}. {profile}\n"
            )


    print()
    print("=" * 60)
    print(
        f"ГОТОВО. Найдено online: "
        f"{len(online_users)}"
    )
    print(
        "Ссылки сохранены в:"
    )
    print(
        "vk_online_users.txt"
    )
    print("=" * 60)

    input(
        "\nENTER для выхода..."
    )

    context.close()