Поиск локальных IP-адресов с использованием Python's stdlib


547

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


7
Местный IP? Или публичный IP? Как вы собираетесь работать с системами с несколькими IP-адресами?
Саргун Диллон

использовать ifconfig -aи использовать вывод оттуда ...
Фредрик Пихл

16
@Fredrik Это плохая идея. Прежде всего, вы излишне разветвляете новый процесс, и это может помешать вашей программе работать в жестко заблокированных конфигурациях (или вам придется разрешить права, которые вашей программе не нужны). Во-вторых, вы будете вводить ошибки для пользователей разных локалей. В-третьих, если вы решили запустить новую программу вообще, вам не следует запускать устаревшую программу - ip addrона гораздо более удобна (и ее легче анализировать, загружать).
Phihag

12
@phihag Вы абсолютно правы, спасибо за исправление моей глупости
Фредрик Пихл

1
Более фундаментальная проблема здесь заключается в том, что в правильно написанной современной сетевой программе правильный (набор) локальных IP-адресов зависит от однорангового узла или набора потенциальных одноранговых узлов. Если локальный IP-адрес необходим bindсокету для конкретного интерфейса, то это вопрос политики. Если локальный IP-адрес необходим для передачи его одноранговому узлу, чтобы одноранговый узел мог «перезвонить», т. Е. Чтобы открыть соединение с локальной машиной, тогда ситуация зависит от того, есть ли NAT (трансляция сетевых адресов) коробки между ними. Если нет NAT, getsocknameэто хороший выбор.
Пекка Никандер

Ответы:


445
import socket
socket.gethostbyname(socket.gethostname())

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


43
Следует отметить, что это не платформо-независимое решение. Многие Linux вернут 127.0.0.1 в качестве вашего IP-адреса, используя этот метод.
Джейсон Бейкер

20
Вариант: socket.gethostbyname (socket.getfqdn ())
гимел

55
Похоже, это возвращает только один IP-адрес. Что делать, если у машины несколько адресов?
Джейсон Р. Кумбс

26
В Ubuntu это возвращает 127.0.1.1 по некоторым причинам.
Slikts

13
@Jason R. Coombs, используйте следующий код для получения списка адресов IPv4, которые принадлежат хост-машине:socket.gethostbyname_ex(socket.gethostname())[-1]
Barmaley

461

Я только что нашел это, но это кажется немного хакерским, однако, говорят, попробовал это на * 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()

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


31
Хорошо, если у вас есть несколько интерфейсов на машине, и вам нужен тот, который направляет, например, на gmail.com
elzapp

4
Хорошей идеей может быть перехват исключений socket.error, которые могут быть вызваны s.connect ()!
Фоби

39
Было бы лучше использовать IP-адрес вместо доменного имени - он должен быть более быстрым и независимым от доступности DNS. Например, мы можем использовать 8.8.8.8 IP - общедоступный DNS-сервер Google.
wobmene

10
Очень умно, отлично работает. Вместо gmail или 8.8.8.8 вы также можете использовать IP-адрес или адрес сервера, с которого вы хотите видеть, если это применимо.
Профессор Фалькен нарушил контракт

3
Этот пример имеет внешнюю зависимость от возможности разрешения gmail.com. Если вы установите для него IP-адрес, который отсутствует в локальной сети (не имеет значения, включен он или нет), он будет работать без зависимостей и без сетевого трафика.
мрачный

254

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

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

Python 3 или 2:

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 Exception:
        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-адрес ... но это полностью отличается от первоначального вопроса. :)


7
Работает в Raspbian с Python 2 и 3!
pierce.jason

3
Brilliant. Работает на Win7,8,8.1 + Linux Mint & Arch, включая виртуальные машины.
shermy

2
Работает в Windows 10 Pro! Спасибо, Джеймисон Беккер!
Варантес

3
По некоторым причинам это не работает в Mac OS X El Capitan 10.11.6 (это генерирует исключительную ошибку ОС: [Errno 49] Невозможно назначить запрошенный адрес). Изменение порта с '0' на '1': s.connect (('10.255.255.255', 1)) работал для меня как в Mac OS X, так и в Linux Ubuntu 17.04
Pedro Scarapicchia Junior

10
Это должен быть принятый ответ. socket.gethostbyname(socket.gethostname())дает ужасные результаты.
Джейсон Флойд

142

Как называется псевдоним 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-адреса, вероятно, является одновременно более надежным и надежным, чем любой из перечисленных здесь методов.


2
@ Александр: Просто сказать, что этот ответ гораздо менее полезен, чем раньше (и это не так, как отфильтровывать дубликаты - большое дело;). Согласно документации socket.getaddrinfo()должно работать согласованно на всех платформах - но я проверял это только в Linux, не заботясь о других операционных системах.
Владимир Палант

1
@ Александр, /etc/resolve.conf: No such file or directoryи у меня есть локальный адрес IPv4, показанный ifconfig.
анатолий техтоник

2
Я могу подтвердить, что обновленная версия работает с Ubuntu 14.04 с Python2 и Py3k.
Ули Келер

4
«Обновление» показывает хороший трюк с connect () на UDP-сокете. Он не отправляет трафик, но позволяет найти адрес отправителя для пакетов указанному получателю. Порт, вероятно, не имеет значения (даже 0 должен работать). На многосетевом хосте важно выбрать адрес в правильной подсети.
Питер Хансен

17
просто потому, что вы можете написать этот код в одной строке, это не значит, что вы должны ...
JackLeo

91

Вы можете использовать модуль 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.


20
Как указано в вопросе, я хочу что-то из установки по умолчанию, так как никаких дополнительных установок не требуется.
UnkwnTech

4
Это был бы мой любимый ответ, за исключением того, что netifaces не поддерживает IPv6 в Windows и выглядит не поддерживаемым. Кто-нибудь разобрался, как получить IPv6-адреса в Windows?
Жан-Поль Кальдероне

3
netifaces не поддерживает py3k и требует компилятора C, который является PITA для окон.
Мэтт Столяр

4
@MattJoiner Ничто из этого не соответствует действительности (последняя версия имеет бинарные файлы Windows для PyPI и поддерживает Py3K).
Аластер

4
@ Жан-PaulCalderone FWIW, последняя версия netifaces делает поддержку IPv6 на Windows.
Аластер

47

Метод Socket API

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

Недостатки:

  • Не кроссплатформенный.
  • Требуется дополнительный запасной код, связанный с наличием определенных адресов в интернете
  • Это также не будет работать, если вы находитесь за 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 (чтобы предотвратить атаку типа «человек посередине» быть сервером)

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


Это вернет ваш адрес в локальной сети, если вы находитесь за NAT. Если вы подключаетесь к Интернету, вы можете подключиться к веб-службе, которая возвращает один из ваших общедоступных IP-адресов.
Phihag

Это не создает соединение TCP, потому что это создает соединение UDP.
Анудж Гупта

2
В качестве альтернативы в версии API сокетов замените s.connect (('INSERT SOME TARGET WEBSITE.com', 0)) на s.setsockopt (socket.SOL_SOCKET, socket.SO_BROADCAST, 1); s.connect (('< широковещательная передача> ', 0)), чтобы избежать поиска DNS. (Я думаю, что может быть проблема с трансляцией, если есть брандмауэр)
dlm

45

Если у компьютера есть маршрут к Интернету, он всегда будет работать для получения предпочтительного локального 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]

как это работает ? , 8.8.8.8Является Google DNS - сервер , мы можем сделать это с локальным сервером DNS?
Ciasto piekarz

39

В 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'
>>> 

Таким образом, это эффективно открывает сокет, с которым он ничего не делает, и вы проверяете необработанные данные об этом сокете, чтобы получить локальный IP-адрес?
Дейв

1
Сокет открывается, чтобы заставить fd взаимодействовать с ядром (через ioctl). Сокет не ограничен интерфейсом, для которого вы хотите узнать информацию об адресе, - это просто механизм связи между пользовательским пространством и ядром. en.wikipedia.org/wiki/Ioctl lxr.free-electrons.com/source/net/socket.c
tMC

2
Работает на Python3 с одной модификацией: struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)должна быть заменена наstruct.pack('16sH14s', iface.encode('utf-8'), socket.AF_INET, b'\x00'*14)
pepoluan

1
@ChristianFischer ioctl- это устаревший интерфейс, я не верю, что поддерживает IPv6 и, вероятно, никогда не будет. Я думаю, что «правильный» путь - через Netlink, который не очень прост в Python. Я думаю, что libc должна иметь функцию, к getifaddrsкоторой можно получить доступ через ctypesмодуль pythons,
tMC

1
@Maddy ioctl - это устаревший интерфейс, я не верю, что поддерживает IPv6 и, вероятно, никогда не будет. Я думаю, что «правильный» путь - через Netlink, который не очень прост в Python. Я думаю, что в libc должна быть функция getifaddrs, к которой можно получить доступ через модуль pytyons ctypes, который может работать - man7.org/linux/man-pages/man3/getifaddrs.3.html
tMC

27

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

#!/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 в отношении предсказуемых имен интерфейсов, как указал Александр . В таких случаях вам необходимо вручную заменить список именами интерфейсов в вашей системе или использовать другое решение, такое как netifaces .


2
Это несовместимо с новыми предсказуемыми именами интерфейсов Linux, такими как enp0s25. Для получения дополнительной информации см. Wiki.archlinux.org/index.php/Network_Configuration#Device_names
Alexander

2
Я использовал Python 3.4, и часть 'struct.pack (...)' нужно было заменить на 'struct.pack (' 256s ', байты (ifname [: 15],' utf-8 '))'. Смотрите этот вопрос: stackoverflow.com/q/27391167/76010
Bakanekobrain

1
на Raspbian w / Python 2.7.3 - bytes () не понравился второй аргумент. Но это сработало: struct.pack('256s', bytes(ifname[:15]))
colm.anseo

24

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

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

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


Красиво и просто. Работает на Amazon AMI от Linux, но только если я root. В противном случае я получил бы ошибку: 'sh: ifconfig: команда не найдена'
Игорь Ганапольский

Поэтому вы должны использовать "/ sbin / ifconfig", как сказал gavaletz. Это также работает на Red Hat 4.1.2-48.
Игорь Ганапольский

7
Устаревший с 2.6. Используйте модуль подпроцесса для запуска команд.
Колин Данкло

5
И ifconfig также не рекомендуется. Используйте iproute2.
Хельмут Гроне

Получить все ips: импорт sh; [ip.split () [1] [5:] для ip в фильтре (лямбда-x: 'inet addr' в x, sh.ifconfig (). split ("\ n"))]
Габриэль Литтман

20

Если вы не хотите использовать внешние пакеты и не хотите полагаться на внешние интернет-серверы, это может помочь. Это пример кода, который я нашел в поиске кода Google и изменил для получения необходимой информации:

def getIPAddresses():
    from ctypes import Structure, windll, sizeof
    from ctypes import POINTER, byref
    from ctypes import c_ulong, c_uint, c_ubyte, c_char
    MAX_ADAPTER_DESCRIPTION_LENGTH = 128
    MAX_ADAPTER_NAME_LENGTH = 256
    MAX_ADAPTER_ADDRESS_LENGTH = 8
    class IP_ADDR_STRING(Structure):
        pass
    LP_IP_ADDR_STRING = POINTER(IP_ADDR_STRING)
    IP_ADDR_STRING._fields_ = [
        ("next", LP_IP_ADDR_STRING),
        ("ipAddress", c_char * 16),
        ("ipMask", c_char * 16),
        ("context", c_ulong)]
    class IP_ADAPTER_INFO (Structure):
        pass
    LP_IP_ADAPTER_INFO = POINTER(IP_ADAPTER_INFO)
    IP_ADAPTER_INFO._fields_ = [
        ("next", LP_IP_ADAPTER_INFO),
        ("comboIndex", c_ulong),
        ("adapterName", c_char * (MAX_ADAPTER_NAME_LENGTH + 4)),
        ("description", c_char * (MAX_ADAPTER_DESCRIPTION_LENGTH + 4)),
        ("addressLength", c_uint),
        ("address", c_ubyte * MAX_ADAPTER_ADDRESS_LENGTH),
        ("index", c_ulong),
        ("type", c_uint),
        ("dhcpEnabled", c_uint),
        ("currentIpAddress", LP_IP_ADDR_STRING),
        ("ipAddressList", IP_ADDR_STRING),
        ("gatewayList", IP_ADDR_STRING),
        ("dhcpServer", IP_ADDR_STRING),
        ("haveWins", c_uint),
        ("primaryWinsServer", IP_ADDR_STRING),
        ("secondaryWinsServer", IP_ADDR_STRING),
        ("leaseObtained", c_ulong),
        ("leaseExpires", c_ulong)]
    GetAdaptersInfo = windll.iphlpapi.GetAdaptersInfo
    GetAdaptersInfo.restype = c_ulong
    GetAdaptersInfo.argtypes = [LP_IP_ADAPTER_INFO, POINTER(c_ulong)]
    adapterList = (IP_ADAPTER_INFO * 10)()
    buflen = c_ulong(sizeof(adapterList))
    rc = GetAdaptersInfo(byref(adapterList[0]), byref(buflen))
    if rc == 0:
        for a in adapterList:
            adNode = a.ipAddressList
            while True:
                ipAddr = adNode.ipAddress
                if ipAddr:
                    yield ipAddr
                adNode = adNode.next
                if not adNode:
                    break

Применение:

>>> for addr in getIPAddresses():
>>>    print addr
192.168.0.100
10.5.9.207

Как и полагается windll, это будет работать только на Windows.


Одно решение выше, как правило, работает на Windows. Это проблема Linux.
ricree

14
+1 Эта техника по крайней мере пытается вернуть все адреса на машине.
Джейсон Р. Кумбс

1
Этот скрипт не работает на моей машине после возврата первого адреса. Ошибка: «AttributeError: у объекта« LP_IP_ADDR_STRING »нет атрибута« ipAddress »». Я подозреваю, что это как-то связано с адресом IPv6.
Джейсон Р. Кумбс

1
Оказывается, проблема в том, что для всего, кроме первого IP-адреса, adNode не разыменовывается. Добавьте еще одну строку в пример в цикле while, и это работает для меня: adNode = adNode.contents
Джейсон Р. Кумбс

18

На Debian (проверено), и я подозреваю, что большинство Linux ..

import commands

RetMyIP = commands.getoutput("hostname -I")

На MS Windows (проверено)

import socket

socket.gethostbyname(socket.gethostname())

1
Не работает на macOS:hostname: illegal option -- I\nusage: hostname [-fs] [name-of-host]
Дерек 功夫 會

18

Версия, которую я не верю, была опубликована. Я тестировал с python 2.7 на Ubuntu 12.04.

Нашел это решение по адресу: http://code.activestate.com/recipes/439094-get-the-ip-address-associated-with-a-network-inter/

import socket
import fcntl
import struct

def get_ip_address(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    return socket.inet_ntoa(fcntl.ioctl(
        s.fileno(),
        0x8915,  # SIOCGIFADDR
        struct.pack('256s', ifname[:15])
    )[20:24])

Пример результата:

>>> get_ip_address('eth0')
'38.113.228.130'

2
Работает на Python3, Ubuntu 18.04; Строка должна быть байтами: >>> socket.inet_ntoa (fcntl.ioctl (s.fileno (), 0x8915, struct.pack ('256s', 'enp0s31f6' [: 15] .encode ('utf-8')) )) [20:24]) '192.168.1.1'
курсор

12

Вариация на ответ ниндзягецко. Это должно работать в любой локальной сети, которая разрешает широковещательную рассылку UDP и не требует доступа к адресу в локальной сети или в Интернете.

import socket
def getNetworkIp():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    s.connect(('<broadcast>', 0))
    return s.getsockname()[0]

print (getNetworkIp())

Подождите, как <broadcast>действительное имя хоста? !! Сколько таких словесных имен хостов допустимо?
Дев Аггарвал

Это работает для меня в Ubuntu 20.04 - получение 192.168.0.24, а не 127.0.0.1
Льюис Моррис

8

Боюсь, что не существует хороших независимых от платформы способов сделать это, кроме как подключиться к другому компьютеру и заставить его отправить вам ваш IP-адрес. Например: findmyipaddress . Обратите внимание, что это не будет работать, если вам нужен IP-адрес, который находится за NAT, если только компьютер, к которому вы подключаетесь, не находится за NAT.

Вот одно решение, которое работает в Linux: получить IP-адрес, связанный с сетевым интерфейсом .


8

Один простой способ произвести «чистый» вывод через утилиты командной строки:

import commands
ips = commands.getoutput("/sbin/ifconfig | grep -i \"inet\" | grep -iv \"inet6\" | " +
                         "awk {'print $2'} | sed -ne 's/addr\:/ /p'")
print ips

Он покажет все IPv4-адреса в системе.


1
Он не будет отображать все адреса IPv4, потому что ifconfig говорит только о первичных. Вам нужно использовать «ip» из iproute2, чтобы увидеть все адреса.
Хельмут Грон

Это чертовски много оболочки для вопроса о стандартной библиотеке ... Кроме того, синтаксический анализ ifconfig не переносим и даже не будет надежно работать на одной машине.
Доминик Джордж

7

К вашему сведению, я могу убедиться, что метод:

import socket
addr = socket.gethostbyname(socket.gethostname())

Работает в OS X (10.6,10.5), Windows XP и на хорошо управляемом сервере отдела RHEL. Он не работал на очень минимальной виртуальной машине CentOS, на которой я просто взломал ядро. Так что для этого экземпляра вы можете просто проверить адрес 127.0.0.1 и в этом случае сделать следующее:

if addr == "127.0.0.1":
     import commands
     output = commands.getoutput("/sbin/ifconfig")
     addr = parseaddress(output)

А затем проанализировать IP-адрес из вывода. Следует отметить, что ifconfig не входит в PATH обычного пользователя по умолчанию, и поэтому я даю полный путь в команде. Надеюсь, это поможет.


7

Это вариант ответа UnkwnTech - он предоставляет get_local_addr()функцию, которая возвращает основной IP-адрес локальной сети хоста. Я публикую его, потому что это добавляет ряд вещей: поддержку ipv6, обработку ошибок, игнорирование адресов localhost / linklocal и использование адреса TESTNET (rfc5737) для подключения.

# imports
import errno
import socket
import logging

# localhost prefixes
_local_networks = ("127.", "0:0:0:0:0:0:0:1")

# ignore these prefixes -- localhost, unspecified, and link-local
_ignored_networks = _local_networks + ("0.", "0:0:0:0:0:0:0:0", "169.254.", "fe80:")

def detect_family(addr):
    if "." in addr:
        assert ":" not in addr
        return socket.AF_INET
    elif ":" in addr:
        return socket.AF_INET6
    else:
        raise ValueError("invalid ipv4/6 address: %r" % addr)

def expand_addr(addr):
    """convert address into canonical expanded form --
    no leading zeroes in groups, and for ipv6: lowercase hex, no collapsed groups.
    """
    family = detect_family(addr)
    addr = socket.inet_ntop(family, socket.inet_pton(family, addr))
    if "::" in addr:
        count = 8-addr.count(":")
        addr = addr.replace("::", (":0" * count) + ":")
        if addr.startswith(":"):
            addr = "0" + addr
    return addr

def _get_local_addr(family, remote):
    try:
        s = socket.socket(family, socket.SOCK_DGRAM)
        try:
            s.connect((remote, 9))
            return s.getsockname()[0]
        finally:
            s.close()
    except socket.error:
        # log.info("trapped error connecting to %r via %r", remote, family, exc_info=True)
        return None

def get_local_addr(remote=None, ipv6=True):
    """get LAN address of host

    :param remote:
        return  LAN address that host would use to access that specific remote address.
        by default, returns address it would use to access the public internet.

    :param ipv6:
        by default, attempts to find an ipv6 address first.
        if set to False, only checks ipv4.

    :returns:
        primary LAN address for host, or ``None`` if couldn't be determined.
    """
    if remote:
        family = detect_family(remote)
        local = _get_local_addr(family, remote)
        if not local:
            return None
        if family == socket.AF_INET6:
            # expand zero groups so the startswith() test works.
            local = expand_addr(local)
        if local.startswith(_local_networks):
            # border case where remote addr belongs to host
            return local
    else:
        # NOTE: the two addresses used here are TESTNET addresses,
        #       which should never exist in the real world.
        if ipv6:
            local = _get_local_addr(socket.AF_INET6, "2001:db8::1234")
            # expand zero groups so the startswith() test works.
            if local:
                local = expand_addr(local)
        else:
            local = None
        if not local:
            local = _get_local_addr(socket.AF_INET, "192.0.2.123")
            if not local:
                return None
    if local.startswith(_ignored_networks):
        return None
    return local

Я думал, что это мог бы быть действительно хороший ответ .. но он всегда возвращалсяNone
Джейми Линдси

@JamieLindsey Есть ли у вас какие-либо подробности о вашей ОС, конфигурации сети? Кроме того, что такое get_local_addr(remove="www.google.com")возвращение? Регистрация socket.errorвыброса с помощью _get_local_addr () может помочь в диагностике.
Эли Коллинз

6
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]

1
Хм ... на сервере с двумя сетевыми картами это дает один из назначенных IP-адресов, но повторяется три раза. На моем ноутбуке он выдает «127.0.1.1» (повторяется три раза ...) ...
Брин

Дает мне ['fe80::34e8:fe19:1459:2cde%22','fe80::d528:99fb:d572:e289%12', '192.168.56.1', '192.168.1.2']на рабочий стол Windows.
Накилон

5

Это будет работать на большинстве Linux-коробок:

import socket, subprocess, re
def get_ipv4_address():
    """
    Returns IP address(es) of current machine.
    :return:
    """
    p = subprocess.Popen(["ifconfig"], stdout=subprocess.PIPE)
    ifc_resp = p.communicate()
    patt = re.compile(r'inet\s*\w*\S*:\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
    resp = patt.findall(ifc_resp[0])
    print resp

get_ipv4_address()

5

Этот ответ является моей личной попыткой решить проблему получения IP- socket.gethostbyname(socket.gethostname())адреса локальной сети, так как также возвращается 127.0.0.1. Этот метод не требует подключения к Интернету только через локальную сеть. Код для Python 3.x, но может быть легко преобразован для 2.x. Использование UDP Broadcast:

import select
import socket
import threading
from queue import Queue, Empty

def get_local_ip():
        def udp_listening_server():
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.bind(('<broadcast>', 8888))
            s.setblocking(0)
            while True:
                result = select.select([s],[],[])
                msg, address = result[0][0].recvfrom(1024)
                msg = str(msg, 'UTF-8')
                if msg == 'What is my LAN IP address?':
                    break
            queue.put(address)

        queue = Queue()
        thread = threading.Thread(target=udp_listening_server)
        thread.queue = queue
        thread.start()
        s2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        waiting = True
        while waiting:
            s2.sendto(bytes('What is my LAN IP address?', 'UTF-8'), ('<broadcast>', 8888))
            try:
                address = queue.get(False)
            except Empty:
                pass
            else:
                waiting = False
        return address[0]

if __name__ == '__main__':
    print(get_local_ip())

1
Что произойдет, если вы запустите это одновременно на двух машинах в одной сети? Когда вы транслируете свое сообщение в сети, все машины получат «Какой у меня IP-адрес в локальной сети». Ваш udp_listening_server может ответить «ваш IP-адрес - ххх» на сообщение.
Николя Дефрану

4

127.0.1.1 это ваш реальный IP адрес. Вообще говоря, компьютер может иметь любое количество IP-адресов. Вы можете отфильтровать их для частных сетей - 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12 и 192.168.0.0/16.

Однако кроссплатформенного способа получения всех IP-адресов не существует. В Linux вы можете использовать SIOCGIFCONFioctl.


2
Он имеет в виду его внешне видимый IP. Диапазон 127. *. *. * Обычно относится к localhost или внутренней сети, что явно не то, что он хочет.
Cerin

4

Небольшое уточнение версии команд, которая использует команду IP и возвращает адреса IPv4 и IPv6:

import commands,re,socket

#A generator that returns stripped lines of output from "ip address show"
iplines=(line.strip() for line in commands.getoutput("ip address show").split('\n'))

#Turn that into a list of IPv4 and IPv6 address/mask strings
addresses1=reduce(lambda a,v:a+v,(re.findall(r"inet ([\d.]+/\d+)",line)+re.findall(r"inet6 ([\:\da-f]+/\d+)",line) for line in iplines))
#addresses1 now looks like ['127.0.0.1/8', '::1/128', '10.160.114.60/23', 'fe80::1031:3fff:fe00:6dce/64']

#Get a list of IPv4 addresses as (IPstring,subnetsize) tuples
ipv4s=[(ip,int(subnet)) for ip,subnet in (addr.split('/') for addr in addresses1 if '.' in addr)]
#ipv4s now looks like [('127.0.0.1', 8), ('10.160.114.60', 23)]

#Get IPv6 addresses
ipv6s=[(ip,int(subnet)) for ip,subnet in (addr.split('/') for addr in addresses1 if ':' in addr)]

4

Ну, вы можете использовать команду «ip route» в GNU / Linux, чтобы узнать ваш текущий IP-адрес.

Это показывает IP, данный интерфейсу сервером DHCP, работающим на маршрутизаторе / модеме. Обычно «192.168.1.1/24» - это IP-адрес для локальной сети, где «24» означает диапазон возможных IP-адресов, заданных сервером DHCP в пределах диапазона маски.

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

#! /usr/bin/env python

import sys , pynotify

if sys.version_info[1] != 7:
   raise RuntimeError('Python 2.7 And Above Only')       

from subprocess import check_output # Available on Python 2.7+ | N/A 

IP = check_output(['ip', 'route'])
Split_Result = IP.split()

# print Split_Result[2] # Remove "#" to enable

pynotify.init("image")
notify = pynotify.Notification("Ip", "Server Running At:" + Split_Result[2] , "/home/User/wireless.png")    
notify.show()    

Преимущество этого в том, что вам не нужно указывать сетевой интерфейс. Это очень полезно при запуске сервера сокетов

Вы можете установить PyNotify, используя easy_install или даже Pip:

easy_install py-notify

или

pip install py-notify

или внутри скрипта / интерпретатора Python

from pip import main

main(['install', 'py-notify'])

4

Если вы ищете IPV4-адрес, отличный от вашего IP-адреса локального хоста 127.0.0.1, вот хороший фрагмент кода Python:

import subprocess
address = subprocess.check_output(['hostname', '-s', '-I'])
address = address.decode('utf-8') 
address=address[:-1]

Который также можно записать в одну строку:

address = subprocess.check_output(['hostname', '-s', '-I']).decode('utf-8')[:-1]

Даже если вы кладете localhostв /etc/hostnameкод все равно будет давать свой локальный IP - адрес.


4

Для Linux, вы можете просто использовать check_outputв hostname -Iсистемных командах , как так:

from subprocess import check_output
check_output(['hostname', '-I'])

для googlers, я знаю, что вопрос был для кроссплатформенного решения
Kasper Skytte Andersen

3

Примечание: это не использует стандартную библиотеку, но довольно просто.

$ pip install pif

from pif import get_public_ip
get_public_ip()

3
вопросы были о том, как найти IP с помощью stdlib
Alexandru Chirila

3

netifaces доступен через pip и easy_install. (Я знаю, это не в базе, но это может стоить установки.)

netifaces имеет некоторые странности на разных платформах:

  • Интерфейс localhost / loop-back не всегда может быть включен (Cygwin).
  • Адреса перечислены для каждого протокола (например, IPv4, IPv6), а протоколы для каждого интерфейса. В некоторых системах (Linux) каждая пара протокол-интерфейс имеет свой собственный связанный интерфейс (используя нотацию имя_интерфейса: n), в то время как в других системах (Windows) один интерфейс будет иметь список адресов для каждого протокола. В обоих случаях есть список протоколов, но он может содержать только один элемент.

Вот код netifaces для игры:

import netifaces

PROTO = netifaces.AF_INET   # We want only IPv4, for now at least

# Get list of network interfaces
# Note: Can't filter for 'lo' here because Windows lacks it.
ifaces = netifaces.interfaces()

# Get all addresses (of all kinds) for each interface
if_addrs = [netifaces.ifaddresses(iface) for iface in ifaces]

# Filter for the desired address type
if_inet_addrs = [addr[PROTO] for addr in if_addrs if PROTO in addr]

iface_addrs = [s['addr'] for a in if_inet_addrs for s in a if 'addr' in s]
# Can filter for '127.0.0.1' here.

Приведенный выше код не отображает адрес обратно на имя его интерфейса (полезно для генерации правил ebtables / iptables на лету). Итак, вот версия, которая хранит вышеуказанную информацию с именем интерфейса в кортеже:

import netifaces

PROTO = netifaces.AF_INET   # We want only IPv4, for now at least

# Get list of network interfaces
ifaces = netifaces.interfaces()

# Get addresses for each interface
if_addrs = [(netifaces.ifaddresses(iface), iface) for iface in ifaces]

# Filter for only IPv4 addresses
if_inet_addrs = [(tup[0][PROTO], tup[1]) for tup in if_addrs if PROTO in tup[0]]

iface_addrs = [(s['addr'], tup[1]) for tup in if_inet_addrs for s in tup[0] if 'addr' in s]

И нет, я не влюблен в списки. Просто так работает мой мозг в эти дни.

Следующий фрагмент напечатает все это:

from __future__ import print_function  # For 2.x folks
from pprint import pprint as pp

print('\nifaces = ', end='')
pp(ifaces)

print('\nif_addrs = ', end='')
pp(if_addrs)

print('\nif_inet_addrs = ', end='')
pp(if_inet_addrs)

print('\niface_addrs = ', end='')
pp(iface_addrs)

Наслаждайтесь!


netifaces действительно облегчает жизнь, имея дело с этой проблемой.
Дрейк Гуань

3

Версия Python 3.4, использующая недавно представленный пакет asyncio.

async get_local_ip():
    loop = asyncio.get_event_loop()
    transport, protocol = await loop.create_datagram_endpoint(
        asyncio.DatagramProtocol,
        remote_addr=('8.8.8.8', 80))
    result = transport.get_extra_info('sockname')[0])
    transport.close()
    return result

Это основано на превосходном ответе UnkwnTech .


3

Чтобы получить IP-адрес, вы можете использовать команду оболочки прямо в Python :

import socket, subprocess

def getIpAndHostname():
    hostname =  socket.gethostname()

    shell_cmd = "ifconfig | awk '/inet addr/{print substr($2,6)}'"
    proc = subprocess.Popen([shell_cmd], stdout=subprocess.PIPE, shell=True)
    (out, err) = proc.communicate()

    ip_list = out.split('\n')
    ip = ip_list[0]

    for _ip in ip_list:
        try:
            if _ip != "127.0.0.1" and _ip.split(".")[3] != "1":
                ip = _ip
        except:
            pass
    return ip, hostname

ip_addr, hostname = getIpAndHostname()
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.