how to set browser window visible

Делитесь приложениями
  • Да в диспечерез задач можно увидеть,
    image.png
    что есть командная строка, а значит ее можно узнать и через API.
    Все это решаем куча проблем)

    Спасибо за наводку

  • @biohacker01 said in how to set browser window visible:

    Покопаюсь можно-ли эту информацию узнать у процесса

    Можно 100%, если есть доступ к процессу в принципе. Например, если он запущен из под того же пользователя, что и скрипт, который пытается получить свойства/параметры процесса.

    @biohacker01 said in how to set browser window visible:

    Спасибо за наводку

    Надо смотреть, как реализуется headless режим у браузеров. У меня есть смутные подозрения, что происходит ровно тоже самое, что ты пытаешься сделать сбоку.

  • @sergerdn said in how to set browser window visible:

    как реализуется headless режим у браузеров

    там уже давно headless new есть, который не как старый, а отрабатывает js и все остальные объекты как будто браузер включен. но да дело надо лучше изучать.

  • @sergerdn еще что интересно в BAS еще идет отрисовка мышки в IDE если запускаем, это особенности, или через js эмулируется и видно после открытия окна по WINAPI ?

  • @biohacker01 said in how to set browser window visible:

    @sergerdn еще что интересно в BAS еще идет отрисовка мышки в IDE если запускаем, это особенности, или через js эмулируется и видно после открытия окна по WINAPI ?

    Понятия не имею. Попробуй использовать https://github.com/CheshireCaat/playwright-with-fingerprints, там ровно тот же браузер, по крайней мере.

  • @biohacker01

    Есть интересный путь сделать то, что ты, вероятно, хочешь:

    • использовать BAS GUI, чтобы задавать настройки, видеть браузеры, скрывать их и так далее.
    • программировать на удобном тебе языке в обычной IDE

    Я написал небольшую библиотеку, которая решают такую задачу. Так как мне самому это нужно. Есть пара не решенных вещей, для которых я пока не знаю простых путей. Например, как из стороннего скрипта передавать управление пользователю в браузере и возвращать его обратно.

    Исходный код не даю, так как код пока очень сырой. В процессе его написания.

    Реализация:

    1. BAS при старте в самом начале работы запускает скрипт cmd_initial.py в командной строке, который создает и сохраняет задания для BAS. BAS принимает задания, настраивает браузеры, стартует их.
    poetry run python cmd_initial.py --bas_fingerprint_key=bla-bla
    

    Пример задания:

    [
    	{
    		"task_id": "ecc53d50-e61b-4a8d-86d7-3b05afff060a",
    		"browser_settings": {
    			"components": {
    				"widevine": "enable",
    				"safe_browsing": "enable",
    				"components": "enable"
    			},
    			"network": {
    				"enable_qiuc_protocol": true
    			},
    			"rendering": {
    				"maximum_fps": 30
    			},
    			"browser_version": "default",
    			"command_line": [
    				"--disk-cache-size=104857600",
    				"--disable-gpu-program-cache",
    				"--disable-gpu-shader-disk-cache",
    				"--disable-features=GpuProcessHighPriorityWin,GpuUseDisplayThreadPriority",
    				"--lang=en"
    			],
    			"profile": {
    				"profile_folder_path": "C:\\Users\\Administrator\\AppData\\Local\\PyBASProfileManager\\tmp2xwpovkm",
    				"always_load_fingerprint_from_profile_folder": false,
    				"always_load_proxy_from_profile_folder": false
    			},
    			"proxy": null,
    			"fingerprint": {
    				"safe_canvas": true,
    				"use_perfect_canvas": true,
    				"safe_webgl": true,
    				"safe_audio": true,
    				"safe_battery": true,
    				"use_font_pack": true,
    				"safe_element_size": false,
    				"emulate_sensor_api": true,
    				"emulate_device_scale_factor": true
    			}
    		}
    	}
    ]
    
    1. После старта браузеров каждый поток в BAS передает информацию о запущенном профиле второму скрипту cmd_worker.py.
    poetry run python cmd_worker.py --bas_current_profile_id="PyBASProfileManager/tmp2xwpovkm"
    

    Что делает cmd_worker.py:

    • берет информацию из параметра bas_current_profile_id, и ищет свое задание, так как мы получили полный путь к профилю. Также в процессах Windows ищет нужный запущенный браузер. Это можно понять, так как в командной строке присутствует путь к профилю.
    • парсит данные командной строки, чтобы получить remote debugging port. Далее уже дело техники из него получить ws_endpoint и подключиться к нему с помощью playwright.

    Скриншоты:

  • я у себя подобное реализовал, только в параметр передаю номер потока )
    но все же парсинг процессов занимает 5-10 секунд, чтоб получить PID и присвоить его.

  • @biohacker01 said in how to set browser window visible:

    я у себя подобное реализовал, только в параметр передаю номер потока )

    Не удивительно.

    @biohacker01 said in how to set browser window visible:

    но все же парсинг процессов занимает 5-10 секунд, чтоб получить PID и присвоить его.

    Не отслеживал скорость работы. Так как я в самом начале пути. И в разрезе медленной работы самих браузеров это не так важно, так как прокси и даже умышленные замедления в работе, чтобы походить на человека.

  • @biohacker01 said in how to set browser window visible:

    но все же парсинг процессов занимает 5-10 секунд, чтоб получить PID и присвоить его.

    Мой сервер https://www.hetzner.com/dedicated-rootserver/ax41-nvme.

    Время поиска процесса по cmd, где есть нужный профиль в параметрах. Надо смотреть на твою реализацию, почему у тебя так долго.

    $ cat logs/cmd_worker.log | grep "_find_proc"
    2023-10-04 05:44:08,851 21432 DEBUG [utils] func:'_find_proc' took: 0.0350 sec
    2023-10-04 05:44:08,879 17828 DEBUG [utils] func:'_find_proc' took: 0.0780 sec
    2023-10-04 05:44:10,105 6716 DEBUG [utils] func:'_find_proc'  took: 0.0564 sec
    2023-10-04 05:44:11,174 9724 DEBUG [utils] func:'_find_proc'  took: 0.0530 sec
    2023-10-04 05:44:16,793 21784 DEBUG [utils] func:'_find_proc' took: 0.1097 sec
    2023-10-04 05:44:18,010 17728 DEBUG [utils] func:'_find_proc' took: 0.0208 sec
    2023-10-04 05:44:18,736 17292 DEBUG [utils] func:'_find_proc' took: 0.0744 sec
    2023-10-04 05:44:25,833 18756 DEBUG [utils] func:'_find_proc' took: 0.0963 sec
    2023-10-04 05:44:29,011 18472 DEBUG [utils] func:'_find_proc' took: 0.0475 sec
    2023-10-04 05:44:30,689 14280 DEBUG [utils] func:'_find_proc' took: 0.0777 sec
    

    Функция:

    
    import os
    import psutil
    from pydantic import FilePath
    
    _worker_proc_name = "Worker.exe"  # Worker browser process (mother)
    _browser_proc_name = "worker.exe"  # Chromium browser process (child)
    
    def _find_proc(profile_folder_path: FilePath) -> int:
        """
        Find and return the process ID of a Chromium browser instance with a specific profile folder path.
    
        Parameters:
        - profile_folder_path (FilePath): The path of the profile folder to search for.
    
        Returns:
        - int: The process ID (PID) of the matching browser instance, or 0 if not found.
        """
        # Iterate through all running processes
        for proc in psutil.process_iter():
            name = proc.name()
    
            # Check if the process is a Worker.exe process
            if name == _worker_proc_name:
                for child in proc.children(recursive=False):
                    # Check if the child process is the browser process
                    if child.name() == _browser_proc_name:
                        cmd_line = child.cmdline()
                        for line in cmd_line:
                            line = line.strip()
    
                            # Check if the command line argument specifies the user data directory
                            if not line.startswith("--user-data-dir="):
                                continue
    
                            # Extract the user data directory path
                            _, profile_dir = line.split("--user-data-dir=")
                            profile_dir = os.path.normpath(profile_dir)
    
                            # Compare the user data directory path with the provided profile folder path
                            if FilePath(profile_dir) == profile_folder_path:
                                return child.pid  # Return the process ID if a match is found
    
        return 0  # Return 0 if no matching process is found
    
  • @biohacker01 said in how to set browser window visible:

    @sergerdn еще что интересно в BAS еще идет отрисовка мышки в IDE если запускаем, это особенности, или через js эмулируется и видно после открытия окна по WINAPI ?

    BAS эмулирует движения мыши через свой внутренний API, если использовать браузер не из BAS, то данная фича будет недоступна. В том числе если использовать и https://wiki.bablosoft.com/doku.php?id=puppeteerwithfingerprints#how_to_start.

    Чтобы была возможность использовать эту фичу, нужно запускать браузер именно из BAS.

    Но если набросать скрипт из двух кубиков, а потом открыть консоль браузера, то там будет куча логов, что мышка двигалась.

    Capture.PNG

    addEventListener("mousemove", (event) => {
        console.log(`Mouse X: ${event.clientX}, Mouse Y: ${event.clientY}`);
    });
    

    Пример реализации, что сделал я:

    https://github.com/sergerdn/py-bas-automation/blob/713f3d09e45849ac50e6b4734abb0b804619bf39/pybas_automation/browser_automator/browser_automator.py#L226

  • Может ксть идеи как через стандартный интерфейс в скомпилированном скрипте с включенной галкой "скрыть браузеры" передать управление браузером пользователю?

  • @Poisen said in how to set browser window visible:

    Может ксть идеи как через стандартный интерфейс в скомпилированном скрипте с включенной галкой "скрыть браузеры" передать управление браузером пользователю?

    Это галка скрывает меню, а не сами браузеры.

    Я не тестил, но вероятно, можно попробовать сделать браузер видимым сбоку и все так же передать управление пользователю.

  • @sergerdn said in how to set browser window visible:

    import time
    import psutil
    import win32con
    import win32gui
    import win32process

    def windows_is_visible(pid):
    def enum_handler(hwnd, data):
    if win32process.GetWindowThreadProcessId(hwnd)[1] == pid:
    return True

    win32gui.EnumWindows(enum_handler, None)
    return False
    

    def window_set_visible(pid):
    def get_hwnds_for_pid(pid):
    def callback(hwnd, hwnds):
    _, found_pid = win32process.GetWindowThreadProcessId(hwnd)
    if found_pid == pid:
    hwnds.append(hwnd)

        hwnds = []
        win32gui.EnumWindows(callback, hwnds)
        return hwnds
    
    handles = get_hwnds_for_pid(pid)
    if len(handles) > 0:
        wnd_handle = handles[0]
        win32gui.ShowWindow(wnd_handle, win32con.SW_SHOWNORMAL)
        return True
    
    return False
    

    if name == 'main':
    process_name = "Worker.exe"

    def set_visible(pid):
        print(window_set_visible(pid))
    
    
    while True:
        for proc in psutil.process_iter():
            if process_name in proc.name():
                pid = proc.pid
                set_visible(pid)
        time.sleep(1)
    

    where to put this script on the bas?