Данный скрипт я стал писать еще до новости о том, что некий Берт Хуберт создал приложение, которое подает звуковой сигнал каждый раз, когда компьютер обращается к серверам Google. У меня не было такой цели, просто хотелось визуально увидеть, желательно на карте, по каким адресам проходят запросы с моего компьютера. Скрипт скорее сделан ради баловства, жутко не оптимизирован и скорее всего, я все сделал неправильно. Но, пока что, не придумал, как сделать лучше.
Что делает данный скрипт? Перехватывает сетевой трафик, фильтрует локальные адреса, внешние адреса перехватывает, получает координаты по IP, получает адрес с помощью обратного геокодирования. Добавляет координаты на карту. Затем с помощью selenium в безголовом режиме открывает карту, делает скриншот, создает окно tkinter и устанавливает в него скриншот, после чего, операция повторяется в бесконечном цикле.
Что понадобиться?
Для того, чтобы скрипт работал, нужно установить следующие библиотеки: scapy, для перехвата трафика. Пишем в терминале команду:
Библиотеку Pillow, для открытия изображений; selenium, для запуска браузера в режиме тестирования; geopy, для получения адреса по координатам; requests, для запросов на получение внешнего адреса, координат по IP-адресу; ipyleaflet, для создания карты с линией проведенной между двумя точками координат. Пишем для установки в терминале:
Сейчас ничего импортировать не будем, а будем делать это в процессе создания модулей. А будет из три, один вспомогательный и два основных. Давайте начинать.
Небольшая инструкция по запуску скриптов
Почему инструкция в начале? Для того, чтобы было понятно, что и в каком порядке запускать впоследствии. Для начала, запускаем модуль arp_sniff.py. Его необходимо запускать из под sudo, так как scapy требует прав суперпользователя. Для этого пишем команду:
Дайте немного поработать модулю, для того, чтобы он создал папку и сохранил в нее результаты. Или можете создать папку map вручную. Ее и модули я добавлю во вложения в архиве.
Затем запускаете модуль image_refresh.py. Его нужно запускать в обычном режиме. Здесь, напротив, selenium не работает под sudo. Поэтому, пишем в терминале:
И немного оговорюсь, для понимания, что это не полноценные скрипты, бери и пользуйся. Это лишь попытка сделать визуализацию. Кривовато, но, с опытом, надеюсь, смогу сделать лучше.
Создание вспомогательного модуля для работы с IP-адресами
Создадим модуль, я назвал его definition.py. Импортируем в него нужные для работы библиотеки. Так как мы установили все, что нужно ранее, проблем с импортом возникнуть не должно.
Создадим класс, в котором определим несколько переменных, для удобства работы с модулем. Значения переменные будут получать из функций вне класса.
Для получения локального адреса компьютера создадим функцию local(). Здесь, с помощью сокета устанавливаем соединение по адресу, получаем имя сокета и возвращаем его из функции. Данная функция кросплатформенна и будет работать как на Windows, так и на Linux.
Создадим еще одну функцию, для получения публичного, то есть внешнего IP-адерса — public(). Отправляем запрос на ipify.org. У данного сайта есть API, который возвращает внешний IP-адрес того компьютера, запрос с которого был сделан. И возвращаем адрес из функции.
Следующая функция, которую мы создадим, будем router(). С ее помощью мы будем получать адрес роутера, вне зависимости от операционной системы. Однако, в некоторых ОС Linux, для примера Ubuntu, пакет net-tools не установлен, поэтому команда route работать не будет. Поэтому, перед тем, как использовать данный модуль, нужно установить следующий пакет:
После этого определяем версию ОС, запускаем route и выполняем парсинг по результатам выполнения команды. После чего проверяем, является ли полученный результат цифрами. Если нет, делаем попытку получить адрес с помощью gethostbyname. И возвращаем полученное значение из функции.
Еще одна функция, которая понадобиться для работы — geo_ip(ip). На вход она принимает IP-адерс (внешний), для которого нужно получить координаты. То есть, с помощью данной функции мы будем получать координаты по IP-адресу. Для этого будем делать запрос на API сервиса ip-api.com, который возвращает координаты в JSON. Забираем широту и долготу и возвращаем из функции.
Осталось создать функцию, с помощью которой будем получать адрес по координатам. Создадим функцию get_addr(location). Создаем экземпляр класса, в который передаем user_agent. В данном случае он может быть любой. Затем, с помощью функции reverse, в которую передаем координаты, получаем адрес. Возвращаем его из функции.
Перехват трафика
Теперь нужно создать один из двух основных модулей, с помощью которого будем перехватывать трафик. Я назвал его arp_sniff.py. Импортируем в него библиотеки, которые нужны для работы, а также объявим глобальный список, для сохранения в него полученных координат.
Создадим функцию, в которую будут передаваться перехваченные пакеты, и выполнятся над ними определенные действия. Я назвал ее packet_sniff(packet). На входе она получает перехваченный пакет.
Получаем локальный и внешний IP-адреса. Они понадобятся нам в функции.
Теперь переводим перехваченный адрес из пакета в строку, делим по точкам, забираем первый элемент. Это же делаем с локальным адресом. Это нужно для того, чтобы отсечь все локальные адреса, к которым будет происходить обращение.
Затем, если это не локальный адрес, получаем его координаты, и добавляем список, сформированный из координат внешнего IP-адреса компьютера, и координат перехваченного IP-адреса, в список для последующего размещения на карте. Списки добавляются именно в том формате, который требуется для построения точек.
Создадим функцию map_maker(). Как понятно из ее названия, с ее помощью будем наносить полученные координаты на карту, и сохранять полученное в файл html.
Создаем карту, центральной точкой которой являются внешние координаты компьютера.
С помощью Polyline создаем линии на карте из списка со списками координат. Добавляем созданные линии на карту.
Сохраняем карту с линиями и обнуляем глобальный список.
Ну и, наконец, функция main(), в которой будем запускать перехват трафика. Для начала, делаем проверку, запущен ли скрипт от суперпользователя. Если нет, выводим сообщение и завершаем работу скрипта. Так как работа со scapy требует запуска скрипта из под sudo.
Затем, в бесконечном цикле делаем перехват трафика. Я ограничил функцию sniff 10-ю пакетами, чтобы дать возможность нанести данные на карту, что и происходит, после того, как будут перехвачены 10-пакетов. Запускаем для этого функцию map_maker(), после чего все повторяется в цикле.
Создание скриншота веб-страницы и добавление его в окно tkinter
Создадим модуль для того, чтобы в нем открывать созданную страницу с картой и метками, делать скриншот и добавлять на окно tkinter, то есть, визуализировать трафик. На самом деле, это конечно же, не полноценный способ, а только попытка сделать что-то подобное. Импортируем библиотеки, которые нужны для работы модуля.
Установим безголовость браузера selenium, создадим окно tkinter и разместим на нем элемент Label.
Затем определим ОС, под которой работает данный скрипт, запустим браузер.
Я бы не стал запускать данный скрипт под Windows. Не знаю почему, но в этой операционной системе наблюдаются жуткие тормоза. Хотя, может быть запуск на реальной машине будет несколько другим, так как я пробовал его запустить на виртуалке. Он работает, но фризит окно tkinter, хотя сама система вроде бы работает без тормозов. В Linux такого эффекта не наблюдается. Хотя, здесь можно списать это на то, что в виртуалке, все же, нет 3D режима, а следовательно, графика работает не полноценно. Что ж, двигаемся дальше.
Создадим функцию scroll_to_bottom(driver). На вход она принимает объект браузера. С ее помощью мы будем прокручивать открытую страницу до конца, чтобы создать скриншот только карты, без элементов управления.
Осталось создать функцию update_image(), в которой и будет происходить все основное действие. Итак, открываем страницу с картой. Прокручиваем вниз, делаем скриншот и сохраняем его в ту же папку, где лежит страница.
Открываем скриншот, помещаем его в Label, затем сохраняем ссылку на картинку.
Затем ждем одну секунду и запускаем сами себя рекурсивно.
Ну и еще небольшой кусочек кода. Это первоначальный старт функции update_image().
А на этом, пожалуй, все. Прошу сильно не пинать )). Это лишь попытка сделать визуализацию и вовсе ни на что не претендует.
Ну и небольшое видео для демонстрации работы. Так как в Linux трафика в спокойном состоянии не особо много, мне пришлось походить по страницам, позапускать разные сайты. Но, тем не менее, даже в спокойном состоянии постоянно держится соединение с адресом Carolina Brothers BBQ, 20702, Ashburn Road, Ashburn, Loudoun County, Virginia, 20147, United States. Вбив в посковик адрес я обнаружил, что это адрес небольшой забегаловки в Штатах. Такие вот дела.
И да, трафика действительно мало. Пришлось даже ускорить видео.
Спасибо за внимание. Надеюсь, данная информация будет вам полезна
Что делает данный скрипт? Перехватывает сетевой трафик, фильтрует локальные адреса, внешние адреса перехватывает, получает координаты по IP, получает адрес с помощью обратного геокодирования. Добавляет координаты на карту. Затем с помощью selenium в безголовом режиме открывает карту, делает скриншот, создает окно tkinter и устанавливает в него скриншот, после чего, операция повторяется в бесконечном цикле.
Что понадобиться?
Для того, чтобы скрипт работал, нужно установить следующие библиотеки: scapy, для перехвата трафика. Пишем в терминале команду:
pip install --pre scapy[basic]
Библиотеку Pillow, для открытия изображений; selenium, для запуска браузера в режиме тестирования; geopy, для получения адреса по координатам; requests, для запросов на получение внешнего адреса, координат по IP-адресу; ipyleaflet, для создания карты с линией проведенной между двумя точками координат. Пишем для установки в терминале:
pip install Pillow selenium geopy requests ipyleaflet
Сейчас ничего импортировать не будем, а будем делать это в процессе создания модулей. А будет из три, один вспомогательный и два основных. Давайте начинать.
Небольшая инструкция по запуску скриптов
Почему инструкция в начале? Для того, чтобы было понятно, что и в каком порядке запускать впоследствии. Для начала, запускаем модуль arp_sniff.py. Его необходимо запускать из под sudo, так как scapy требует прав суперпользователя. Для этого пишем команду:
sudo python3 arp_sniff.py
Дайте немного поработать модулю, для того, чтобы он создал папку и сохранил в нее результаты. Или можете создать папку map вручную. Ее и модули я добавлю во вложения в архиве.
Затем запускаете модуль image_refresh.py. Его нужно запускать в обычном режиме. Здесь, напротив, selenium не работает под sudo. Поэтому, пишем в терминале:
python3 image_refresh.py
И немного оговорюсь, для понимания, что это не полноценные скрипты, бери и пользуйся. Это лишь попытка сделать визуализацию. Кривовато, но, с опытом, надеюсь, смогу сделать лучше.
Создание вспомогательного модуля для работы с IP-адресами
Создадим модуль, я назвал его definition.py. Импортируем в него нужные для работы библиотеки. Так как мы установили все, что нужно ранее, проблем с импортом возникнуть не должно.
Python:
import subprocess
from platform import system
from socket import gethostbyname, socket, AF_INET, SOCK_DGRAM
from geopy.exc import GeocoderUnavailable
from geopy.geocoders import Nominatim
from requests import exceptions, get
Создадим класс, в котором определим несколько переменных, для удобства работы с модулем. Значения переменные будут получать из функций вне класса.
Python:
class IPDefinition:
def __init__(self):
self.local_ip = local()
self.public_ip = public()
self.router_ip = router()
Для получения локального адреса компьютера создадим функцию local(). Здесь, с помощью сокета устанавливаем соединение по адресу, получаем имя сокета и возвращаем его из функции. Данная функция кросплатформенна и будет работать как на Windows, так и на Linux.
Python:
def local():
"""
Получаем локальный IP-адрес с помощью коннекта
на адрес 10.255.255.255. В ответ получаем имя
сокета, которое и является адресом.
:return: возвращает локальный IP-адрес.
"""
st = socket(AF_INET, SOCK_DGRAM)
try:
st.connect(('10.255.255.255', 1))
ip = st.getsockname()[0]
except Exception:
ip = '127.0.0.1'
finally:
st.close()
return ip
Создадим еще одну функцию, для получения публичного, то есть внешнего IP-адерса — public(). Отправляем запрос на ipify.org. У данного сайта есть API, который возвращает внешний IP-адрес того компьютера, запрос с которого был сделан. И возвращаем адрес из функции.
Python:
def public():
try:
return get('https://api.ipify.org/').text
except exceptions.ConnectionError:
return '127.0.0.1'
Следующая функция, которую мы создадим, будем router(). С ее помощью мы будем получать адрес роутера, вне зависимости от операционной системы. Однако, в некоторых ОС Linux, для примера Ubuntu, пакет net-tools не установлен, поэтому команда route работать не будет. Поэтому, перед тем, как использовать данный модуль, нужно установить следующий пакет:
sudo apt install net-tools
После этого определяем версию ОС, запускаем route и выполняем парсинг по результатам выполнения команды. После чего проверяем, является ли полученный результат цифрами. Если нет, делаем попытку получить адрес с помощью gethostbyname. И возвращаем полученное значение из функции.
Python:
def router():
"""
Получение IP-адреса роутера или маршрутизатора.
:return: возвращает IP-адрес роутера или маршрутизатора.
"""
ip_route = None
if system() == "Linux":
ip_route = str(subprocess.check_output('route -n | grep UG', shell=True).decode()).split()[1]
elif system() == "Windows":
com = 'route PRINT 0* -4 | findstr 0.0.0.0'.split()
interface_temp = subprocess.check_output(com, shell=True).decode('cp866')
ip_route = interface_temp.split()[-3]
if ip_route.isdigit():
return ip_route
else:
sock = gethostbyname(ip_route)
return sock
Еще одна функция, которая понадобиться для работы — geo_ip(ip). На вход она принимает IP-адерс (внешний), для которого нужно получить координаты. То есть, с помощью данной функции мы будем получать координаты по IP-адресу. Для этого будем делать запрос на API сервиса ip-api.com, который возвращает координаты в JSON. Забираем широту и долготу и возвращаем из функции.
Python:
def geo_ip(ip):
"""
Функция для получения координат по внешнему IP-адресу.
Выполняет обращение к сервису, в который передает адрес.
В ответ получает JSON с координатами и не только (остальные данные не возвращаются из функции).
:param ip: внешний IP-адрес или любой адрес координаты которого нужно узнать.
:return: возвращает широту и долготу по IP.
"""
try:
url = f'http://ip-api.com/json/{ip}'
req = get(url=url).json()
return [req['lat'], req['lon']]
except Exception:
return 'Не удалось получить координаты'
Осталось создать функцию, с помощью которой будем получать адрес по координатам. Создадим функцию get_addr(location). Создаем экземпляр класса, в который передаем user_agent. В данном случае он может быть любой. Затем, с помощью функции reverse, в которую передаем координаты, получаем адрес. Возвращаем его из функции.
Python:
def get_addr(location):
"""
Получение адреса по координатам (обратная геолокация).
:param location: координаты (широта и долгота).
:return: адрес локации.
"""
try:
geoloc = Nominatim(user_agent="GetLoc")
locname = geoloc.reverse(location)
return locname.address
except (GeocoderUnavailable, ValueError):
return 'Unknown'
Python:
"""
Работа с IP-адресами. Получение локального, внешнего
IP-адресов. Получение адреса роутера. Получение координат
по IP-адресу, получение адреса по координатам.
Для работы модуля необходимо установить библиотеки:
pip install geopy requests
На некоторых машинах требует установки
пакета net-tools, т.к. без него не
работает команда route (Ubuntu).
sudo apt install net-tools
"""
import subprocess
from platform import system
from socket import gethostbyname, socket, AF_INET, SOCK_DGRAM
from geopy.exc import GeocoderUnavailable
from geopy.geocoders import Nominatim
from requests import exceptions, get
class IPDefinition:
def __init__(self):
self.local_ip = local()
self.public_ip = public()
self.router_ip = router()
def local():
"""
Получаем локальный IP-адрес с помощью коннекта
на адрес 10.255.255.255. В ответ получаем имя
сокета, которое и является адресом.
:return: возвращает локальный IP-адрес.
"""
st = socket(AF_INET, SOCK_DGRAM)
try:
st.connect(('10.255.255.255', 1))
ip = st.getsockname()[0]
except Exception:
ip = '127.0.0.1'
finally:
st.close()
return ip
def public():
try:
return get('https://api.ipify.org/').text
except exceptions.ConnectionError:
return '127.0.0.1'
def router():
"""
Получение IP-адреса роутера или маршрутизатора.
:return: возвращает IP-адрес роутера или маршрутизатора.
"""
ip_route = None
if system() == "Linux":
ip_route = str(subprocess.check_output('route -n | grep UG', shell=True).decode()).split()[1]
elif system() == "Windows":
com = 'route PRINT 0* -4 | findstr 0.0.0.0'.split()
interface_temp = subprocess.check_output(com, shell=True).decode('cp866')
ip_route = interface_temp.split()[-3]
if ip_route.isdigit():
return ip_route
else:
sock = gethostbyname(ip_route)
return sock
def geo_ip(ip):
"""
Функция для получения координат по внешнему IP-адресу.
Выполняет обращение к сервису, в который передает адрес.
В ответ получает JSON с координатами и не только (остальные данные не возвращаются из функции).
:param ip: внешний IP-адрес или любой адрес координаты которого нужно узнать.
:return: возвращает широту и долготу по IP.
"""
try:
url = f'http://ip-api.com/json/{ip}'
req = get(url=url).json()
return [req['lat'], req['lon']]
except Exception:
return 'Не удалось получить координаты'
def get_addr(location):
"""
Получение адреса по координатам (обратная геолокация).
:param location: координаты (широта и долгота).
:return: адрес локации.
"""
try:
geoloc = Nominatim(user_agent="GetLoc")
locname = geoloc.reverse(location)
return locname.address
except (GeocoderUnavailable, ValueError):
return 'Unknown'
Перехват трафика
Теперь нужно создать один из двух основных модулей, с помощью которого будем перехватывать трафик. Я назвал его arp_sniff.py. Импортируем в него библиотеки, которые нужны для работы, а также объявим глобальный список, для сохранения в него полученных координат.
Python:
import os
from ipyleaflet import Map, Polyline
from ipywidgets import Layout
from scapy.all import sniff
from definition import IPDefinition, geo_ip, get_addr
ip_set = []
Создадим функцию, в которую будут передаваться перехваченные пакеты, и выполнятся над ними определенные действия. Я назвал ее packet_sniff(packet). На входе она получает перехваченный пакет.
Получаем локальный и внешний IP-адреса. Они понадобятся нам в функции.
Python:
ipv4 = IPDefinition().local_ip
geo_public = geo_ip(IPDefinition().public_ip)
Теперь переводим перехваченный адрес из пакета в строку, делим по точкам, забираем первый элемент. Это же делаем с локальным адресом. Это нужно для того, чтобы отсечь все локальные адреса, к которым будет происходить обращение.
Python:
if str(packet['IP'].dst).split(".")[0] == ipv4.split(".")[0]:
pass
Затем, если это не локальный адрес, получаем его координаты, и добавляем список, сформированный из координат внешнего IP-адреса компьютера, и координат перехваченного IP-адреса, в список для последующего размещения на карте. Списки добавляются именно в том формате, который требуется для построения точек.
Python:
def packet_sniff(packet):
"""
Получаем локальный IP, получаем координаты
компьютера по IP-адресу. Отсекаем все локальные
адреса, к которым идут запросы.
Если адрес не локальный, добавляем список из
координат компьютера и координат адреса, куда
отправлен запрос в глобальный список.
Печатаем адрес полученный по координатам,
куда отправлен запрос.
:param packet: пакет данных от снифера.
"""
ipv4 = IPDefinition().local_ip
geo_public = geo_ip(IPDefinition().public_ip)
try:
if str(packet['IP'].dst).split(".")[0] == ipv4.split(".")[0]:
pass
else:
ip_set.append([geo_public, geo_ip(str(packet['IP'].dst))])
print(get_addr(geo_ip(str(packet['IP'].dst))))
except IndexError:
pass
Создадим функцию map_maker(). Как понятно из ее названия, с ее помощью будем наносить полученные координаты на карту, и сохранять полученное в файл html.
Создаем карту, центральной точкой которой являются внешние координаты компьютера.
Python:
global ip_set
m = Map(center=geo_ip(IPDefinition().public_ip), zoom=2, layout=Layout(width='100%', height='900px'))
С помощью Polyline создаем линии на карте из списка со списками координат. Добавляем созданные линии на карту.
Python:
lines = Polyline(locations=[ip_set], color="red", fill=False, weight=1)
m.add(lines)
Сохраняем карту с линиями и обнуляем глобальный список.
Python:
m.save(os.path.join(os.getcwd(), 'map', 'index.html'), title='Moving')
ip_set = []
Ну и, наконец, функция main(), в которой будем запускать перехват трафика. Для начала, делаем проверку, запущен ли скрипт от суперпользователя. Если нет, выводим сообщение и завершаем работу скрипта. Так как работа со scapy требует запуска скрипта из под sudo.
Python:
if not os.getuid() == 0:
print('\n[+] Запустите скрипт с правами суперпользователя!\n')
return
Затем, в бесконечном цикле делаем перехват трафика. Я ограничил функцию sniff 10-ю пакетами, чтобы дать возможность нанести данные на карту, что и происходит, после того, как будут перехвачены 10-пакетов. Запускаем для этого функцию map_maker(), после чего все повторяется в цикле.
Python:
while True:
sniff(count=10, store=0, prn=packet_sniff)
map_maker()
Python:
"""
Перехват трафика (ip-адресов).
Получение координат по адресам.
Получение адреса по геолокации.
"""
import os
from ipyleaflet import Map, Polyline
from ipywidgets import Layout
from scapy.all import sniff
from definition import IPDefinition, geo_ip, get_addr
ip_set = []
def packet_sniff(packet):
"""
Получаем локальный IP, получаем координаты
компьютера по IP-адресу. Отсекаем все локальные
адреса, к которым идут запросы.
Если адрес не локальный, добавляем список из
координат компьютера и координат адреса, куда
отправлен запрос в глобальный список.
Печатаем адрес полученный по координатам,
куда отправлен запрос.
:param packet: пакет данных от снифера.
"""
ipv4 = IPDefinition().local_ip
geo_public = geo_ip(IPDefinition().public_ip)
try:
if str(packet['IP'].dst).split(".")[0] == ipv4.split(".")[0]:
pass
else:
ip_set.append([geo_public, geo_ip(str(packet['IP'].dst))])
print(get_addr(geo_ip(str(packet['IP'].dst))))
except IndexError:
pass
def map_maker():
"""
Создаем карту с центром по координатам компьютера,
Наносим линии из списка списков на карту (Polyline).
Сохраняем карту с нанесенными линиями.
Обнуляем содержимое глобального списка.
:return:
"""
global ip_set
m = Map(center=geo_ip(IPDefinition().public_ip), zoom=2, layout=Layout(width='100%', height='900px'))
lines = Polyline(locations=[ip_set], color="red", fill=False, weight=1)
m.add(lines)
m.save(os.path.join(os.getcwd(), 'map', 'index.html'), title='Moving')
ip_set = []
def main():
"""
В бесконечном цикле запускаем перехват
трафика ограниченный 10 пакетами,
после запускаем функцию создания карты,
затем снова повторяем все в цикле.
"""
if not os.getuid() == 0:
print('\n[+] Запустите скрипт с правами суперпользователя!\n')
return
print(f'[+] Анализ ARP-пакетов... \n')
try:
while True:
sniff(count=10, store=0, prn=packet_sniff)
map_maker()
except KeyboardInterrupt:
print('Good by...')
if __name__ == "__main__":
main()
Создание скриншота веб-страницы и добавление его в окно tkinter
Создадим модуль для того, чтобы в нем открывать созданную страницу с картой и метками, делать скриншот и добавлять на окно tkinter, то есть, визуализировать трафик. На самом деле, это конечно же, не полноценный способ, а только попытка сделать что-то подобное. Импортируем библиотеки, которые нужны для работы модуля.
Python:
import os
import time
import tkinter as tk
from platform import system
from PIL import Image, ImageTk, ImageDraw
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service
Установим безголовость браузера selenium, создадим окно tkinter и разместим на нем элемент Label.
Python:
options = Options()
options.headless = True
root = tk.Tk()
browser = None
label = tk.Label(root, bg="black")
label.pack(fill="both", expand=1)
Затем определим ОС, под которой работает данный скрипт, запустим браузер.
Python:
if system() == "Windows":
browser = webdriver.Firefox(options=options, service=Service(log_path=os.devnull,
executable_path=os.path.join(os.getcwd(),
'geckodriver',
'geckodriver.exe')))
elif system() == "Linux":
browser = webdriver.Firefox(options=options, service=Service(log_path=os.devnull,
executable_path=os.path.join(os.getcwd(),
'geckodriver',
'geckodriver')))
Я бы не стал запускать данный скрипт под Windows. Не знаю почему, но в этой операционной системе наблюдаются жуткие тормоза. Хотя, может быть запуск на реальной машине будет несколько другим, так как я пробовал его запустить на виртуалке. Он работает, но фризит окно tkinter, хотя сама система вроде бы работает без тормозов. В Linux такого эффекта не наблюдается. Хотя, здесь можно списать это на то, что в виртуалке, все же, нет 3D режима, а следовательно, графика работает не полноценно. Что ж, двигаемся дальше.
Создадим функцию scroll_to_bottom(driver). На вход она принимает объект браузера. С ее помощью мы будем прокручивать открытую страницу до конца, чтобы создать скриншот только карты, без элементов управления.
Python:
def scroll_to_bottom(driver):
"""
Прокручивает страницу в самый конец.
Когда достигнут конец страницы,
выполнение функции завершается.
:param driver: объект браузера,
в котором нужно сделать прокрутку.
"""
old_position = 0
new_position = None
while new_position != old_position:
# Get old scroll position
old_position = driver.execute_script(
("return (window.pageYOffset !== undefined) ?"
" window.pageYOffset : (document.documentElement ||"
" document.body.parentNode || document.body);"))
# Sleep and Scroll
time.sleep(1)
driver.execute_script((
"var scrollingElement = (document.scrollingElement ||"
" document.body);scrollingElement.scrollTop ="
" scrollingElement.scrollHeight;"))
# Get new position
new_position = driver.execute_script(
("return (window.pageYOffset !== undefined) ?"
" window.pageYOffset : (document.documentElement ||"
" document.body.parentNode || document.body);"))
Осталось создать функцию update_image(), в которой и будет происходить все основное действие. Итак, открываем страницу с картой. Прокручиваем вниз, делаем скриншот и сохраняем его в ту же папку, где лежит страница.
Python:
browser.get(f"file:///{os.path.join(os.getcwd(), 'map', 'index.html')}")
scroll_to_bottom(browser)
browser.save_screenshot(os.path.join(os.getcwd(), 'map', 'screenshot.png'))
Открываем скриншот, помещаем его в Label, затем сохраняем ссылку на картинку.
Python:
base_image = Image.open(os.path.join(os.getcwd(), 'map', 'screenshot.png'))
draw = ImageDraw.Draw(base_image)
tkimg = ImageTk.PhotoImage(base_image)
label.config(image=tkimg)
label.image = tkimg
Затем ждем одну секунду и запускаем сами себя рекурсивно.
root.after(1, update_image)
Ну и еще небольшой кусочек кода. Это первоначальный старт функции update_image().
update_image()
Python:
"""
Создается окно приложения, с помощью tkinter.
Запускаем браузер selenium в безголовом режиме.
Загружаем сохраненную страницу с метками, делаем
скриншот, устанавливаем в качестве фона в окно
tkinter.
Для работы функций требуется установить следующие
библиотеки:
pip install --pre scapy[basic]
pip install Pillow selenium geopy requests ipyleaflet
"""
import os
import time
import tkinter as tk
from platform import system
from PIL import Image, ImageTk, ImageDraw
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service
options = Options()
options.headless = True
root = tk.Tk()
browser = None
label = tk.Label(root, bg="black")
label.pack(fill="both", expand=1)
if system() == "Windows":
browser = webdriver.Firefox(options=options, service=Service(log_path=os.devnull,
executable_path=os.path.join(os.getcwd(),
'geckodriver',
'geckodriver.exe')))
elif system() == "Linux":
browser = webdriver.Firefox(options=options, service=Service(log_path=os.devnull,
executable_path=os.path.join(os.getcwd(),
'geckodriver',
'geckodriver')))
def scroll_to_bottom(driver):
"""
Прокручивает страницу в самый конец.
Когда достигнут конец страницы,
выполнение функции завершается.
:param driver: объект браузера,
в котором нужно сделать прокрутку.
"""
old_position = 0
new_position = None
while new_position != old_position:
# Get old scroll position
old_position = driver.execute_script(
("return (window.pageYOffset !== undefined) ?"
" window.pageYOffset : (document.documentElement ||"
" document.body.parentNode || document.body);"))
# Sleep and Scroll
time.sleep(1)
driver.execute_script((
"var scrollingElement = (document.scrollingElement ||"
" document.body);scrollingElement.scrollTop ="
" scrollingElement.scrollHeight;"))
# Get new position
new_position = driver.execute_script(
("return (window.pageYOffset !== undefined) ?"
" window.pageYOffset : (document.documentElement ||"
" document.body.parentNode || document.body);"))
def update_image():
"""
В запущенном браузере в безголовом режиме
загружаем страницу, сгенерированную в функции
перехвата трафика. Прокручиваем до конца,
делаем скриншот и сохраняем в папку со страницей.
Загружаем сделанный скриншот.
Устанавливаем скриншот в качестве картинки в окно tkinter.
Сохраняем ссылку на картинку во избежание сборки мусора.
Ждем секунду и рекурсивно запускаем функцию на выполнение.
"""
browser.get(f"file:///{os.path.join(os.getcwd(), 'map', 'index.html')}")
scroll_to_bottom(browser)
browser.save_screenshot(os.path.join(os.getcwd(), 'map', 'screenshot.png'))
base_image = Image.open(os.path.join(os.getcwd(), 'map', 'screenshot.png'))
draw = ImageDraw.Draw(base_image)
tkimg = ImageTk.PhotoImage(base_image)
label.config(image=tkimg)
label.image = tkimg
root.after(1, update_image)
update_image()
if __name__ == "__main__":
root.mainloop()
А на этом, пожалуй, все. Прошу сильно не пинать )). Это лишь попытка сделать визуализацию и вовсе ни на что не претендует.
Ну и небольшое видео для демонстрации работы. Так как в Linux трафика в спокойном состоянии не особо много, мне пришлось походить по страницам, позапускать разные сайты. Но, тем не менее, даже в спокойном состоянии постоянно держится соединение с адресом Carolina Brothers BBQ, 20702, Ashburn Road, Ashburn, Loudoun County, Virginia, 20147, United States. Вбив в посковик адрес я обнаружил, что это адрес небольшой забегаловки в Штатах. Такие вот дела.
И да, трафика действительно мало. Пришлось даже ускорить видео.
Спасибо за внимание. Надеюсь, данная информация будет вам полезна
Вложения
Последнее редактирование: