Encontrar direcciones IP locales usando stdlib de Python

547

¿Cómo puedo encontrar direcciones IP locales (es decir, 192.168.xx o 10.0.xx) en la plataforma Python de forma independiente y usando solo la biblioteca estándar?

UnkwnTech
fuente
77
La IP local? O IP pública? ¿Cómo vas a lidiar con los sistemas con múltiples IP?
Sargun Dhillon
usar ifconfig -ay usar la salida de allí ...
Fredrik Pihl
16
@Fredrik Esa es una mala idea. En primer lugar, está bifurcando innecesariamente un nuevo proceso, y eso puede evitar que su programa funcione en configuraciones estrechamente bloqueadas (o tendrá que permitir derechos que su programa no necesita). En segundo lugar, introducirá errores para usuarios de diferentes configuraciones regionales. En tercer lugar, si decide iniciar un nuevo programa, no debería iniciar uno en desuso, ip addres mucho más adecuado (y más fácil de analizar, arrancar).
phihag
12
@phihag tienes toda la razón, gracias por corregir mi estupidez
Fredrik Pihl
1
Un problema más fundamental aquí es que en un programa de red moderno escrito correctamente, la (s) dirección (es) local (es) correcta (s) de IP depende del par o del conjunto de pares potenciales. Si se necesita la dirección IP local para bindun socket a una interfaz particular, entonces es una cuestión de política. Si se necesita la dirección IP local para entregarla a un par para que el par pueda "devolver la llamada", es decir, para abrir una conexión de nuevo a la máquina local, entonces la situación depende de si hay alguna NAT (traducción de dirección de red) cajas en el medio. Si no hay NAT, getsocknamees una buena opción.
Pekka Nikander

Respuestas:

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

Esto no funcionará siempre (regresa 127.0.0.1en máquinas que tienen el nombre de host /etc/hostscomo 127.0.0.1), un paliativo sería lo que muestra gimel, use socket.getfqdn()en su lugar. Por supuesto, su máquina necesita un nombre de host resoluble.

Vinko Vrsalovic
fuente
43
Cabe señalar que esta no es una solución independiente de la plataforma. Muchos Linux devolverán 127.0.0.1 como su dirección IP utilizando este método.
Jason Baker,
20
Una variación: socket.gethostbyname (socket.getfqdn ())
gimel
55
Esto parece devolver solo una única dirección IP. ¿Qué pasa si la máquina tiene varias direcciones?
Jason R. Coombs
26
En Ubuntu, esto devuelve 127.0.1.1 por alguna razón.
slikts
13
@Jason R. Coombs, use el siguiente código para recuperar la lista de direcciones IPv4 que pertenecen a la máquina host:socket.gethostbyname_ex(socket.gethostname())[-1]
Barmaley
460

Acabo de encontrar esto, pero parece un poco difícil, sin embargo, dicen que lo probé en * nix y lo hice en Windows y funcionó.

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

Esto supone que tiene acceso a Internet y que no hay un proxy local.

UnkwnTech
fuente
31
Bueno si usted tiene varias interfaces de la máquina, y necesita el que rutas de ejemplo gmail.com
elzapp
44
¡Podría ser una buena idea detectar excepciones de socket.error que s.connect () puede aumentar!
phobie
39
Sería mejor usar una dirección IP en lugar de un nombre de dominio; debe ser más rápido e independiente de la disponibilidad de DNS. Por ejemplo, podemos usar 8.8.8.8 IP: el servidor DNS público de Google.
wobmene
10
Muy inteligente, funciona perfectamente. En lugar de gmail o 8.8.8.8, también puede usar la dirección IP o la dirección del servidor desde el que desea que lo vean, si corresponde.
contrato del Prof. Falken incumplió el
3
Este ejemplo tiene una dependencia externa de poder resolver realmente gmail.com. Si lo configura en una dirección IP que no está en su LAN local (no importa si está activo o inactivo) funcionará sin dependencias y sin tráfico de red.
sombrío
254

Este método devuelve la IP "primaria" en el cuadro local (el que tiene una ruta predeterminada) .

  • NO necesita acceso a la red enrutable ni ninguna conexión.
  • Funciona incluso si todas las interfaces están desconectadas de la red.
  • NO necesita ni trata de llegar a ningún otro lado .
  • Funciona con NAT, IP públicas, privadas, externas e internas.
  • Python puro 2 (o 3) sin dependencias externas.
  • Funciona en Linux, Windows y OSX.

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

Esto devuelve una única IP que es la principal (la que tiene una ruta predeterminada). Si necesita en su lugar todas las IP conectadas a todas las interfaces (incluido localhost, etc.), consulte esta respuesta .

Si está detrás de un cortafuegos NAT como su caja wifi en casa, entonces esto no mostrará su IP NAT pública, sino su IP privada en la red local que tiene una ruta predeterminada a su enrutador WIFI local; obtener la IP externa de su enrutador wifi requeriría ejecutar esto en ESA caja, o conectarse a un servicio externo como whatismyip.com/whatismyipaddress.com que podría reflejar la IP ... pero eso es completamente diferente de la pregunta original. :)

error fatal
fuente
77
¡Funciona en Raspbian con Python 2 y 3!
pierce.jason
3
Brillante. Funciona en Win7,8,8.1 + Linux Mint & Arch, incluidas las máquinas virtuales.
Shermy
2
Funciona en Windows 10 Pro! ¡Gracias Jamieson Becker!
varantes
3
Por alguna razón, esto no funciona en Mac OS X El Capitan 10.11.6 (genera un error de excepción del sistema operativo: [Errno 49] No se puede asignar la dirección solicitada). Cambiar el puerto de '0' a '1': s.connect (('10.255.255.255', 1)) funcionó para mí en Mac OS X y Linux Ubuntu 17.04
Pedro Scarapicchia Junior
10
Esta debería ser la respuesta aceptada. socket.gethostbyname(socket.gethostname())da resultados horribles
Jason Floyd el
142

Como se llama un alias myip, eso debería funcionar en todas partes:

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])'"
  • Funciona correctamente con Python 2.x, Python 3.x, distribuciones de Linux modernas y antiguas, OSX / macOS y Windows para encontrar la dirección IPv4 actual.
  • No devolverá el resultado correcto para máquinas con múltiples direcciones IP, IPv6, sin dirección IP configurada o sin acceso a Internet.

Igual que el anterior, pero solo el código 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])
  • Esto generará una excepción si no hay una dirección IP configurada.

Versión que también funcionará en LAN sin conexión a 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])

(gracias @ccpizza )


Antecedentes :

El uso socket.gethostbyname(socket.gethostname())no funcionó aquí, porque una de las computadoras en las que estaba tenía /etc/hostsentradas duplicadas y referencias a sí misma. socket.gethostbyname()solo devuelve la última entrada en /etc/hosts.

Este fue mi intento inicial, que elimina todas las direcciones que comienzan con "127.":

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

Esto funciona con Python 2 y 3, en Linux y Windows, pero no se ocupa de varios dispositivos de red o IPv6. Sin embargo, dejó de funcionar en distribuciones recientes de Linux, así que probé esta técnica alternativa. Intenta conectarse al servidor DNS de Google 8.8.8.8en el puerto 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])

Luego combiné las dos técnicas anteriores en una línea que debería funcionar en todas partes, y creé el myipalias y el fragmento de Python en la parte superior de esta respuesta.

Con la creciente popularidad de IPv6, y para servidores con múltiples interfaces de red, el uso de un módulo Python de terceros para encontrar la dirección IP es probablemente más robusto y confiable que cualquiera de los métodos enumerados aquí.

Alejandro
fuente
2
@Alexander: Solo decir que esta respuesta es mucho menos útil de lo que solía ser (y no es como filtrar duplicados es un gran problema;). Según la documentación, socket.getaddrinfo()debería funcionar de manera consistente en todas las plataformas, pero solo lo comprobé en Linux, no me molesté en ningún otro sistema operativo.
Wladimir Palant
1
@Alexander, /etc/resolve.conf: No such file or directoryy tengo la dirección IPv4 local mostrada por ifconfig.
anatoly techtonik
2
Puedo confirmar que la versión actualizada funciona con Ubuntu 14.04 con Python2 y Py3k.
Uli Köhler
44
La "actualización" muestra un buen truco con connect () en un socket UDP. No envía tráfico, pero le permite encontrar cuál sería la dirección del remitente de los paquetes al destinatario especificado. Es probable que el puerto sea irrelevante (incluso 0 debería funcionar). En un host de alojamiento múltiple, es importante elegir una dirección en la subred correcta.
Peter Hansen
17
solo porque puedes escribir ese código en una sola línea, no significa que
debas
91

Puede usar el módulo netifaces . Sólo tipo:

pip install netifaces

en su shell de comandos y se instalará en la instalación predeterminada de Python.

Entonces puedes usarlo así:

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

En mi computadora imprimió:

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

El autor de este módulo afirma que debería funcionar en Windows, UNIX y Mac OS X.

DzinX
fuente
20
Como se indicó en la pregunta, quiero algo de la instalación predeterminada, ya que no se necesitan instalaciones adicionales.
UnkwnTech
44
Esta sería mi respuesta favorita, excepto que netifaces no es compatible con IPv6 en Windows y aparece sin mantenimiento. ¿Alguien ha descubierto cómo obtener direcciones IPv6 en Windows?
Jean-Paul Calderone
3
netifaces no es compatible con py3k, y requiere un compilador de C que es un PITA en Windows.
Matt Joiner
44
@MattJoiner Ninguna de estas cosas es cierta (la última versión tiene binarios de Windows en PyPI y admite Py3K).
alastair
44
@ Jean-PaulCalderone Fwiw, la última versión del netifaces hace el soporte de IPv6 en Windows.
alastair
47

Método de API de socket

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

Desventajas:

  • No multiplataforma.
  • Requiere más código alternativo, vinculado a la existencia de direcciones particulares en Internet
  • Esto tampoco funcionará si estás detrás de un NAT
  • Probablemente crea una conexión UDP, no independiente de la disponibilidad de DNS (generalmente del ISP) (vea otras respuestas para ideas como usar 8.8.8.8: el servidor de Google (casualmente también DNS))
  • Asegúrese de que la dirección de destino NO SE PUEDE ALCANZAR, como una dirección IP numérica con garantía de especificación que no se utilizará. NO use algún dominio como fakesubdomain.google.com o somefakewebsite.com; seguirás enviando spam a esa parte (ahora o en el futuro) y enviando spam a tus propias cajas de red también en el proceso.

Método de reflector

(Tenga en cuenta que esto no responde a la pregunta del OP sobre la dirección IP local, por ejemplo, 192.168 ...; le da su dirección IP pública, que podría ser más deseable dependiendo del caso de uso).

Puede consultar algún sitio como whatismyip.com (pero con una API), como:

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)

o si usa 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)

Ventajas:

  • Una ventaja de este método es que es multiplataforma
  • Funciona desde NAT desagradables (por ejemplo, el enrutador de su casa).

Desventajas (y soluciones):

  • Requiere que este sitio web esté activo, que el formato no cambie (casi seguro que no) y que sus servidores DNS funcionen. Uno puede mitigar este problema al consultar también otros reflectores de direcciones IP de terceros en caso de falla.
  • Posible vector de ataque si no consulta múltiples reflectores (para evitar que un reflector comprometido le diga que su dirección es algo que no es), o si no utiliza HTTPS (para evitar un simulacro de ataque de hombre en el medio ser el servidor)

editar : Aunque inicialmente pensé que estos métodos eran realmente malos (a menos que use muchos fallos, el código puede ser irrelevante dentro de muchos años), plantea la pregunta "¿qué es Internet?". Una computadora puede tener muchas interfaces que apuntan a muchas redes diferentes. Para una descripción más completa del tema, google forgateways and routes. Una computadora puede acceder a una red interna a través de una puerta de enlace interna, o acceder a la red mundial a través de una puerta de enlace en, por ejemplo, un enrutador (generalmente el caso). La dirección IP local sobre la que pregunta el OP solo está bien definida con respecto a una sola capa de enlace, por lo que debe especificar eso ("¿es de la tarjeta de red o del cable de Ethernet, de lo que estamos hablando?") . Puede haber múltiples respuestas no únicas a esta pregunta como se plantea. Sin embargo, la dirección IP global en la red mundial probablemente esté bien definida (en ausencia de una fragmentación masiva de la red): probablemente la ruta de retorno a través de la puerta de enlace que puede acceder a los TLD.

ninjagecko
fuente
Esto devolverá su dirección de LAN si está detrás de un NAT. Si se está conectando a Internet, puede conectarse a un servicio web que devuelve una de sus direcciones IP públicas.
phihag
No crea una conexión TCP porque crea una conexión UDP.
Anuj Gupta
2
Como alternativa en la versión de API de socket, reemplace s.connect (('INSERTAR ALGUNO SITIO WEB OBJETIVO.com', 0)) con s.setsockopt (socket.SOL_SOCKET, socket.SO_BROADCAST, 1); s.connect (('< broadcast> ', 0)) para evitar la búsqueda de DNS. (Supongo que podría haber un problema con una transmisión si hay un firewall)
dlm
45

Si la computadora tiene una ruta a Internet, esto siempre funcionará para obtener la dirección IP local preferida, incluso si / etc / hosts no está configurado correctamente.

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
fuente
como funciona esto ? , 8.8.8.8es un servidor dns de google ¿podemos hacerlo con un servidor dns local?
Ciasto piekarz
39

En 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
fuente
Entonces, ¿esto efectivamente abre un socket con el que no hace nada y verifica los datos sin procesar sobre ese socket para obtener la IP local?
Dave
1
El socket se abre para que un fd se comunique con el kernel (a través de ioctl). El socket no está vinculado a la interfaz para la que desea información adicional, es solo un mecanismo de comunicación entre el espacio de usuario y el kernel. en.wikipedia.org/wiki/Ioctl lxr.free-electrons.com/source/net/socket.c
tMC
2
Funciona en Python3 con una modificación: struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)debe reemplazarse construct.pack('16sH14s', iface.encode('utf-8'), socket.AF_INET, b'\x00'*14)
pepoluan
1
@ChristianFischer ioctles una interfaz heredada que no creo que sea compatible con IPv6 y probablemente nunca lo hará. Creo que la forma 'correcta' es a través de Netlink, que no es muy sencillo en Python. Creo que libc debería tener la función a la getifaddrsque se puede acceder a través del ctypesmódulo de pitones que puede funcionar - man7.org/linux/man-pages/man3/getifaddrs.3.html
tMC
1
@Maddy ioctl es una interfaz heredada que no creo que sea compatible con IPv6 y probablemente nunca lo hará. Creo que la forma 'correcta' es a través de Netlink, que no es muy sencillo en Python. Creo libc debe tener las getifaddrs de función que se puede acceder a través de pitones ctypes módulo que puede trabajar - man7.org/linux/man-pages/man3/getifaddrs.3.html
tMC
27

Estoy usando el siguiente módulo:

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

Probado con Windows y Linux (y no requiere módulos adicionales para esos) destinados a su uso en sistemas que se encuentran en una sola LAN basada en IPv4.

La lista fija de nombres de interfaz no funciona para las versiones recientes de Linux, que han adoptado el cambio systemd v197 con respecto a los nombres de interfaz predecibles como lo señala Alexander . En tales casos, debe reemplazar manualmente la lista con los nombres de interfaz en su sistema, o usar otra solución como netifaces .

smerlin
fuente
2
Esto es incompatible con los nuevos nombres de interfaz de Linux predecibles, como enp0s25. Para obtener más información, consulte wiki.archlinux.org/index.php/Network_Configuration#Device_names
Alexander
2
Estaba usando python 3.4 y la parte 'struct.pack (...)' necesitaba cambiarse a 'struct.pack (' 256s ', bytes (ifname [: 15],' utf-8 '))'. Ver esta pregunta: stackoverflow.com/q/27391167/76010
Bakanekobrain
1
en Raspbian con Python 2.7.3 - a bytes () no le gustó el segundo argumento Pero esto funcionó: struct.pack('256s', bytes(ifname[:15]))
colm.anseo
24

Lo uso en mis máquinas ubuntu:

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

Esto no funciona

shino
fuente
Agradable y simple También funciona en Linux AMI de Amazon, pero solo si soy root. De lo contrario, obtendría un error: 'sh: ifconfig: comando no encontrado'
IgorGanapolsky
Entonces deberías usar "/ sbin / ifconfig" como dijo gavaletz. También funciona en Red Hat 4.1.2-48.
IgorGanapolsky
77
En desuso desde 2.6. Use el módulo de subproceso para ejecutar comandos.
Colin Dunklau
55
Y ifconfig también está en desuso. Use iproute2.
Helmut Grohne
Obtenga todos los ips: import sh; [ip.split () [1] [5:] para ip en filtro (lambda x: 'inet addr' en x, sh.ifconfig (). split ("\ n"))]
Gabriel Littman
20

Si no desea utilizar paquetes externos y no desea confiar en servidores externos de Internet, esto podría ayudar. Es una muestra de código que encontré en Google Code Search y modifiqué para devolver la información requerida:

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

Uso:

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

Como se basa windll, esto solo funcionará en Windows.

DzinX
fuente
La solución de un revestimiento anterior generalmente funciona en ventanas. Es el Linux el que está siendo un problema.
ricree
14
+1 Esta técnica al menos intenta devolver todas las direcciones en la máquina.
Jason R. Coombs
1
Este script falla en mi máquina después de devolver la primera dirección. El error es "AttributeError: el objeto 'LP_IP_ADDR_STRING' no tiene el atributo 'ipAddress'" Sospecho que tiene algo que ver con la dirección IPv6.
Jason R. Coombs
1
Resulta que el problema es que para cualquier cosa que no sea la primera dirección IP, el adNode no está desreferenciado. Agregue una línea más al ejemplo en el ciclo while y funciona para mí: adNode = adNode.contents
Jason R. Coombs
18

En Debian (probado) y sospecho que la mayoría de Linux ...

import commands

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

En MS Windows (probado)

import socket

socket.gethostbyname(socket.gethostname())
www-0av-Com
fuente
1
No funciona en macOS:hostname: illegal option -- I\nusage: hostname [-fs] [name-of-host]
Derek 朕 會 功夫
18

Una versión que no creo que haya sido publicada todavía. Probé con python 2.7 en Ubuntu 12.04.

Encontré esta solución en: 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])

Resultado de ejemplo:

>>> get_ip_address('eth0')
'38.113.228.130'
Graham Chap
fuente
2
Funciona en Python3, Ubuntu 18.04; La cadena debe ser bytes: >>> socket.inet_ntoa (fcntl.ioctl (s.fileno (), 0x8915, struct.pack ('256s', 'enp0s31f6' [: 15] .encode ('utf-8') )) [20:24]) '192.168.1.1'
asesor
12

Variación sobre la respuesta de ninjagecko. Esto debería funcionar en cualquier LAN que permita la transmisión UDP y no requiera acceso a una dirección en la LAN o 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
fuente
Espera, ¿cómo es <broadcast>un nombre de host válido? ¿Cuántos de estos tipos de nombres de host verbales son válidos?
Dev Aggarwal
Esto funciona para mí en Ubuntu 20.04: obtener 192.168.0.24 no 127.0.0.1
Lewis Morris
8

Me temo que no hay una buena plataforma independiente para hacer esto que no sea conectarse a otra computadora y que le envíe su dirección IP. Por ejemplo: findmyipaddress . Tenga en cuenta que esto no funcionará si necesita una dirección IP que esté detrás de NAT a menos que la computadora a la que se está conectando también esté detrás de NAT.

Aquí hay una solución que funciona en Linux: obtenga la dirección IP asociada con una interfaz de red .

Jason Baker
fuente
8

Una forma sencilla de producir resultados "limpios" a través de las utilidades de línea de comandos:

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

Mostrará todas las direcciones IPv4 en el sistema.

Viker
fuente
1
No mostrará todas las direcciones IPv4, porque ifconfig solo le informa sobre las principales. Debe usar "ip" de iproute2 para ver todas las direcciones.
Helmut Grohne
Esa es una gran cantidad de shell para una pregunta que solicita la biblioteca estándar ... Además, analizar ifconfig no es portátil y ni siquiera funcionará de manera confiable en una máquina.
Dominik George
7

FYI puedo verificar que el método:

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

Funciona en OS X (10.6,10.5), Windows XP y en un servidor de departamento RHEL bien administrado. No funcionó en una VM CentOS muy mínima en la que solo hago un poco de pirateo del kernel. Entonces, para esa instancia, solo puede verificar la dirección 127.0.0.1 y, en ese caso, hacer lo siguiente:

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

Y luego analiza la dirección IP de la salida. Cabe señalar que ifconfig no está en la RUTA de un usuario normal de forma predeterminada y es por eso que doy la ruta completa en el comando. Espero que esto ayude.

gavaletz
fuente
7

Esta es una variante de la respuesta de UnkwnTech: proporciona una get_local_addr()función que devuelve la dirección IP de LAN principal del host. Lo estoy publicando porque esto agrega una serie de cosas: soporte de ipv6, manejo de errores, ignorando los agregadores localeshost / linklocal, y usa un complemento TESTNET (rfc5737) para conectarse.

# 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
fuente
Pensé que esta podría haber sido una muy buena respuesta ... pero siempre regresaNone
Jamie Lindsey
@JamieLindsey ¿Tiene algunos detalles sobre su sistema operativo, la configuración de red? Además, ¿qué hace algo como get_local_addr(remove="www.google.com")regresar? El registro de los socket.errorlanzados por _get_local_addr () podría ayudar en el diagnóstico.
Eli Collins
6
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
Nakilon
fuente
1
Hmm ... en un servidor con dos NIC, esto proporciona una de las direcciones IP asignadas, pero se repite tres veces. En mi computadora portátil da '127.0.1.1' (repetido tres veces ...) ...
bryn
Me da ['fe80::34e8:fe19:1459:2cde%22','fe80::d528:99fb:d572:e289%12', '192.168.56.1', '192.168.1.2']en el escritorio de Windows.
Nakilon
5

Esto funcionará en la mayoría de las cajas de 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
fuente
5

Esta respuesta es mi intento personal de resolver el problema de obtener la IP de LAN, ya que socket.gethostbyname(socket.gethostname())también devolvió 127.0.0.1. Este método no requiere Internet, solo una conexión LAN. El código es para Python 3.x pero podría convertirse fácilmente para 2.x. Usando UDP Broadcast:

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

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

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

if __name__ == '__main__':
    print(get_local_ip())
WolfRage
fuente
1
¿Qué sucede si ejecuta esto simultáneamente en dos máquinas en la misma red? A medida que difunde su mensaje en la red, todas las máquinas recibirán '¿Cuál es mi dirección IP de LAN? Su udp_listening_server podría responder 'su dirección IP es xxx' al mensaje.
Nicolas Defranoux
4

127.0.1.1 es tu dirección IP real En términos más generales, una computadora puede tener cualquier número de direcciones IP. Puede filtrarlos para redes privadas: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12 y 192.168.0.0/16.

Sin embargo, no hay una forma multiplataforma para obtener todas las direcciones IP. En Linux, puede usar el SIOCGIFCONFioctl.

phihag
fuente
2
Se refiere a su IP externamente visible. El rango 127. *. *. * Generalmente se refiere a localhost o una red interna, que claramente no es lo que él quiere.
Cerin
4

Un ligero refinamiento de la versión de los comandos que usa el comando IP y devuelve las direcciones IPv4 e 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
fuente
4

Bueno, puede usar el comando "ip route" en GNU / Linux para conocer su dirección IP actual.

Esto muestra la IP dada a la interfaz por el servidor DHCP que se ejecuta en el enrutador / módem. Por lo general, "192.168.1.1/24" es la IP para la red local, donde "24" significa el rango de posibles direcciones IP proporcionadas por el servidor DHCP dentro del rango de máscara.

Aquí hay un ejemplo: tenga en cuenta que PyNotify es solo una adición para aclarar mi punto y no es obligatorio en absoluto

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

La ventaja de esto es que no necesita especificar la interfaz de red. Eso es bastante útil cuando se ejecuta un servidor de socket

Puede instalar PyNotify usando easy_install o incluso Pip:

easy_install py-notify

o

pip install py-notify

o dentro de un script / intérprete de Python

from pip import main

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

Si está buscando una dirección IPV4 diferente de su dirección IP localhost 127.0.0.1, aquí hay una buena pieza de códigos python:

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

Que también se puede escribir en una sola línea:

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

Incluso si se pone localhosten /etc/hostnameel código todavía le dará a su dirección IP local.

hmofrad
fuente
4

Para Linux, puede usar check_outputel hostname -Icomando del sistema de la siguiente manera:

from subprocess import check_output
check_output(['hostname', '-I'])
Kasper Skytte Andersen
fuente
para googlers, sé que la pregunta era para una solución multiplataforma
Kasper Skytte Andersen
3

Nota: Esto no está usando la biblioteca estándar, pero es bastante simple.

$ pip instalar pif

from pif import get_public_ip
get_public_ip()
Artur Barseghyan
fuente
3
la pregunta era sobre encontrar la IP usando stdlib
Alexandru Chirila
3

netifaces está disponible a través de pip y easy_install. (Lo sé, no está en la base, pero podría valer la pena la instalación).

netifaces tiene algunas rarezas entre plataformas:

  • La interfaz localhost / loop-back no siempre se puede incluir (Cygwin).
  • Las direcciones se enumeran por protocolo (por ejemplo, IPv4, IPv6) y los protocolos se enumeran por interfaz. En algunos sistemas (Linux) cada par de protocolo-interfaz tiene su propia interfaz asociada (usando la notación interface_name: n) mientras que en otros sistemas (Windows) una única interfaz tendrá una lista de direcciones para cada protocolo. En ambos casos hay una lista de protocolos, pero puede contener solo un elemento.

Aquí hay un código de netifaces para jugar:

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.

El código anterior no asigna una dirección a su nombre de interfaz (útil para generar reglas de ebtables / iptables sobre la marcha). Así que aquí hay una versión que mantiene la información anterior con el nombre de la interfaz en una tupla:

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]

Y no, no estoy enamorado de las listas de comprensión. Es la forma en que funciona mi cerebro en estos días.

El siguiente fragmento lo imprimirá todo:

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)

¡Disfrutar!

usuario3712955
fuente
Netifaces realmente facilita mucho la vida al lidiar con este problema.
Drake Guan el
3

Una versión de Python 3.4 que utiliza el paquete asyncio recientemente introducido.

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

Esto se basa en la excelente respuesta de UnkwnTech .

Frederik Aalund
fuente
3

Para obtener la dirección IP, puede usar un comando de shell directamente 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
fuente