Пошук локальних IP-адрес за допомогою stdlib Python


545

Як я можу самостійно знайти локальні IP-адреси (тобто 192.168.xx або 10.0.xx) на платформі Python, використовуючи лише стандартну бібліотеку?


7
Локальний IP? Або публічний IP? Як ви збираєтеся мати справу з системами з декількома IP-адресами?
Саргун Діллон

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

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

12
@phihag Ви абсолютно правильні, дякую, що виправили мою дурість
Фредрік Піль

1
Більш фундаментальна проблема тут полягає в тому, що в правильно написаній сучасній мережевій програмі правильний (набір) локальних IP-адрес залежить від однолітка або набору потенційних партнерів. Якщо локальна IP-адреса потрібна для bindсокета до певного інтерфейсу, то це питання політики. Якщо локальна IP-адреса потрібна для того, щоб передати її одноранговому, щоб одноранговий користувач міг "передзвонити", тобто відкрити з'єднання назад до локальної машини, ситуація залежить від того, чи є NAT (Network Address Translation) ящики між ними. Якщо немає НАТ getsockname- це хороший вибір.
Pekka Nikander

Відповіді:


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

Це не буде працювати завжди (повертається 127.0.0.1на машинах, у яких ім'я хоста вказано /etc/hostsяк 127.0.0.1), паліативним буде те, що показує gimel, socket.getfqdn()замість цього використовувати . Звичайно, вашій машині потрібно вирішувати ім’я хоста.


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

20
Варіація: socket.gethostbyname (socket.getfqdn ())
gimel

55
Це, здається, повертає лише одну IP-адресу. Що робити, якщо машина має кілька адрес?
Джейсон Р. Кумбс

26
У Ubuntu це чомусь повертає 127.0.1.1.
slikts

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

460

Я щойно знайшов це, але це здається трохи хакітським, однак, кажуть, спробував це на * 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
Приємно, якщо у вас є кілька інтерфейсів на машині, і вам потрібен той, який
спрямовується,

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-адресою, яка не знаходиться на локальній локальній мережі (неважливо, чи вона вгору чи вниз), вона працюватиме без залежностей і без мережевого трафіку.
похмурий

253

Цей метод повертає "первинний" 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 удома, це не відображатиме ваш загальнодоступний NAT IP, а натомість ваш приватний IP в локальній мережі, який має маршрут за замовчуванням до вашого локального маршрутизатора WIFI; отримання зовнішнього IP-маршрутизатора вашого Wi-Fi-роутера вимагає або запустити це в ТАКІй коробці, або підключитись до зовнішньої служби, наприклад, whatismyip.com/whatismyipaddress.com, яка може відображати назад IP ..., але це абсолютно відрізняється від початкового питання. :)


7
Працює в Raspbian з Python 2 і 3!
pierce.jason

3
Блискуча. Працює на Win7,8,8.1 + Linux Mint & Arch, включаючи VM.
шерми

2
Працює в Windows 10 Pro! Дякую, Джеймісон Бекер!
varantes

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

9
Це має бути прийнятою відповіддю. 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
@Alexander, /etc/resolve.conf: No such file or directoryі у мене локальна IPv4-адреса, показана ifconfig.
анатолійський технонік

2
Я можу підтвердити, що оновлена ​​версія працює з Ubuntu 14.04 і з Python2, і з Py3k.
Улі Келер

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

16
тільки тому, що ви можете написати цей код на одному рядку, це не означає, що вам слід ...
JackLeo

91

Можна використовувати модуль netiface . Просто введіть:

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
Це була б моя улюблена відповідь, за винятком того, що netiface не підтримує IPv6 у Windows і, здається, не підтримується. Хтось придумав, як отримати IPv6 адреси в Windows?
Жан-Поль Кальдероне

3
netiface не підтримує py3k, і потрібен компілятор C, який є PITA у Windows.
Метт Столяр

4
@MattJoiner Ніщо з цього не відповідає дійсності (остання версія має бінарні файли Windows на PyPI і підтримує Py3K).
аластер

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

47

Метод Socket API

див. https://stackoverflow.com/a/28950776/711085

Недоліки:

  • Не крос-платформа.
  • Потрібно більше резервного коду, пов'язаного з наявністю певних адрес в Інтернеті
  • Це також не вийде, якщо ви стоїте за NAT
  • Можливо, створюється UDP-з'єднання, не залежне від доступності DNS (як правило, провайдера) (див. Інші відповіді щодо таких ідей, як використання 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)

або якщо використовується 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-адреса, про яку запитує ОП, є лише чітко визначеною щодо одного шару зв'язку, тому вам потрібно вказати це ("це мережева карта чи кабель Ethernet, про який ми говоримо?") . На це питання може бути кілька неповторних відповідей. Однак глобальна IP-адреса у всесвітній мережі, ймовірно, чітко визначена (за відсутності масивної фрагментації мережі): ймовірно, шлях повернення через шлюз, який може отримати доступ до TLD.


Це поверне вашу локальну адресу, якщо ви стоїте за NAT. Якщо ви підключаєтесь до Інтернету, ви можете підключитися до веб-служби, яка повертає одну з ваших загальнодоступних IP-адрес.
фігаг

Він не створює TCP-з'єднання, оскільки створює UDP-з'єднання.
Ануй Гупта

2
В якості альтернативи у версії API сокета замініть s.connect (("ВСТАВЛЯЄТЬСЯ ЦІЛЬНИЙ ЦЕНТР 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модуль пітонів, який може працювати - man7.org/linux/man-pages/man3/getifaddrs.3.html
tMC

1
@Maddy ioctl - це застарілий інтерфейс, я не вірю, що підтримує IPv6 і, ймовірно, ніколи не буде. Я думаю, що «правильний» шлях - це через Netlink, який не дуже простий у Python. Я думаю, що libc повинен мати функцію getifaddrs, до якої можна отримати доступ через модуль ctypes pythons, який може працювати - 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 щодо передбачуваних імен інтерфейсу, як вказував Олександр . У таких випадках вам потрібно вручну замінити список назви інтерфейсів у вашій системі або скористатися іншим рішенням, таким як netiface .


2
Це несумісно з новими передбачуваними іменами інтерфейсу Linux, такими як enp0s25. Для отримання додаткової інформації дивіться wiki.archlinux.org/index.php/Network_Configuration#Device_names
Олександр

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 - байти () не сподобався 2-й аргумент. Але це спрацювало: struct.pack('256s', bytes(ifname[:15]))
colm.anseo

24

Я використовую це на своїх машинах ubuntu:

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

Це не працює.


Приємно і просто. Працює і на Linux AMI Amazon, але тільки якщо я root. Інакше я отримав би помилку: 'sh: ifconfig: команда не знайдена'
IgorGanapolsky

Отже, ви повинні використовувати "/ sbin / ifconfig", як сказав gavaletz. Він також працює над Red Hat 4.1.2-48.
ІгорГанапольський

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

5
І якщо конфігурація також застаріла. Використовуйте iproute2.
Гельмут Гроне

Отримати всі ips: import sh; [ip.split () [1] [5:] для ip у фільтрі (лямбда x: 'inet addr' в x, sh.ifconfig (). спліт ("\ 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>дійсне ім’я хоста? !! Скільки таких видів словесних імен хостів є дійсними?
Dev Aggarwal

Це працює для мене на Ubuntu 20.04 - отримання 192.168.0.24 не 127.0.0.1
Льюїс Морріс

8

Я боюся, що немає жодних хороших незалежних від платформи способів зробити це, окрім підключення до іншого комп’ютера та надання йому надіслати вашу IP-адресу. Наприклад: findmyipadress . Зауважте, що це не буде працювати, якщо вам потрібна 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

FYI Я можу переконатися, що метод:

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

Працює в OS X (10.6,10.5), Windows XP та на добре керованому сервері відділу RHEL. Це не спрацьовувало надто мінімальною CentOS VM, що я просто роблю злом ядра. Тож для цього примірника ви можете просто перевірити наявність 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 адресу хоста LAN. Я розміщую це повідомлення, оскільки це додає низку речей: підтримка ipv6, керування помилками, ігнорування localhost / linklocal addr та використовує TESTNET addr (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
Хм ... на сервері з двома NIC це дає одну із призначених IP-адрес, але повторюється три рази. На моєму ноутбуці він дає "127.0.1.1" (повторюється три рази ...) ...
bryn

Подає мене ['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:

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-адреса ххх".
Nicolas Defranoux

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 або внутрішньої мережі, що явно не є тим, що він хоче.
Серін

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()    

Перевага цього в тому, що вам не потрібно вказувати мережевий інтерфейс. Це досить корисно під час роботи сервера socket

Ви можете встановити 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, ось охайний фрагмент пітонних кодів:

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 - адреса.



3

Примітка: тут використовується не стандартна бібліотека, а досить проста.

$ pip встановити pif

from pif import get_public_ip
get_public_ip()

3
питання стосувалися пошуку ІР за допомогою stdlib
Александру Чиріла

3

netiface доступний через pip та easy_install. (Я знаю, це не в базі, але це може бути варте встановлення.)

netiface має деякі диваки на різних платформах:

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

Ось декілька кодів netiface, з якими можна грати:

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)

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


netiface дуже полегшує життя, займаючись цим питанням.
Дрейк Гуан

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 адреси можна використовувати команду shell безпосередньо в 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.