Znajdowanie lokalnych adresów IP za pomocą stdlib Pythona

545

Jak mogę znaleźć lokalne adresy IP (tj. 192.168.xx lub 10.0.xx) na platformie Python niezależnie i używając tylko standardowej biblioteki?

UnkwnTech
źródło
7
Lokalny adres IP? Czy publiczne IP? Jak poradzisz sobie z systemami z wieloma adresami IP?
Sargun Dhillon
użyj ifconfig -ai użyj danych stamtąd ...
Fredrik Pihl
16
@Fredrik To zły pomysł. Przede wszystkim niepotrzebnie rozpoczynasz nowy proces, co może uniemożliwić programowi pracę w ściśle zamkniętych konfiguracjach (lub musisz zezwolić na prawa, których Twój program nie potrzebuje). Po drugie, wprowadzisz błędy dla użytkowników różnych lokalizacji. Po trzecie, jeśli zdecydujesz się w ogóle uruchomić nowy program, nie powinieneś uruchamiać przestarzałego programu - ip addrjest on o wiele bardziej odpowiedni (i łatwiej go analizować, uruchamiać).
phihag
12
@ phihag masz całkowitą rację, dziękuję za naprawienie mojej głupoty
Fredrik Pihl
1
Bardziej fundamentalnym problemem jest to, że we właściwie napisanym nowoczesnym programie sieciowym właściwy (zbiór) lokalnych adresów IP zależy od peera lub zestawu potencjalnych peerów. Jeśli lokalny adres IP jest potrzebny do bindgniazda określonego interfejsu, jest to kwestia zasad. Jeśli lokalny adres IP jest potrzebny do przekazania go peerowi, aby peer mógł „oddzwonić”, tj. W celu ponownego nawiązania połączenia z maszyną lokalną, sytuacja zależy od tego, czy istnieje NAT (translacja adresów sieciowych) pola pomiędzy. Jeśli nie ma NAT, getsocknameto dobry wybór.
Pekka Nikander

Odpowiedzi:

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

Nie zawsze będzie to działać (zwraca 127.0.0.1na komputerach z nazwą hosta w /etc/hostsas 127.0.0.1), paliative byłby tym, co pokazuje gimel, użyj socket.getfqdn()zamiast tego. Oczywiście twój komputer potrzebuje rozpoznawalnej nazwy hosta.

Vinko Vrsalovic
źródło
43
Należy zauważyć, że nie jest to rozwiązanie niezależne od platformy. Wiele Linuksów zwróci 127.0.0.1 jako adres IP za pomocą tej metody.
Jason Baker
20
Odmiana: socket.gethostbyname (socket.getfqdn ())
gimel
55
Wygląda na to, że zwraca tylko jeden adres IP. Co się stanie, jeśli urządzenie ma wiele adresów?
Jason R. Coombs
26
Na Ubuntu z jakiegoś powodu zwraca 127.0.1.1.
slikts
13
@Jason R. Coombs, użyj następującego kodu, aby pobrać listę adresów IPv4, które należą do hosta:socket.gethostbyname_ex(socket.gethostname())[-1]
Barmaley
459

Właśnie to znalazłem, ale wydaje się to trochę hackingowe, ale mówią, że wypróbowałem to na * nix, a ja zrobiłem na Windowsie i zadziałało.

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

Zakłada się, że masz dostęp do Internetu i nie ma lokalnego serwera proxy.

UnkwnTech
źródło
31
Fajnie, jeśli masz kilka interfejsów na komputerze i potrzebujesz tego, który prowadzi do np.
Gmail.com
4
Dobrym pomysłem może być przechwycenie wyjątków dotyczących socket.error, które mogą zostać podniesione przez s.connect ()!
phobie
39
Lepiej byłoby użyć adresu IP zamiast nazwy domeny - musi być szybszy i niezależny od dostępności DNS. Np. Możemy użyć 8.8.8.8 IP - publicznego serwera DNS Google.
wobmene
10
Bardzo sprytny, działa idealnie. Zamiast Gmaila lub 8.8.8.8 możesz również użyć adresu IP lub adresu serwera, z którego chcesz się widzieć, jeśli ma to zastosowanie.
Umowa prof. Falkena została naruszona
3
Ten przykład ma zewnętrzną zależność od możliwości faktycznego rozwiązania gmail.com. Jeśli ustawisz go na adres IP, który nie znajduje się w lokalnej sieci LAN (nie ma znaczenia, czy to w górę, czy w dół), będzie działał bez zależności i bez ruchu sieciowego.
ponury
253

Ta metoda zwraca „podstawowy” adres IP w lokalnym polu (tym z domyślną trasą) .

  • NIE wymaga w ogóle routowalnego dostępu do sieci ani żadnego połączenia.
  • Działa, nawet jeśli wszystkie interfejsy są odłączone od sieci.
  • NIE potrzebuje ani nawet nie próbuje zdobyć nigdzie indziej .
  • Współpracuje z NAT, publicznymi, prywatnymi, zewnętrznymi i wewnętrznymi adresami IP
  • Czysty Python 2 (lub 3) bez zewnętrznych zależności.
  • Działa w systemach Linux, Windows i OSX.

Python 3 lub 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

Zwraca pojedynczy adres IP, który jest podstawowy (ten z domyślną trasą). Jeśli zamiast tego potrzebujesz wszystkich adresów IP dołączonych do wszystkich interfejsów (w tym localhost itp.), Zapoznaj się z tą odpowiedzią .

Jeśli jesteś w domu za firewallem NAT, takim jak WiFi box, to nie pokaże twojego publicznego adresu IP NAT, ale twój prywatny adres IP w sieci lokalnej, która ma domyślną trasę do lokalnego routera WIFI; uzyskanie zewnętrznego adresu IP routera Wi-Fi wymagałoby uruchomienia tego na tym polu lub połączenia się z usługą zewnętrzną, taką jak whatismyip.com/whatismyipaddress.com, która może odzwierciedlać adres IP ... ale to zupełnie inne pytanie niż pierwotne. :)

błąd krytyczny
źródło
7
Działa w języku Raspbian z Python 2 i 3!
pierce.jason
3
Znakomity. Działa na Win7,8,8.1 + Linux Mint & Arch, w tym na maszynach wirtualnych.
Shermy,
2
Działa w systemie Windows 10 Pro! Dziękuję, Jamieson Becker!
varantes 12.04.17
3
Z jakiegoś powodu nie działa to w systemie Mac OS X El Capitan 10.11.6 (generuje wyjątek Błąd systemu operacyjnego: [Errno 49] Nie można przypisać żądanego adresu). Zmiana portu z „0” na „1”: s.connect ((„10.255.255.255”, 1)) działał dla mnie zarówno na Mac OS X, jak i Linux Ubuntu 17.04
Pedro Scarapicchia Junior
9
To powinna być zaakceptowana odpowiedź. socket.gethostbyname(socket.gethostname())daje okropne wyniki.
Jason Floyd
142

Jako alias myip, powinien działać wszędzie:

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])'"
  • Działa poprawnie z Python 2.x, Python 3.x, nowoczesnymi i starymi dystrybucjami Linux, OSX / macOS i Windows do wyszukiwania aktualnego adresu IPv4.
  • Nie zwróci poprawnego wyniku dla maszyn z wieloma adresami IP, IPv6, bez skonfigurowanego adresu IP lub bez dostępu do Internetu.

To samo co powyżej, ale tylko kod 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])
  • Spowoduje to zgłoszenie wyjątku, jeśli nie skonfigurowano adresu IP.

Wersja, która będzie działać również w sieciach LAN bez połączenia z Internetem:

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

(dzięki @ccpizza )


Tło :

Korzystanie socket.gethostbyname(socket.gethostname())nie działało tutaj, ponieważ jeden z komputerów, na których byłem, miał /etc/hostszduplikowane wpisy i odniesienia do siebie. socket.gethostbyname()zwraca tylko ostatni wpis w /etc/hosts.

To była moja pierwsza próba usunięcia wszystkich adresów zaczynających się od "127.":

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

Działa to z Pythonem 2 i 3 w systemach Linux i Windows, ale nie obsługuje kilku urządzeń sieciowych ani IPv6. Jednak przestał działać na ostatnich dystrybucjach Linuksa, więc zamiast tego spróbowałem tej alternatywnej techniki. Próbuje połączyć się z serwerem Google DNS 8.8.8.8na porcie 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])

Następnie połączyłem dwie powyższe techniki w jedną linijkę, która powinna działać wszędzie, i myipna początku tej odpowiedzi utworzyłem alias i fragment kodu w języku Python.

Wraz ze wzrostem popularności protokołu IPv6 oraz w przypadku serwerów z wieloma interfejsami sieciowymi korzystanie z zewnętrznego modułu Python do znajdowania adresu IP jest prawdopodobnie bardziej niezawodne i niezawodne niż którakolwiek z wymienionych tutaj metod.

Alexander
źródło
2
@Alexander: Samo powiedzenie, że ta odpowiedź jest o wiele mniej przydatne niż kiedyś (i to nie jest tak, jakby odfiltrowywać duplikaty, to wielka sprawa;). Według dokumentacji socket.getaddrinfo()powinna działać konsekwentnie na różnych platformach - ale sprawdziłem to tylko w systemie Linux, nie zawracałem sobie głowy innymi systemami.
Wladimir Palant
1
@Alexander /etc/resolve.conf: No such file or directoryi mam lokalny adres IPv4 pokazany przez ifconfig.
anatoly techtonik,
2
Mogę potwierdzić, że zaktualizowana wersja działa z Ubuntu 14.04 zarówno z Python2, jak i Py3k.
Uli Köhler
4
„Aktualizacja” pokazuje niezłą sztuczkę z connect () na gnieździe UDP. Nie wysyła żadnego ruchu, ale pozwala znaleźć adres nadawcy dla pakietów do określonego odbiorcy. Port jest prawdopodobnie nieistotny (nawet 0 powinno działać). Na hoście z wieloma domami ważne jest, aby wybrać adres w odpowiedniej podsieci.
Peter Hansen
16
tylko dlatego, że możesz napisać ten kod w jednym wierszu, nie oznacza to, że powinieneś ...
JackLeo
91

Możesz użyć modułu netifaces . Po prostu wpisz:

pip install netifaces

w twojej powłoce poleceń i zainstaluje się na domyślnej instalacji Pythona.

Następnie możesz użyć tego w następujący sposób:

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

Na moim komputerze wydrukowano:

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

Autor tego modułu twierdzi, że powinien działać w systemach Windows, UNIX i Mac OS X.

DzinX
źródło
20
Jak stwierdzono w pytaniu, chcę czegoś z domyślnej instalacji, ponieważ nie są potrzebne żadne dodatkowe instalacje.
UnkwnTech
4
To byłaby moja ulubiona odpowiedź, z wyjątkiem tego, że netifaces nie obsługuje IPv6 w systemie Windows i wydaje się nieobsługiwany. Czy ktoś wymyślił, jak uzyskać adresy IPv6 w systemie Windows?
Jean-Paul Calderone
3
netifaces nie obsługuje py3k i wymaga kompilatora C, który jest PITA w systemie Windows.
Matt Joiner
4
@MattJoiner Żadna z tych rzeczy nie jest już prawdą (najnowsza wersja ma binaria Windows na PyPI i obsługuje Py3K).
alastair
4
@ Jean-PaulCalderone FWIW, najnowsza wersja netifaces czyni obsługę IPv6 w systemie Windows.
alastair
47

Metoda interfejsu API gniazda

patrz https://stackoverflow.com/a/28950776/711085

Wady:

  • Nie na różnych platformach.
  • Wymaga więcej kodu zastępczego, związanego z istnieniem określonych adresów w Internecie
  • To również nie zadziała, jeśli masz za sobą NAT
  • Prawdopodobnie tworzy połączenie UDP, niezależne od dostępności DNS (zwykle dostawcy ISP) (zobacz inne odpowiedzi na pomysły, takie jak korzystanie z 8.8.8.8: serwer Google (przypadkowo także DNS))
  • Upewnij się, że adres docelowy jest NIEDOSTĘPNY, podobnie jak numeryczny adres IP, który ma gwarancję, że będzie nieużywany. NIE używaj niektórych domen, takich jak fakesubdomain.google.com lub somefakewebsite.com; nadal będziesz spamować tę stronę (teraz lub w przyszłości), a także spamować swoje własne skrzynki sieciowe.

Metoda odbłyśnika

(Pamiętaj, że nie odpowiada to na pytanie OP dotyczące lokalnego adresu IP, np. 192.168 ...; daje ci twój publiczny adres IP, który może być bardziej pożądany w zależności od przypadku użycia).

Możesz wysłać zapytanie do niektórych witryn, takich jak whatismyip.com (ale z interfejsem API), takich jak:

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)

lub jeśli używasz 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)

Zalety:

  • Zaletą tej metody jest to, że jest ona wieloplatformowa
  • Działa zza brzydkich NAT (np. Routera domowego).

Wady (i obejścia):

  • Wymaga, aby ta strona była uruchomiona, format się nie zmieniał (prawie na pewno się nie zmieni), a twoje serwery DNS działają. Można złagodzić ten problem, sprawdzając również inne odbijające adresy IP innych firm w przypadku awarii.
  • Możliwy wektor ataku, jeśli nie zapytasz wielu odbłyśników (aby nie dopuścić do tego, by zaatakowany reflektor powiedział ci, że twój adres jest czymś innym, niż nie), lub jeśli nie użyjesz HTTPS (aby zapobiec atakowi typu man-in-the-middle udając być serwerem)

edytuj : Chociaż początkowo myślałem, że te metody były naprawdę złe (chyba że użyjesz wielu awaryjnych metod, kod może być nieistotny za wiele lat od teraz), stawia pytanie „co to jest internet?”. Komputer może mieć wiele interfejsów wskazujących wiele różnych sieci. Aby uzyskać dokładniejszy opis tego tematu, odwiedź Google forgateways and routes. Komputer może być w stanie uzyskać dostęp do sieci wewnętrznej za pośrednictwem wewnętrznej bramy lub uzyskać dostęp do sieci światowej za pośrednictwem bramy na przykład na routerze (zwykle tak jest). Lokalny adres IP, o który prosi OP, jest dobrze zdefiniowany tylko w odniesieniu do pojedynczej warstwy łącza, więc musisz to określić („czy to karta sieciowa, czy kabel Ethernet, o którym mówimy?”) . Może być wiele niejednoznacznych odpowiedzi na postawione pytanie. Jednak globalny adres IP w sieci WWW jest prawdopodobnie dobrze zdefiniowany (przy braku znacznego rozdrobnienia sieci): prawdopodobnie ścieżka powrotna przez bramę, która może uzyskać dostęp do TLD.

ninjagecko
źródło
Spowoduje to zwrócenie twojego adresu LAN, jeśli jesteś za NAT. Jeśli łączysz się z Internetem, możesz połączyć się z usługą internetową, która zwraca jeden z twoich publicznych adresów IP.
phihag
Nie tworzy połączenia TCP, ponieważ tworzy połączenie UDP.
Anuj Gupta
2
Alternatywnie w wersji API gniazda zamień s.connect (('INSERT SOME TARGET WEBSITE.com', 0)) na s.setsockopt (socket.SOL_SOCKET, socket.SO_BROADCAST, 1); s.connect (('< broadcast> ', 0)), aby uniknąć wyszukiwania DNS. (
Wydaje
45

Jeśli komputer ma trasę do Internetu, zawsze będzie działać, aby uzyskać preferowany lokalny adres IP, nawet jeśli / etc / hosts nie jest ustawiony poprawnie.

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]
Collin Anderson
źródło
jak to działa ? , 8.8.8.8czy serwer Google dns możemy to zrobić z lokalnym serwerem dns?
Ciasto piekarz
39

W systemie 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'
>>> 
tMC
źródło
Czy to skutecznie otwiera gniazdo, z którym nic nie robi, a Ty sprawdzasz surowe dane o tym gnieździe, aby uzyskać lokalny adres IP?
Dave
1
Gniazdo jest otwarte, aby uzyskać fd do komunikacji z jądrem (via ioctl). Gniazdo nie jest powiązane z interfejsem, o którym chcesz uzyskać informacje o adresatach - to tylko mechanizm komunikacji między przestrzenią użytkownika a jądrem. en.wikipedia.org/wiki/Ioctl lxr.free-electrons.com/source/net/socket.c
tMC
2
Działa na Python3 z jedną modyfikacją: struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)należy go zastąpićstruct.pack('16sH14s', iface.encode('utf-8'), socket.AF_INET, b'\x00'*14)
pepoluan
1
@ChristianFischer ioctljest starszym interfejsem. Nie sądzę, że obsługuje IPv6 i prawdopodobnie nigdy nie będzie. Myślę, że „właściwą” drogą jest Netlink, co nie jest bardzo proste w Pythonie. Myślę, że libc powinien mieć funkcję, do getifaddrsktórej można uzyskać dostęp poprzez ctypesmoduł pytonów , który może działać - man7.org/linux/man-pages/man3/getifaddrs.3.html
tMC
1
@Maddy ioctl jest starszym interfejsem. Nie sądzę, że obsługuje IPv6 i prawdopodobnie nigdy nie będzie. Myślę, że „właściwą” drogą jest Netlink, co nie jest bardzo proste w Pythonie. Myślę, że libc powinien mieć funkcję getifaddrs, do której można uzyskać dostęp poprzez moduł pyttypów
ctypes,
27

używam następującego modułu:

#!/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

Testowany z Windows i Linuxem (i nie wymaga dodatkowych modułów) przeznaczony do użytku w systemach, które są w jednej sieci LAN opartej na IPv4.

Naprawiona lista nazw interfejsów nie działa w najnowszych wersjach systemu Linux, które przyjęły zmianę systemd v197 dotyczącą przewidywalnych nazw interfejsów, jak wskazał Alexander . W takich przypadkach należy ręcznie zastąpić listę nazwami interfejsów w systemie lub użyć innego rozwiązania, takiego jak netifaces .

Smerlin
źródło
2
Jest to niezgodne z nowymi przewidywalnymi nazwami interfejsu Linux, takimi jak enp0s25. Aby uzyskać więcej informacji, zobacz wiki.archlinux.org/index.php/Network_Configuration#Device_names
Alexander
2
Używałem Pythona 3.4 i część „struct.pack (...)” musiała zostać zmieniona na „struct.pack („ 256s ”, bytes (ifname [: 15],„ utf-8 ')) ”. Zobacz to pytanie: stackoverflow.com/q/27391167/76010
Bakanekobrain
1
na Raspbian w / Python 2.7.3 - bytes () nie podobał się drugi argument. Ale to zadziałało: struct.pack('256s', bytes(ifname[:15]))
colm.anseo
24

Używam tego na moich maszynach Ubuntu:

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

To nie działa

shino
źródło
Ładne i proste. Działa również na Amazon AMI Linux, ale tylko jeśli jestem rootem. W przeciwnym razie
dostałbym
Dlatego powinieneś używać „/ sbin / ifconfig”, jak powiedział gavaletz. Działa również na Red Hat 4.1.2-48.
IgorGanapolsky
7
Przestarzałe od 2.6. Użyj modułu podprocesu do uruchamiania poleceń.
Colin Dunklau
5
I jeśli ifconfig jest również przestarzałe. Użyj iproute2.
Helmut Grohne
Zdobądź wszystkie ips: import sh; [ip.split () [1] [5:] dla ip w filtrze (lambda x: 'inet addr' w x, sh.ifconfig (). split ("\ n"))]
Gabriel Littman
20

Jeśli nie chcesz korzystać z zewnętrznych pakietów i nie chcesz polegać na zewnętrznych serwerach internetowych, może to pomóc. To przykładowy kod, który znalazłem w Google Code Search i zmodyfikowałem, aby zwrócić wymagane informacje:

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

Stosowanie:

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

Jak się opiera windll, działa to tylko w systemie Windows.

DzinX
źródło
Powyższe jedno liniowe rozwiązanie ogólnie działa w systemie Windows. Problem dotyczy Linuksa.
ricree
14
+1 Ta technika przynajmniej próbuje zwrócić wszystkie adresy na maszynie.
Jason R. Coombs
1
Ten skrypt nie działa na moim komputerze po zwróceniu pierwszego adresu. Błąd to „AttributeError: Obiekt„ LP_IP_ADDR_STRING ”nie ma atrybutu„ ipAddress ”„ Podejrzewam, że ma to coś wspólnego z adresem IPv6.
Jason R. Coombs
1
Okazuje się, że problemem jest to, że dla niczego poza pierwszym adresem IP, adNode nie jest zaniedbywany. Dodaj jeszcze jedną linię do przykładu w pętli while i to działa dla mnie: adNode = adNode.contents
Jason R. Coombs
18

Na Debianie (testowane) i podejrzewam, że większość Linuksa ..

import commands

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

W systemie MS Windows (testowane)

import socket

socket.gethostbyname(socket.gethostname())
www-0av-Com
źródło
1
Nie działa w systemie macOS:hostname: illegal option -- I\nusage: hostname [-fs] [name-of-host]
Derek 功夫 會 功夫
18

Wersja, której nie wierzę, została jeszcze opublikowana. Testowałem z Pythonem 2.7 na Ubuntu 12.04.

Znaleziono to rozwiązanie na stronie : 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])

Przykład wyniku:

>>> get_ip_address('eth0')
'38.113.228.130'
Graham Chap
źródło
2
Działa na Python3, Ubuntu 18.04; Ciąg musi być bajtami: >>> socket.inet_ntoa (fcntl.ioctl (s.fileno (), 0x8915, struct.pack ('256s', 'enp0s31f6' [: 15] .encode ('utf-8') )) [20:24]) „192.168.1.1”
procesor
12

Wariacja na temat odpowiedzi ninja. Powinno to działać w każdej sieci LAN, która umożliwia transmisję UDP i nie wymaga dostępu do adresu w sieci LAN ani w Internecie.

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())
dlm
źródło
Chwila, jaka jest <broadcast>poprawna nazwa hosta? !! Ile takich słownych nazw hostów jest poprawnych?
Dev Aggarwal
To działa dla mnie na Ubuntu 20.04 - pobieranie 192.168.0.24 nie 127.0.0.1
Lewis Morris
8

Obawiam się, że nie ma innych dobrych sposobów na niezależne od platformy, niż połączenie się z innym komputerem i wysłanie ci adresu IP. Na przykład: findmyipaddress . Pamiętaj, że to nie zadziała, jeśli potrzebujesz adresu IP spoza NAT, chyba że komputer, z którym się łączysz, również ma NAT.

Oto jedno rozwiązanie, które działa w systemie Linux: uzyskaj adres IP powiązany z interfejsem sieciowym .

Jason Baker
źródło
8

Jednym prostym sposobem na uzyskanie „czystego” wyjścia za pomocą narzędzi wiersza poleceń:

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

Wyświetli wszystkie adresy IPv4 w systemie.

viker
źródło
1
Nie wyświetli wszystkich adresów IPv4, ponieważ ifconfig mówi tylko o tych podstawowych. Musisz użyć „ip” z iproute2, aby zobaczyć wszystkie adresy.
Helmut Grohne
To bardzo dużo powłoki dla pytania o standardową bibliotekę… Ponadto parsowanie ifconfig nie jest przenośne i nawet nie działa niezawodnie na jednym komputerze.
Dominik George,
7

Do Twojej wiadomości mogę zweryfikować, czy metoda:

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

Działa w OS X (10.6,10.5), Windows XP i na dobrze administrowanym serwerze działu RHEL. Nie działało na bardzo minimalnej maszynie Wirtualnej CentOS, na której po prostu hakowałem jądro. W tym przypadku możesz po prostu sprawdzić adres 127.0.0.1 i w takim przypadku wykonaj następujące czynności:

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

Następnie przeanalizuj adres IP z wyjścia. Należy zauważyć, że ifconfig domyślnie nie znajduje się w PATH normalnego użytkownika i dlatego podaję pełną ścieżkę w poleceniu. Mam nadzieję, że to pomoże.

gavaletz
źródło
7

Jest to wariant odpowiedzi UnkwnTech - zapewnia get_local_addr()funkcję, która zwraca podstawowy adres IP LAN hosta. Publikuję to, ponieważ dodaje to wiele rzeczy: obsługę ipv6, obsługę błędów, ignorowanie adresatów localhost / linklocal i użycie adresu TESTNET (rfc5737) do połączenia.

# 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
Eli Collins
źródło
Myślałem, że to może być naprawdę dobra odpowiedź ... ale zawsze wracaNone
Jamie Lindsey
@JamieLindsey Czy masz jakieś szczegóły dotyczące swojego systemu operacyjnego, konfiguracji sieci? Ponadto, co get_local_addr(remove="www.google.com")zwraca coś takiego ? Rejestrowanie socket.errorwyrzuconego przez _get_local_addr () może pomóc w diagnostyce.
Eli Collins
6
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
Nakilon
źródło
1
Hmm ... na serwerze z dwiema kartami sieciowymi daje to jeden z przypisanych adresów IP, ale jest powtarzany trzy razy. Na moim laptopie daje „127.0.1.1” (trzykrotnie powtórzone ...) ...
bryn
Daje mi ['fe80::34e8:fe19:1459:2cde%22','fe80::d528:99fb:d572:e289%12', '192.168.56.1', '192.168.1.2']na pulpicie Windows.
Nakilon,
5

Działa to na większości pudełek z Linuksem:

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()
fccoelho
źródło
5

Ta odpowiedź jest moją osobistą próbą rozwiązania problemu uzyskania adresu IP sieci LAN, ponieważ socket.gethostbyname(socket.gethostname())zwrócił również 127.0.0.1. Ta metoda nie wymaga Internetu tylko połączenia LAN. Kod dotyczy Pythona 3.x, ale można go łatwo przekonwertować na wersję 2.x. Korzystanie z emisji 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())
WolfRage
źródło
1
Co się stanie, jeśli uruchomisz to jednocześnie na dwóch komputerach w tej samej sieci? Podczas nadawania wiadomości w sieci wszystkie maszyny otrzymają komunikat „Jaki jest mój adres IP sieci LAN? Twój serwer udp_listening_server może odpowiedzieć „Twój adres IP to xxx” na wiadomość.
Nicolas Defranoux,
4

127.0.1.1 jest twój prawdziwy adres IP. Mówiąc bardziej ogólnie, komputer może mieć dowolną liczbę adresów IP. Możesz je filtrować dla sieci prywatnych - 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12 i 192.168.0.0/16.

Jednak nie ma wieloplatformowego sposobu uzyskania wszystkich adresów IP. W systemie Linux możesz użyć SIOCGIFCONFioctl.

phihag
źródło
2
Ma na myśli jego zewnętrznie widoczne IP. Zakres 127. *. *. * Zazwyczaj odnosi się do hosta lokalnego lub sieci wewnętrznej, co oczywiście nie jest tym, czego chce.
Cerin
4

Nieznaczne udoskonalenie wersji poleceń, która korzysta z polecenia IP i zwraca adresy IPv4 i 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)]
Ben Last
źródło
4

Cóż, możesz użyć polecenia „ip route” w systemie GNU / Linux, aby poznać swój aktualny adres IP.

Pokazuje adres IP nadany interfejsowi przez serwer DHCP działający na routerze / modemie. Zwykle „192.168.1.1/24” to adres IP sieci lokalnej, gdzie „24” oznacza zakres możliwych adresów IP podanych przez serwer DHCP w zakresie maski.

Oto przykład: Zauważ, że PyNotify to tylko dodatek, aby wyprostować mój punkt i wcale nie jest wymagany

#! /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()    

Zaletą tego jest to, że nie trzeba określać interfejsu sieciowego. Jest to bardzo przydatne podczas uruchamiania serwera gniazd

Możesz zainstalować PyNotify za pomocą easy_install lub nawet Pip:

easy_install py-notify

lub

pip install py-notify

lub w skrypcie / interpretatorze Pythona

from pip import main

main(['install', 'py-notify'])
DarkXDroid
źródło
4

Jeśli szukasz adresu IPV4 innego niż adres IP lokalnego hosta 127.0.0.1, oto schludny kawałek kodów python:

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

Które można również zapisać w jednym wierszu:

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

Nawet jeśli umieścić localhostw /etc/hostnamekod będzie nadal dać swój lokalny adres IP.

hmofrad
źródło
4

Dla systemu Linux, można po prostu użyć check_outputz hostname -Ipoleceniem systemowym tak:

from subprocess import check_output
check_output(['hostname', '-I'])
Kasper Skytte Andersen
źródło
dla pracowników Google wiem, że pytanie dotyczyło rozwiązania wieloplatformowego
Kasper Skytte Andersen
3

Uwaga: To nie jest użycie standardowej biblioteki, ale dość proste.

$ pip install pif

from pif import get_public_ip
get_public_ip()
Artur Barseghyan
źródło
3
pytania dotyczyły znalezienia adresu IP za pomocą stdlib
Alexandru Chirila
3

netifaces jest dostępny poprzez pip i easy_install. (Wiem, że nie jest w bazie, ale może być wart instalacji).

netifaces ma pewne dziwactwa na różnych platformach:

  • Interfejs localhost / loop-back nie zawsze może być dołączony (Cygwin).
  • Adresy są wymienione według protokołu (np. IPv4, IPv6), a protokoły są wymienione dla poszczególnych interfejsów. W niektórych systemach (Linux) każda para protokół-interfejs ma swój własny powiązany interfejs (używając notacji nazwa_interfejsu: n), podczas gdy w innych systemach (Windows) pojedynczy interfejs będzie miał listę adresów dla każdego protokołu. W obu przypadkach istnieje lista protokołów, ale może ona zawierać tylko jeden element.

Oto kod netifaces do zabawy:

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.

Powyższy kod nie odwzorowuje adresu z powrotem na nazwę interfejsu (przydatne do generowania reguł ebtables / iptables w locie). Oto wersja, która przechowuje powyższe informacje z nazwą interfejsu w krotce:

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]

I nie, nie jestem zakochana w listach. Tak właśnie działa mój mózg.

Poniższy fragment kodu wydrukuje wszystko:

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)

Cieszyć się!

użytkownik3712955
źródło
netifaces naprawdę ułatwia życie, radząc sobie z tym problemem.
Drake Guan,
3

Wersja Python 3.4 wykorzystująca nowo wprowadzony pakiet 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

Jest to oparte na doskonałej odpowiedzi UnkwnTech .

Frederik Aalund
źródło
3

Aby uzyskać adres IP, możesz użyć polecenia powłoki bezpośrednio w 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()
RiccardoCh
źródło