Recherche d'adresses IP locales à l'aide de stdlib de Python

547

Comment puis-je trouver des adresses IP locales (par exemple 192.168.xx ou 10.0.xx) dans la plate-forme Python indépendamment et en utilisant uniquement la bibliothèque standard?

UnkwnTech
la source
7
L'IP locale? Ou IP publique? Comment allez-vous gérer les systèmes avec plusieurs IP?
Sargun Dhillon
utiliser ifconfig -aet utiliser la sortie à partir de là ...
Fredrik Pihl
16
@Fredrik C'est une mauvaise idée. Tout d'abord, vous bousculez inutilement un nouveau processus, ce qui peut empêcher votre programme de fonctionner dans des configurations étroitement verrouillées (ou, vous devrez accorder des droits dont votre programme n'a pas besoin). Deuxièmement, vous introduirez des bogues pour les utilisateurs de différents paramètres régionaux. Troisièmement, si vous décidez de démarrer un nouveau programme, vous ne devriez pas en lancer un obsolète - ip addrest beaucoup plus approprié (et plus facile à analyser, à démarrer).
phihag
12
@phihag vous avez absolument raison, merci d'avoir corrigé ma stupidité
Fredrik Pihl
1
Un problème plus fondamental ici est que dans un programme de mise en réseau moderne correctement écrit, la ou les bonnes adresses IP locales dépendent du pair ou du groupe de pairs potentiels. Si l'adresse IP locale est nécessaire pour bindun socket vers une interface particulière, c'est une question de politique. Si l'adresse IP locale est nécessaire pour la remettre à un homologue afin que l'homologue puisse "rappeler", c'est-à-dire pour rétablir une connexion avec la machine locale, la situation dépend de l'existence de NAT (Network Address Translation). boîtes entre les deux. S'il n'y a pas de NAT, getsocknamec'est un bon choix.
Pekka Nikander

Réponses:

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

Cela ne fonctionnera pas toujours (retourne 127.0.0.1sur les machines ayant le nom d'hôte en /etc/hoststant que 127.0.0.1), un paliatif serait ce que gimel montre, utilisez à la socket.getfqdn()place. Bien sûr, votre machine a besoin d'un nom d'hôte résoluble.

Vinko Vrsalovic
la source
43
Il convient de noter que ce n'est pas une solution indépendante de la plate-forme. Beaucoup de Linux renverront 127.0.0.1 comme votre adresse IP en utilisant cette méthode.
Jason Baker
20
Une variante: socket.gethostbyname (socket.getfqdn ())
gimel
55
Cela semble ne renvoyer qu'une seule adresse IP. Et si la machine a plusieurs adresses?
Jason R. Coombs
26
Sur Ubuntu, cela renvoie 127.0.1.1 pour une raison quelconque.
slikts
13
@Jason R. Coombs, utilisez le code suivant pour récupérer la liste des adresses IPv4 qui appartiennent à la machine hôte:socket.gethostbyname_ex(socket.gethostname())[-1]
Barmaley
461

Je viens de le trouver, mais cela semble un peu hackish, mais ils disent qu'il a essayé sur * nix et que je l'ai fait sur Windows et cela a fonctionné.

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

Cela suppose que vous disposez d'un accès Internet et qu'il n'y a pas de proxy local.

UnkwnTech
la source
31
Bien si vous avez plusieurs interfaces sur la machine, et que vous avez besoin de celle qui
conduit
4
Ce pourrait être une bonne idée d'attraper les exceptions socket.error qui peuvent être levées par s.connect ()!
phobie
39
Il serait préférable d'utiliser une adresse IP au lieu d'un nom de domaine - elle doit être plus rapide et indépendante de la disponibilité DNS. Par exemple, nous pouvons utiliser 8.8.8.8 IP - le serveur DNS public de Google.
wobmene
10
Très intelligent, fonctionne parfaitement. Au lieu de gmail ou 8.8.8.8, vous pouvez également utiliser l'adresse IP ou l'adresse du serveur à partir duquel vous souhaitez être vu, le cas échéant.
contrat du professeur Falken a été rompu
3
Cet exemple a une dépendance externe de pouvoir réellement résoudre gmail.com. Si vous le définissez sur une adresse IP qui ne se trouve pas sur votre réseau local (peu importe si elle est active ou non), cela fonctionnera sans dépendances et sans trafic réseau.
sinistre
254

Cette méthode renvoie l'IP "primaire" sur la boîte locale (celle avec une route par défaut) .

  • N'a PAS besoin d'un accès net routable ou d'une connexion du tout.
  • Fonctionne même si toutes les interfaces sont débranchées du réseau.
  • N'a PAS besoin ni même essayé d'aller ailleurs .
  • Fonctionne avec NAT, IP publiques, privées, externes et internes
  • Pure Python 2 (ou 3) sans dépendances externes.
  • Fonctionne sur Linux, Windows et OSX.

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

Cela renvoie une seule IP qui est la principale (celle avec une route par défaut). Si vous avez plutôt besoin de toutes les IP attachées à toutes les interfaces (y compris localhost, etc.), consultez cette réponse .

Si vous êtes derrière un pare-feu NAT comme votre box wifi à la maison, cela ne montrera pas votre IP NAT publique, mais plutôt votre IP privée sur le réseau local qui a une route par défaut vers votre routeur WIFI local; obtenir l'IP externe de votre routeur wifi nécessiterait soit de l'exécuter sur CETTE boîte, soit de se connecter à un service externe tel que whatismyip.com/whatismyipaddress.com qui pourrait refléter l'IP ... mais cela est complètement différent de la question d'origine. :)

erreur fatale
la source
7
Fonctionne en Raspbian avec Python 2 et 3!
pierce.jason
3
Brillant. Fonctionne sur Win7,8,8.1 + Linux Mint & Arch, y compris les VM.
shermy
2
Fonctionne dans Windows 10 Pro! Merci, Jamieson Becker!
varantes
3
Pour une raison quelconque, cela ne fonctionne pas sur Mac OS X El Capitan 10.11.6 (cela génère une erreur de système d'exploitation d'exception: [Errno 49] Impossible d'attribuer l'adresse demandée). Changer le port de '0' à '1': s.connect (('10.255.255.255', 1)) a fonctionné pour moi à la fois sur Mac OS X et Linux Ubuntu 17.04
Pedro Scarapicchia Junior
10
Ce devrait être la réponse acceptée. socket.gethostbyname(socket.gethostname())donne des résultats horribles.
Jason Floyd
142

Comme un alias s'appelle myip, cela devrait fonctionner partout:

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])'"
  • Fonctionne correctement avec Python 2.x, Python 3.x, les distributions Linux modernes et anciennes, OSX / macOS et Windows pour trouver l'adresse IPv4 actuelle.
  • Ne retournera pas le résultat correct pour les machines avec plusieurs adresses IP, IPv6, aucune adresse IP configurée ou aucun accès Internet.

Comme ci-dessus, mais uniquement le code 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])
  • Cela lèvera une exception si aucune adresse IP n'est configurée.

Version qui fonctionnera également sur les réseaux locaux sans connexion Internet:

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

(merci @ccpizza )


Contexte :

L'utilisation socket.gethostbyname(socket.gethostname())n'a pas fonctionné ici, car l'un des ordinateurs sur lesquels je me trouvais avait une /etc/hostsentrée en double et des références à lui-même. socket.gethostbyname()renvoie uniquement la dernière entrée /etc/hosts.

C'était ma première tentative, qui élimine toutes les adresses en commençant par "127.":

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

Cela fonctionne avec Python 2 et 3, sous Linux et Windows, mais ne concerne pas plusieurs périphériques réseau ou IPv6. Cependant, il a cessé de fonctionner sur les distributions récentes de Linux, j'ai donc plutôt essayé cette technique alternative. Il essaie de se connecter au serveur DNS de Google 8.8.8.8sur le port53 :

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

Ensuite, j'ai combiné les deux techniques ci-dessus en une seule doublure qui devrait fonctionner partout, et j'ai créé le myip alias et l'extrait de code Python en haut de cette réponse.

Avec la popularité croissante d'IPv6 et pour les serveurs avec plusieurs interfaces réseau, l'utilisation d'un module Python tiers pour trouver l'adresse IP est probablement à la fois plus robuste et fiable que l'une des méthodes répertoriées ici.

Alexandre
la source
2
@Alexander: Je dis simplement que cette réponse est beaucoup moins utile qu'elle ne l'était (et ce n'est pas comme filtrer les doublons, c'est une grosse affaire;). Selon la documentation, cela socket.getaddrinfo()devrait fonctionner de manière cohérente sur toutes les plateformes - mais je ne l'ai vérifié que sur Linux, je ne me suis pas soucié des autres systèmes d'exploitation.
Wladimir Palant
1
@Alexander, /etc/resolve.conf: No such file or directoryet j'ai l'adresse IPv4 locale indiquée par ifconfig.
anatoly techtonik
2
Je peux confirmer que la version mise à jour fonctionne avec Ubuntu 14.04 avec Python2 et Py3k.
Uli Köhler
4
La "mise à jour" montre une bonne astuce avec connect () sur un socket UDP. Il n'envoie aucun trafic mais vous permet de trouver quelle serait l'adresse de l'expéditeur pour les paquets au destinataire spécifié. Le port n'est probablement pas pertinent (même 0 devrait fonctionner). Sur un hôte multirésident, il est important de choisir une adresse dans le bon sous-réseau.
Peter Hansen
17
juste parce que vous pouvez écrire ce code sur une seule ligne, cela ne signifie pas que vous devriez ...
JackLeo
91

Vous pouvez utiliser le module netifaces . Tapez simplement:

pip install netifaces

dans votre shell de commande et il s'installera sur l'installation Python par défaut.

Ensuite, vous pouvez l'utiliser comme ceci:

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

Sur mon ordinateur, il a imprimé:

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

L'auteur de ce module affirme qu'il devrait fonctionner sous Windows, UNIX et Mac OS X.

DzinX
la source
20
Comme indiqué dans la question, je veux quelque chose de l'installation par défaut, car aucune installation supplémentaire n'est nécessaire.
UnkwnTech
4
Ce serait ma réponse préférée, sauf que netifaces ne prend pas en charge IPv6 sous Windows et semble non entretenu. Quelqu'un a-t-il compris comment obtenir des adresses IPv6 sous Windows?
Jean-Paul Calderone
3
netifaces ne prend pas en charge py3k et nécessite un compilateur C qui est un PITA sous Windows.
Matt Joiner
4
@MattJoiner Aucune de ces choses n'est plus vraie (la dernière version a des binaires Windows sur PyPI et prend en charge Py3K).
alastair
4
@ Jean-PaulCalderone FWIW, la dernière version de netifaces prend en charge IPv6 sous Windows.
alastair
47

Méthode API Socket

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

Inconvénients:

  • Pas multiplateforme.
  • Nécessite plus de code de secours, lié à l'existence d'adresses particulières sur Internet
  • Cela ne fonctionnera pas non plus si vous êtes derrière un NAT
  • Crée probablement une connexion UDP, non indépendante de la disponibilité DNS (généralement du FAI) (voir d'autres réponses pour des idées comme l'utilisation de 8.8.8.8: le serveur de Google (par coïncidence aussi DNS))
  • Assurez-vous que l'adresse de destination est inaccessible, comme une adresse IP numérique dont la spécification est garantie comme inutilisée. N'utilisez PAS un domaine comme fakesubdomain.google.com ou somefakewebsite.com; vous spammerez toujours cette partie (maintenant ou dans le futur) et spammerez également vos propres boîtiers réseau au cours du processus.

Méthode du réflecteur

(Notez que cela ne répond pas à la question de l'OP concernant l'adresse IP locale, par exemple 192.168 ...; cela vous donne votre adresse IP publique, ce qui pourrait être plus souhaitable selon le cas d'utilisation.)

Vous pouvez interroger certains sites comme whatismyip.com (mais avec une API), tels que:

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)

ou si vous utilisez 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)

Avantages:

  • Un avantage de cette méthode est son multiplateforme
  • Il fonctionne à partir de NAT laids (par exemple votre routeur domestique).

Inconvénients (et solutions):

  • Nécessite que ce site Web soit en place, que le format ne change pas (presque certainement pas) et que vos serveurs DNS fonctionnent. On peut atténuer ce problème en interrogeant également d'autres réflecteurs d'adresses IP tiers en cas d'échec.
  • Vecteur d'attaque possible si vous n'interrogez pas plusieurs réflecteurs (pour empêcher un réflecteur compromis de vous dire que votre adresse est quelque chose qui ne l'est pas), ou si vous n'utilisez pas HTTPS (pour empêcher une attaque de l'homme du milieu de faire semblant être le serveur)

edit : Bien qu'au départ, je pensais que ces méthodes étaient vraiment mauvaises (à moins que vous n'utilisiez de nombreuses solutions de rechange, le code pourrait ne plus être pertinent dans plusieurs années), cela pose la question "qu'est-ce qu'Internet?". Un ordinateur peut avoir de nombreuses interfaces pointant vers de nombreux réseaux différents. Pour une description plus approfondie du sujet, google forgateways and routes. Un ordinateur peut être en mesure d'accéder à un réseau interne via une passerelle interne, ou d'accéder au Web mondial via une passerelle sur par exemple un routeur (généralement le cas). L'adresse IP locale que l'OP demande n'est bien définie que par rapport à une seule couche de liaison, vous devez donc spécifier cela ("est-ce la carte réseau ou le câble Ethernet dont nous parlons?") . Il peut y avoir plusieurs réponses non uniques à cette question telle que posée. Cependant, l'adresse IP globale sur le World Wide Web est probablement bien définie (en l'absence de fragmentation massive du réseau): probablement le chemin de retour via la passerelle qui peut accéder aux TLD.

ninjagecko
la source
Cela renverra votre adresse LAN si vous êtes derrière un NAT. Si vous vous connectez à Internet, vous pouvez vous connecter à un service Web qui renvoie l'une de vos adresses IP publiques.
phihag
Il ne crée pas de connexion TCP car il crée une connexion UDP.
Anuj Gupta
2
Comme alternative dans la version API du socket, remplacez s.connect (('INSERT SOME TARGET WEBSITE.com', 0)) par s.setsockopt (socket.SOL_SOCKET, socket.SO_BROADCAST, 1); s.connect (('' < broadcast> ', 0)) pour éviter la recherche DNS. (Je suppose qu'il pourrait y avoir un problème avec une diffusion s'il y a un pare-feu)
dlm
45

Si l'ordinateur dispose d'une route vers Internet, cela fonctionnera toujours pour obtenir l'adresse IP locale préférée, même si / etc / hosts n'est pas défini correctement.

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
la source
Comment cela marche-t-il ? , 8.8.8.8est-ce qu'un serveur google dns peut-on le faire avec un serveur dns local?
Ciasto piekarz
39

Sous 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
la source
Donc, cela ouvre effectivement un socket avec lequel il ne fait rien et vous vérifiez les données brutes sur ce socket pour obtenir l'adresse IP locale?
Dave
1
Le socket est ouvert pour qu'un fd communique avec le noyau (via ioctl). Le socket n'est pas lié à l'interface pour laquelle vous voulez des informations sur l'adr - c'est juste un mécanisme de communication entre l'espace utilisateur et le noyau. en.wikipedia.org/wiki/Ioctl lxr.free-electrons.com/source/net/socket.c
tMC
2
Fonctionne sur Python3 avec une modification: struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)devrait être remplacé parstruct.pack('16sH14s', iface.encode('utf-8'), socket.AF_INET, b'\x00'*14)
pepoluan
1
@ChristianFischer ioctlest une interface héritée, je ne crois pas, prend en charge IPv6 et ne le sera probablement jamais. Je pense que la bonne façon est via Netlink qui n'est pas très simple en Python. Je pense que libc devrait avoir la fonction getifaddrsaccessible via le ctypesmodule pythons qui peut fonctionner - man7.org/linux/man-pages/man3/getifaddrs.3.html
tMC
1
@Maddy ioctl est une interface héritée, je ne crois pas, prend en charge IPv6 et ne le sera probablement jamais. Je pense que la bonne façon est via Netlink qui n'est pas très simple en Python. Je pense que libc devrait avoir la fonction getifaddrs accessible via le module pythons ctypes qui peut fonctionner - man7.org/linux/man-pages/man3/getifaddrs.3.html
tMC
27

im en utilisant le module suivant:

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

Testé avec Windows et Linux (et ne nécessite pas de modules supplémentaires pour ceux-ci) destiné à être utilisé sur des systèmes qui sont dans un seul LAN basé sur IPv4.

La liste fixe des noms d'interface ne fonctionne pas pour les versions récentes de Linux, qui ont adopté la modification systemd v197 concernant les noms d'interface prévisibles, comme l'a souligné Alexander . Dans de tels cas, vous devez remplacer manuellement la liste par les noms d'interface sur votre système ou utiliser une autre solution comme netifaces .

smerlin
la source
2
Ceci est incompatible avec les nouveaux noms d'interface Linux prévisibles, tels que enp0s25. Pour plus d'informations, voir wiki.archlinux.org/index.php/Network_Configuration#Device_names
Alexander
2
J'utilisais python 3.4 et la partie 'struct.pack (...)' devait être changée en 'struct.pack (' 256s ', octets (ifname [: 15],' utf-8 '))'. Voir cette question: stackoverflow.com/q/27391167/76010
Bakanekobrain
1
sur Raspbian avec Python 2.7.3 - bytes () n'aimait pas le 2ème argument. Mais cela a fonctionné: struct.pack('256s', bytes(ifname[:15]))
colm.anseo
24

J'utilise ceci sur mes machines Ubuntu:

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

Ça ne marche pas.

shino
la source
Agréable et simple. Fonctionne également sur l'AMI Linux d'Amazon, mais uniquement si je suis root. Sinon, j'obtiendrais une erreur: «sh: ifconfig: commande introuvable»
IgorGanapolsky
Vous devez donc utiliser "/ sbin / ifconfig" comme l'a dit gavaletz. Il fonctionne également sur Red Hat 4.1.2-48.
IgorGanapolsky
7
Déconseillé depuis 2.6. Utilisez le module de sous - processus pour exécuter des commandes.
Colin Dunklau
5
Et ifconfig est également obsolète. Utilisez iproute2.
Helmut Grohne
Obtenez toutes les ips: import sh; [ip.split () [1] [5:] pour ip in filter (lambda x: 'inet addr' in x, sh.ifconfig (). split ("\ n"))]
Gabriel Littman
20

Si vous ne souhaitez pas utiliser de packages externes et ne comptez pas sur des serveurs Internet externes, cela peut vous aider. Il s'agit d'un exemple de code que j'ai trouvé sur Google Code Search et modifié pour renvoyer les informations requises:

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

Usage:

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

Comme il dépend windll, cela ne fonctionnera que sous Windows.

DzinX
la source
La solution à un revêtement ci-dessus fonctionne généralement sur les fenêtres. C'est Linux qui pose problème.
ricree
14
+1 Cette technique tente au moins de renvoyer toutes les adresses sur la machine.
Jason R. Coombs
1
Ce script échoue sur ma machine après avoir renvoyé la première adresse. L'erreur est "AttributeError: l'objet 'LP_IP_ADDR_STRING' n'a pas d'attribut 'ipAddress'" Je soupçonne qu'il a quelque chose à voir avec l'adresse IPv6.
Jason R. Coombs
1
Il s'avère que le problème est que pour tout sauf la première adresse IP, l'adNode n'est pas déréférencé. Ajoutez une ligne supplémentaire à l'exemple dans la boucle while et cela fonctionne pour moi: adNode = adNode.contents
Jason R. Coombs
18

Sur Debian (testé) et je soupçonne la plupart des Linux ..

import commands

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

Sur MS Windows (testé)

import socket

socket.gethostbyname(socket.gethostname())
www-0av-Com
la source
1
Ne fonctionne pas sur macOS:hostname: illegal option -- I\nusage: hostname [-fs] [name-of-host]
Derek 朕 會 功夫
18

Une version que je ne crois pas encore publiée. J'ai testé avec python 2.7 sur Ubuntu 12.04.

Trouvé cette solution à: 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])

Exemple de résultat:

>>> get_ip_address('eth0')
'38.113.228.130'
Graham Chap
la source
2
Fonctionne sur Python3, Ubuntu 18.04; La chaîne doit être en octets: >>> socket.inet_ntoa (fcntl.ioctl (s.fileno (), 0x8915, struct.pack ('256s', 'enp0s31f6' [: 15] .encode ('utf-8')) )) [20:24]) '192.168.1.1'
cessor
12

Variation sur la réponse de ninjagecko. Cela devrait fonctionner sur n'importe quel LAN qui autorise la diffusion UDP et ne nécessite pas l'accès à une adresse sur le LAN ou Internet.

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
la source
Attendez, comment est <broadcast>un nom d'hôte valide? !! Combien de ces types de noms d'hôtes verbaux sont valides?
Dev Aggarwal
Cela fonctionne pour moi sur Ubuntu 20.04 - obtenant 192.168.0.24 et non 127.0.0.1
Lewis Morris
8

Je crains qu'il n'y ait pas de bons moyens indépendants de la plate-forme pour ce faire, à part la connexion à un autre ordinateur et le fait de vous envoyer votre adresse IP. Par exemple: findmyipaddress . Notez que cela ne fonctionnera pas si vous avez besoin d'une adresse IP derrière NAT, à moins que l'ordinateur auquel vous vous connectez ne soit également derrière NAT.

Voici une solution qui fonctionne sous Linux: obtenez l'adresse IP associée à une interface réseau .

Jason Baker
la source
8

Une façon simple de produire une sortie "propre" via des utilitaires de ligne de commande:

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

Il affichera toutes les adresses IPv4 du système.

viker
la source
1
Il n'affichera pas toutes les adresses IPv4, car ifconfig ne vous indique que les adresses principales. Vous devez utiliser "ip" d'iproute2 pour voir toutes les adresses.
Helmut Grohne
C'est beaucoup de shell pour une question demandant la bibliothèque standard… De plus, l'analyse ifconfig n'est ni portable ni même fiable sur une seule machine.
Dominik George
7

Pour info je peux vérifier que la méthode:

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

Fonctionne sous OS X (10.6,10.5), Windows XP et sur un serveur de département RHEL bien administré. Cela n'a pas fonctionné sur une machine virtuelle CentOS très minimale sur laquelle je fais juste un piratage du noyau. Donc, pour cette instance, vous pouvez simplement vérifier une adresse 127.0.0.1 et dans ce cas, procédez comme suit:

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

Et puis analysez l'adresse IP de la sortie. Il convient de noter que ifconfig n'est pas dans le PATH d'un utilisateur normal par défaut et c'est pourquoi je donne le chemin complet dans la commande. J'espère que ça aide.

gavaletz
la source
7

Il s'agit d'une variante de la réponse d'UnkwnTech - elle fournit une get_local_addr()fonction, qui renvoie l'adresse IP LAN principale de l'hôte. Je le poste car cela ajoute un certain nombre de choses: prise en charge d'ipv6, gestion des erreurs, ignorant les adresses localeshost / linklocal et utilise un addr TESTNET (rfc5737) pour se connecter.

# 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
la source
Je pensais que cela aurait pu être une très bonne réponse .. mais ça revient toujoursNone
Jamie Lindsey
@JamieLindsey Avez-vous des détails sur votre système d'exploitation, la configuration réseau? Aussi, qu'est-ce que quelque chose comme le get_local_addr(remove="www.google.com")retour? La journalisation de la socket.errorlevée par _get_local_addr () peut aider au diagnostic.
Eli Collins
6
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
Nakilon
la source
1
Hmm ... sur un serveur avec deux cartes réseau, cela donne l' une des adresses IP attribuées, mais répétée trois fois. Sur mon ordinateur portable, il donne '127.0.1.1' (répété trois fois ...) ...
bryn
Me donne ['fe80::34e8:fe19:1459:2cde%22','fe80::d528:99fb:d572:e289%12', '192.168.56.1', '192.168.1.2']sur le bureau Windows.
Nakilon
5

Cela fonctionnera sur la plupart des boîtiers 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()
fccoelho
la source
5

Cette réponse est ma tentative personnelle de résoudre le problème d'obtention de l'IP LAN, car elle a socket.gethostbyname(socket.gethostname())également renvoyé 127.0.0.1. Cette méthode ne nécessite pas Internet uniquement une connexion LAN. Le code est pour Python 3.x mais pourrait facilement être converti pour 2.x. Utilisation de la diffusion 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
la source
1
Que se passe-t-il si vous l'exécutez simultanément sur deux machines sur le même réseau? Lorsque vous diffusez votre message sur le réseau, toutes les machines reçoivent «Quelle est mon adresse IP LAN». Votre udp_listening_server pourrait répondre «votre adresse IP est xxx» au message.
Nicolas Defranoux
4

127.0.1.1 est votre véritable adresse IP. Plus généralement, un ordinateur peut avoir un nombre quelconque d'adresses IP. Vous pouvez les filtrer pour les réseaux privés - 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12 et 192.168.0.0/16.

Cependant, il n'existe aucun moyen multiplateforme pour obtenir toutes les adresses IP. Sous Linux, vous pouvez utiliser l' SIOCGIFCONFioctl.

phihag
la source
2
Il signifie son IP visible de l'extérieur. La plage 127. *. *. * Fait généralement référence à un hôte local ou à un réseau interne, ce qui n'est clairement pas ce qu'il souhaite.
Cerin
4

Un léger raffinement de la version des commandes qui utilise la commande IP et renvoie les adresses IPv4 et 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
la source
4

Eh bien, vous pouvez utiliser la commande "ip route" sur GNU / Linux pour connaître votre adresse IP actuelle.

Cela montre l'IP donnée à l'interface par le serveur DHCP exécuté sur le routeur / modem. Habituellement, "192.168.1.1/24" est l'IP du réseau local où "24" signifie la plage d'adresses IP possibles donnée par le serveur DHCP dans la plage de masques.

Voici un exemple: Notez que PyNotify est juste un ajout pour clarifier mon point et n'est pas du tout requis

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

L'avantage est que vous n'avez pas besoin de spécifier l'interface réseau. C'est assez utile lors de l'exécution d'un serveur de socket

Vous pouvez installer PyNotify en utilisant easy_install ou même Pip:

easy_install py-notify

ou

pip install py-notify

ou dans un script / interprète python

from pip import main

main(['install', 'py-notify'])
DarkXDroid
la source
4

Si vous recherchez une adresse IPV4 différente de votre adresse IP localhost 127.0.0.1, voici un morceau de codes python:

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

Qui peut également s'écrire sur une seule ligne:

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

Même si vous mettez localhostdans /etc/hostname, le code sera toujours votre adresse IP locale.

hmofrad
la source
4

Pour linux, vous pouvez simplement utiliser check_outputla hostname -Icommande système comme ceci:

from subprocess import check_output
check_output(['hostname', '-I'])
Kasper Skytte Andersen
la source
pour les googleurs, je sais que la question était pour une solution multiplateforme
Kasper Skytte Andersen
3

Remarque: cela n'utilise pas la bibliothèque standard, mais assez simple.

$ pip install pif

from pif import get_public_ip
get_public_ip()
Artur Barseghyan
la source
3
la question était de trouver l'IP en utilisant stdlib
Alexandru Chirila
3

netifaces est disponible via pip et easy_install. (Je sais, ce n'est pas dans la base, mais cela pourrait valoir la peine d'être installé.)

netifaces a quelques bizarreries sur toutes les plateformes:

  • L'interface localhost / loop-back peut ne pas toujours être incluse (Cygwin).
  • Les adresses sont répertoriées par protocole (par exemple, IPv4, IPv6) et les protocoles sont répertoriés par interface. Sur certains systèmes (Linux), chaque paire protocole-interface a sa propre interface associée (en utilisant la notation nom_interface: n) tandis que sur d'autres systèmes (Windows), une seule interface aura une liste d'adresses pour chaque protocole. Dans les deux cas, il existe une liste de protocoles, mais elle ne peut contenir qu'un seul élément.

Voici quelques codes Netifaces pour jouer avec:

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.

Le code ci-dessus ne mappe pas une adresse à son nom d'interface (utile pour générer des règles ebtables / iptables à la volée). Voici donc une version qui conserve les informations ci-dessus avec le nom de l'interface dans un tuple:

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]

Et, non, je ne suis pas amoureux de la compréhension des listes. C'est juste la façon dont mon cerveau fonctionne de nos jours.

L'extrait suivant l'imprimera entièrement:

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)

Prendre plaisir!

user3712955
la source
netifaces facilite vraiment la vie en traitant ce problème.
Drake Guan
3

Une version Python 3.4 utilisant le package asyncio nouvellement introduit.

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

Ceci est basé sur l' excellente réponse d' UnkwnTech .

Frederik Aalund
la source
3

Pour obtenir l'adresse IP, vous pouvez utiliser une commande shell directement en 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
la source