Поиск локальных IP-адресов с использованием Python's stdlib - PullRequest
480 голосов
/ 03 октября 2008

Как найти локальные IP-адреса (например, 192.168.x.x или 10.0.x.x) на платформе Python независимо и с использованием только стандартной библиотеки?

Ответы [ 43 ]

407 голосов
/ 03 октября 2008

Я только что нашел это, но это кажется немного хакерским, однако, говорят, попробовал это на * nix, а я на Windows, и это сработало.

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
print(s.getsockname()[0])
s.close()

Предполагается, что у вас есть доступ к Интернету, а локальный прокси отсутствует.

393 голосов
/ 03 октября 2008
import socket
socket.gethostbyname(socket.gethostname())

Это не будет работать всегда (возвращает 127.0.0.1 на машинах с именем хоста в /etc/hosts как 127.0.0.1), в качестве палиативного выражения будет то, что показывает гимел, вместо этого используйте socket.getfqdn(). Конечно, вашей машине нужно разрешаемое имя хоста.

158 голосов
/ 09 марта 2015

Этот метод возвращает «основной» IP-адрес локального ящика (тот, который имеет маршрут по умолчанию) .

  • НЕ нуждается в маршрутизируемом сетевом доступе или каком-либо соединении вообще.
  • Работает, даже если все интерфейсы отключены от сети.
  • НЕ нуждается или даже не пытается получить где-либо еще .
  • Работает с NAT, публичными, частными, внешними и внутренними IP-адресами
  • Pure Python 2 (или 3) без внешних зависимостей.
  • Работает в Linux, Windows и OSX.

Python 2 или 3:

import socket
def get_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        # doesn't even have to be reachable
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except:
        IP = '127.0.0.1'
    finally:
        s.close()
    return IP

Возвращает один IP-адрес, который является основным (с маршрутом по умолчанию). Если вам нужны все IP-адреса, подключенные ко всем интерфейсам (включая localhost и т. Д.), См. этот ответ .

Если вы находитесь за брандмауэром NAT, таким как домашний Wi-Fi, то он не будет показывать ваш общедоступный IP-адрес NAT, а вместо этого будет использовать ваш частный IP-адрес в локальной сети, который имеет маршрут по умолчанию к локальному маршрутизатору WIFI; Чтобы получить внешний IP-адрес вашего маршрутизатора Wi-Fi, потребуется либо запустить его в поле THAT, либо подключиться к внешней службе, такой как whatismyip.com/whatismyipaddress.com, которая может отражать IP-адрес ... но это полностью отличается от первоначального вопроса. :)

Обновлен вызов connect () в соответствии с предложением Педро в комментариях. (Если вам требуется конкретное заявление о лицензии, это общественное достояние / бесплатно для любого использования или MIT / CC2-BY-SA для лицензии на код / ​​контент Stack Overflow по вашему выбору.)

139 голосов
/ 12 августа 2009

Как псевдоним myip, он должен работать везде:

alias myip="python -c 'import socket; print([l for l in ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith(\"127.\")][:1], [[(s.connect((\"8.8.8.8\", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0][0])'"
  • Работает корректно с Python 2.x, Python 3.x, современными и старыми дистрибутивами Linux, OSX / macOS и Windows для поиска текущего адреса IPv4.
  • Не вернет правильный результат для компьютеров с несколькими IP-адресами, IPv6, без настроенного IP-адреса или без доступа к Интернету.

То же, что и выше, но только код Python:

import socket
print([l for l in ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1], [[(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0][0])
  • Это вызовет исключение, если IP-адрес не настроен.

Версия, которая также будет работать в локальных сетях без подключения к интернету:

import socket
print((([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")] or [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) + ["no IP found"])[0])

(спасибо @ ccpizza )


Фон

Использование socket.gethostbyname(socket.gethostname()) здесь не работало, потому что на одном из моих компьютеров был /etc/hosts с дублирующимися записями и ссылками на себя. socket.gethostbyname() возвращает только последнюю запись в /etc/hosts.

Это была моя первая попытка, которая отсеивает все адреса, начинающиеся с "127.":

import socket
print([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1])

Это работает с Python 2 и 3 в Linux и Windows, но не работает с несколькими сетевыми устройствами или IPv6. Однако на последних дистрибутивах Linux он перестал работать, поэтому я попробовал этот альтернативный метод. Он пытается подключиться к DNS-серверу Google на 8.8.8.8 через порт 53:

import socket
print([(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1])

Затем я объединил две вышеупомянутые техники в одну строку, которая должна работать везде, и создал псевдоним myip и фрагмент Python в верхней части этого ответа.

С ростом популярности IPv6 и для серверов с несколькими сетевыми интерфейсами использование стороннего модуля Python для поиска IP-адреса, вероятно, является одновременно более надежным и надежным, чем любой из перечисленных здесь методов.

86 голосов
/ 03 октября 2008

Вы можете использовать модуль netifaces . Просто введите:

pip install netifaces

в вашей командной оболочке, и она будет установлена ​​сама по умолчанию при установке Python.

Тогда вы можете использовать это так:

from netifaces import interfaces, ifaddresses, AF_INET
for ifaceName in interfaces():
    addresses = [i['addr'] for i in ifaddresses(ifaceName).setdefault(AF_INET, [{'addr':'No IP addr'}] )]
    print '%s: %s' % (ifaceName, ', '.join(addresses))

На моем компьютере напечатано:

{45639BDC-1050-46E0-9BE9-075C30DE1FBC}: 192.168.0.100
{D43A468B-F3AE-4BF9-9391-4863A4500583}: 10.5.9.207

Автор этого модуля утверждает, что он должен работать в Windows, UNIX и Mac OS X.

48 голосов
/ 23 июня 2011

Метод API сокетов

см. https://stackoverflow.com/a/28950776/711085

Downsides:

  • Не кроссплатформенный.
  • Требуется дополнительный запасной код, связанный с наличием определенных адресов в Интернете
  • Это также не будет работать, если вы находитесь за NAT
  • Вероятно, создается соединение UDP, не зависящее от доступности (обычно ISP) DNS (см. Другие ответы на такие идеи, как использование 8.8.8.8: сервер Google (также DNS))
  • Убедитесь, что вы сделали адрес назначения недоступным, например, числовой IP-адрес, который гарантированно не будет использоваться. НЕ используйте какой-либо домен, например, fakesubdomain.google.com или somefakewebsite.com; вы все равно будете рассылать спам этой стороне (сейчас или в будущем), а также спамить свои собственные сетевые блоки в процессе.

Метод отражателя

(Обратите внимание, что это не отвечает на вопрос OP о локальном 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)

или при использовании python2:

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 (чтобы предотвратить попадание человека средняя атака, выдавая себя за сервер)

edit : Хотя изначально я думал, что эти методы действительно плохие (если вы не используете много запасных вариантов, код может не иметь значения через много лет), он ставит вопрос "что такое Интернет?" , Компьютер может иметь много интерфейсов, указывающих на множество различных сетей. Для более подробного описания темы, Google для gateways and routes. Компьютер может иметь доступ к внутренней сети через внутренний шлюз или к всемирной сети через шлюз, например, на маршрутизаторе (обычно это так). Локальный IP-адрес, о котором запрашивает OP, четко определен только для одного канального уровня, поэтому вы должны указать это («речь идет о сетевой карте или кабеле Ethernet, о которых мы говорим?») , На поставленный вопрос может быть несколько неуникальных ответов. Однако глобальный IP-адрес во всемирной сети, вероятно, четко определен (в отсутствие массовой фрагментации сети): возможно, обратный путь через шлюз, который может получить доступ к TLD.

42 голосов
/ 15 сентября 2014

Если у компьютера есть маршрут к Интернету, всегда будет работать, чтобы получить предпочтительный локальный IP-адрес, даже если / etc / hosts не установлена ​​правильно.

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 1))  # connect() for UDP doesn't send packets
local_ip_address = s.getsockname()[0]
38 голосов
/ 14 февраля 2012

В Linux:

>>> import socket, struct, fcntl
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> sockfd = sock.fileno()
>>> SIOCGIFADDR = 0x8915
>>>
>>> def get_ip(iface = 'eth0'):
...     ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)
...     try:
...         res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)
...     except:
...         return None
...     ip = struct.unpack('16sH2x4s8x', res)[2]
...     return socket.inet_ntoa(ip)
... 
>>> get_ip('eth0')
'10.80.40.234'
>>> 
26 голосов
/ 22 декабря 2009

я использую следующий модуль:

#!/usr/bin/python
# module for getting the lan ip address of the computer

import os
import socket

if os.name != "nt":
    import fcntl
    import struct
    def get_interface_ip(ifname):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        return socket.inet_ntoa(fcntl.ioctl(
                s.fileno(),
                0x8915,  # SIOCGIFADDR
                struct.pack('256s', bytes(ifname[:15], 'utf-8'))
                # Python 2.7: remove the second argument for the bytes call
            )[20:24])

def get_lan_ip():
    ip = socket.gethostbyname(socket.gethostname())
    if ip.startswith("127.") and os.name != "nt":
        interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"]
        for ifname in interfaces:
            try:
                ip = get_interface_ip(ifname)
                break;
            except IOError:
                pass
    return ip

Протестировано с Windows и Linux (и не требует дополнительных модулей для них) предназначен для использования в системах, которые находятся в одной локальной сети на основе IPv4.

Фиксированный список имен интерфейсов не работает для последних версий Linux, которые приняли изменение systemd v197 относительно предсказуемых имен интерфейсов, как указано Alexander . В таких случаях вам нужно вручную заменить список именами интерфейсов в вашей системе или использовать другое решение, например netifaces .

24 голосов
/ 05 июля 2010

Я использую это на своих машинах с Ubuntu:

import commands
commands.getoutput("/sbin/ifconfig").split("\n")[1].split()[1][5:]

Это не работает.

...