Поиск локальных IP-адресов с использованием стандартной библиотеки Python
Как я могу найти локальные IP-адреса (то есть 192.168.x.x или 10.0.x.x) в Python, независимо от платформы и используя только стандартную библиотеку?
5 ответ(ов)
Вы правы, данный способ может показаться немного "хитрым", однако он действительно работает и позволяет получить IP-адрес вашего устройства в сети. Приведенный вами код создает UDP-сокет и устанавливает его соединение с удаленным адресом (в данном случае с DNS-сервером 8.8.8.8). После этого метод getsockname()
возвращает локальный адрес, используемый для этого соединения.
Стоит отметить, что данный метод предполагает наличие доступа к интернету, а также отсутствие локального прокси-сервера, так как в противном случае результат может быть другим.
Вот аналогичный пример кода на Python:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
print(s.getsockname()[0]) # Выводит локальный IP-адрес
s.close()
Если вы испробовали это на Windows и у вас все сработало, то это хороший индикатор того, что подход работает в разных операционных системах. Однако при использовании такого метода всегда стоит помнить о зависимости от сетевых условий.
Это решение не всегда будет работать, так как может возвращать 127.0.0.1
, если на вашем компьютере в файле /etc/hosts
имя хоста сопоставлено с 127.0.0.1
. Устранить эту проблему можно с помощью использования socket.getfqdn()
, как показывает gimel. Однако стоит отметить, что ваше имя хоста должно быть разрешаемым для корректной работы данного подхода.
Если компьютер имеет маршрут к Интернету, данный код всегда будет работать для получения предпочтительного локального IP-адреса, даже если файл /etc/hosts настроен некорректно.
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 1)) # connect() для UDP не отправляет пакеты
local_ip_address = s.getsockname()[0]
Этот код создает UDP-сокет и устанавливает соединение с публичным DNS-сервером Google (8.8.8.8). Метод connect()
используется, чтобы получить локальный IP-адрес, который будет использоваться для подключения к указанному адресу. В случае, если сервер недоступен, метод connect()
все равно не отправляет никаких пакетов, что делает его безопасным для этого применения. Таким образом, даже если возникают проблемы с конфигурацией сети, код способен корректно вернуть локальный IP-адрес.
Метод Sокета
Смотрите это сообщение на StackOverflow.
Недостатки:
- Не кроссплатформенный.
- Требуется больше кода для обработки ошибок, зависимого от наличия конкретных адресов в интернете.
- Это также не сработает, если вы находитесь за NAT.
- Вероятно, создает UDP-соединение, которое не зависит от доступности DNS (обычно принадлежащего вашему ISP) (см. другие ответы с идеями, например, использовать 8.8.8.8 – сервер Google (который, кстати, также является DNS-сервером)).
- Убедитесь, что целевой адрес находится вне досягаемости, например, используйте числовой IP-адрес, который согласно спецификации гарантированно не используется. НЕ используйте такие домены, как fakesubdomain.google.com или somefakewebsite.com; вы все равно будете спамить эту сторону (сейчас или в будущем), и при этом будете также засорять свои сетевые устройства.
Метод Рефлектора
(Обратите внимание, что это не отвечает на вопрос ОП о локальном IP-адресе, например, 192.168...; он предоставляет ваш публичный IP-адрес, который может оказаться более желаемым в зависимости от случая использования.)
Вы можете запросить некоторые сайты, такие как whatismyip.com (но с API), например:
from urllib.request import urlopen
import re
def getPublicIp():
data = str(urlopen('http://checkip.dyndns.com/').read())
# data = '<html><head><title>Current IP Check</title></head><body>Current IP Address: 65.96.168.198</body></html>\r\n'
return re.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search(data).group(1)
или, если вы используете Python 2:
from urllib import urlopen
import re
def getPublicIp():
data = str(urlopen('http://checkip.dyndns.com/').read())
# data = '<html><head><title>Current IP Check</title></head><body>Current IP Address: 65.96.168.198</body></html>\r\n'
return re.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search(data).group(1)
Преимущества:
- Одним из плюсов этого метода является его кроссплатформенность.
- Он работает и за «некрасивыми» NAT (например, вашим домашним маршрутизатором).
Недостатки (и обходные решения):
- Требует, чтобы данный сайт был доступен, формат не изменился (что маловероятно), и ваши DNS-серверы работали. Можно смягчить эту проблему, запрашивая также других сторонних рефлекторов IP-адресов на случай сбоя.
- Возможен вектор атаки, если вы не запрашиваете несколько рефлекторов (чтобы предотвратить ситуацию, когда скомпрометированный рефлектор говорит вам, что ваш адрес - это не так), или если вы не используете HTTPS (чтобы предотвратить атаку «человек посередине», выдающего себя за сервер).
Редактировать: Хотя изначально я думал, что эти методы действительно плохие (если не использовать много запасных вариантов, код может стать неактуальным через несколько лет), это ставит вопрос: «что такое интернет?». Компьютер может иметь множество интерфейсов, подключенных к разным сетям. Для более подробного описания темы, погуглите шлюзы и маршруты
. Компьютер может получить доступ к внутренней сети через внутренний шлюз или получить доступ к всемирной паутине через шлюз, например, на маршрутизаторе (что обычно и происходит). Локальный IP-адрес, о котором спрашивает ОП, четко определен только в отношении одного канала передачи, поэтому вы должны это указать («это сетевой адаптер или Ethernet-кабель, о котором мы говорим?»). На этот вопрос может быть несколько неопределенных ответов, однако глобальный IP-адрес в сети интернет, вероятно, имеет четкое определение (при отсутствии значительной фрагментации сети): вероятно, это обратный путь через шлюз, который может получить доступ к TLD.
Чтобы получить IP-адрес сетевого интерфейса в Linux с использованием Python, воспользуйтесь следующей программой:
import socket
import struct
import fcntl
# Создаем сокет
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockfd = sock.fileno()
# Константа для получения IP-адреса
SIOCGIFADDR = 0x8915
def get_ip(iface='eth0'):
# Создаем структуру запроса для интерфейса
ifreq = struct.pack('16sH14s', iface.encode('utf-8'), socket.AF_INET, b'\x00'*14)
try:
# Выполняем ioctl для получения информации об IP
res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)
except:
return None
# Извлекаем IP-адрес из результата
ip = struct.unpack('16sH2x4s8x', res)[2]
return socket.inet_ntoa(ip)
# Пример использования
print(get_ip('eth0')) # Выводит IP-адрес (например, '10.80.40.234')
В этом коде мы создаем сокет и используем системный вызов ioctl
, чтобы получить IP-адрес для указанного интерфейса (eth0
по умолчанию). Если интерфейс недоступен или возникает ошибка, функция возвращает None
. Обратите внимание, что вам может понадобиться запустить скрипт с правами суперпользователя, чтобы получить доступ к интерфейсам сети.
Какой самый быстрый способ выполнить HTTP GET в Python?
Сохранить график в файл изображения вместо его отображения
Преобразование списка словарей в DataFrame pandas
Как отсортировать список/кортеж списков/кортежей по элементу на заданном индексе
Как отменить последнюю миграцию?