Localizando endereços IP locais usando o stdlib do Python

547

Como posso encontrar endereços IP locais (por exemplo, 192.168.xx ou 10.0.xx) na plataforma Python de forma independente e usando apenas a biblioteca padrão?

UnkwnTech
fonte
7
O IP local? Ou IP público? Como você vai lidar com sistemas com vários IPs?
Sargun Dhillon 5/11
usar ifconfig -ae usar a saída de lá ...
Fredrik Pihl
16
@ Fredrik: É uma má ideia. Primeiro de tudo, você está desnecessariamente criando um novo processo, o que pode impedir que seu programa funcione em configurações firmemente bloqueadas (ou você terá que permitir direitos que seu programa não precisa). Em segundo lugar, você apresentará bugs para usuários de diferentes localidades. Em terceiro lugar, se você decidir iniciar um novo programa, não deverá iniciar um programa obsoleto - ip addré muito mais adequado (e mais fácil de analisar, inicializar).
Phihag
12
@phihag você está absolutamente correto, obrigado por corrigir minha estupidez
Fredrik Pihl
1
Um problema mais fundamental aqui é que, em um programa de rede moderno escrito adequadamente, o (conjunto de) endereços IP locais depende do par ou do conjunto de possíveis pares. Se o endereço IP local for necessário para bindum soquete para uma interface específica, será uma questão de política. Se o endereço IP local for necessário para entregá-lo a um par, para que ele possa "ligar de volta", ou seja, para abrir uma conexão de volta à máquina local, a situação depende se existe algum NAT (Network Address Translation) caixas no meio. Se não houver NATs, getsocknameé uma boa escolha.
Pekka Nikander

Respostas:

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

Isso não funcionará sempre (retorna 127.0.0.1em máquinas com o nome do host em /etc/hostscomo 127.0.0.1), um paliativo seria o que o gimel mostra, em socket.getfqdn()vez disso , use . É claro que sua máquina precisa de um nome de host resolvível.

Vinko Vrsalovic
fonte
43
Deve-se notar que essa não é uma solução independente de plataforma. Muitos Linuxes retornarão 127.0.0.1 como seu endereço IP usando este método.
Jason Baker
20
Uma variação: socket.gethostbyname (socket.getfqdn ())
gimel 3/08/08
55
Isso parece retornar apenas um único endereço IP. E se a máquina tiver vários endereços?
21139 Jason R. Coombs
26
No Ubuntu, isso retorna 127.0.1.1 por algum motivo.
slikts
13
@ Jason R. Coombs, use o seguinte código para recuperar a lista de endereços IPv4 que pertencem à máquina host:socket.gethostbyname_ex(socket.gethostname())[-1]
Barmaley
460

Acabei de encontrar isso, mas parece um pouco imprudente, no entanto, eles dizem que tentaram no * nix e eu fiz no Windows e funcionou.

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

Isso pressupõe que você tenha acesso à Internet e que não há proxy local.

UnkwnTech
fonte
31
Bom se você tiver várias interfaces na máquina, e precisa a que rotas para eg gmail.com
elzapp
4
Pode ser uma boa idéia capturar exceções do socket.error que podem ser levantadas pelo s.connect ()!
Phobie
39
Seria melhor usar o endereço IP em vez de um nome de domínio - ele deve ser mais rápido e independente da disponibilidade do DNS. Por exemplo, podemos usar o IP 8.8.8.8 - servidor DNS público do Google.
Wobmene
10
Muito inteligente, funciona perfeitamente. Em vez do gmail ou 8.8.8.8, você também pode usar o IP ou o endereço do servidor do qual deseja ser visto, se aplicável.
O contrato do Prof. Falken violou
3
Este exemplo tem uma dependência externa de poder realmente resolver o gmail.com. Se você configurá-lo para um endereço IP que não esteja na sua LAN local (não importa se está ativo ou inativo), ele funcionará sem dependências e sem tráfego de rede.
sombrio
254

Este método retorna o IP "primário" na caixa local (aquela com uma rota padrão) .

  • NÃO precisa de acesso à rede roteável ou qualquer conexão.
  • Funciona mesmo se todas as interfaces estiverem desconectadas da rede.
  • NÃO precisa nem tenta chegar a outro lugar .
  • Funciona com IPs NAT, públicos, privados, externos e internos
  • Pure Python 2 (ou 3) sem dependências externas.
  • Funciona em Linux, Windows e 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

Isso retorna um único IP que é o principal (aquele com uma rota padrão). Se você precisar de todos os IPs conectados a todas as interfaces (incluindo localhost, etc), consulte esta resposta .

Se você estiver protegido por um firewall NAT como a sua caixa wifi em casa, isso não mostrará seu IP NAT público, mas seu IP privado na rede local que possui uma rota padrão para o roteador WIFI local; Para obter o IP externo do seu roteador wifi, é necessário executá-lo nessa caixa ou conectar-se a um serviço externo, como whatismyip.com/whatismyipaddress.com, que pode refletir o IP ... mas isso é completamente diferente da pergunta original. :)

erro fatal
fonte
7
Funciona em Raspbian com Python 2 e 3!
pierce.jason
3
Brilhante. Funciona no Win7,8,8.1 + Linux Mint & Arch, incluindo VMs.
Shermy 03/03
2
Funciona no Windows 10 Pro! Obrigado, Jamieson Becker!
varantes
3
Por algum motivo, isso não funciona no Mac OS X El Capitan 10.11.6 (gera um erro de exceção do SO: [Erro 49] Não é possível atribuir o endereço solicitado). Alterando a porta de '0' a '1': s.connect (( '10.255.255.255', 1)) trabalhou para mim tanto no Mac OS X e Linux Ubuntu 17.04
Pedro Scarapicchia Júnior
10
Essa deve ser a resposta aceita. socket.gethostbyname(socket.gethostname())dá resultados horríveis.
Jason Floyd
142

Como um apelido chamado myip, isso deve funcionar em qualquer lugar:

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 corretamente com Python 2.x, Python 3.x, distribuições Linux modernas e antigas, OSX / macOS e Windows para encontrar o endereço IPv4 atual.
  • Não retornará o resultado correto para máquinas com vários endereços IP, IPv6, sem endereço IP configurado ou sem acesso à Internet.

O mesmo que acima, mas apenas o 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])
  • Isso gerará uma exceção se nenhum endereço IP estiver configurado.

Versão que também funcionará em LANs sem conexão à 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])

(obrigado @ccpizza )


Antecedentes :

O uso socket.gethostbyname(socket.gethostname())não funcionou aqui, porque um dos computadores em que eu estava tinha um /etc/hostscom entradas duplicadas e referências a si mesmo. socket.gethostbyname()retorna apenas a última entrada em /etc/hosts.

Esta foi minha tentativa inicial, que elimina todos os endereços, começando com "127.":

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

Isso funciona com Python 2 e 3, no Linux e Windows, mas não lida com vários dispositivos de rede ou IPv6. No entanto, ele parou de funcionar em distros recentes do Linux, então tentei essa técnica alternativa. Ele tenta se conectar ao servidor DNS do Google 8.8.8.8na porta 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])

Em seguida, combinei as duas técnicas acima em uma linha que deve funcionar em qualquer lugar e criei o myipalias e o snippet do Python na parte superior desta resposta.

Com a crescente popularidade do IPv6 e para servidores com várias interfaces de rede, o uso de um módulo Python de terceiros para encontrar o endereço IP é provavelmente mais robusto e confiável do que qualquer um dos métodos listados aqui.

Alexander
fonte
2
@ Alexander: Apenas dizer que esta resposta é muito menos útil do que costumava ser (e não é como filtrar duplicatas é um grande negócio;). De acordo com a documentação, ele socket.getaddrinfo()deve funcionar de maneira consistente em várias plataformas - mas eu só verifiquei no Linux, não me incomodei com nenhum outro sistema operacional.
Wladimir Palant 04/10
1
@ Alexander, /etc/resolve.conf: No such file or directorye eu tenho o endereço IPv4 local mostrado por ifconfig.
Anatoly techtonik
2
Posso confirmar que a versão atualizada funciona com o Ubuntu 14.04 com o Python2 e o Py3k.
Uli Köhler
4
A "atualização" mostra um bom truque com o connect () em um soquete UDP. Ele não envia tráfego, mas permite encontrar qual seria o endereço do remetente dos pacotes para o destinatário especificado. A porta é provavelmente irrelevante (até 0 deve funcionar). Em um host com hospedagem múltipla, é importante escolher um endereço na sub-rede correta.
Peter Hansen
17
só porque você pode escrever esse código em uma única linha, isso não significa que você deve ...
JackLeo
91

Você pode usar o módulo netifaces . Basta digitar:

pip install netifaces

no shell de comando e ele se instalará na instalação padrão do Python.

Então você pode usá-lo assim:

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

No meu computador, ele imprimiu:

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

O autor deste módulo afirma que ele deve funcionar no Windows, UNIX e Mac OS X.

DzinX
fonte
20
Conforme indicado na pergunta, quero algo da instalação padrão, pois não é necessária nenhuma instalação adicional.
UnkwnTech 3/08/08
4
Essa seria minha resposta favorita, exceto que os netifaces não suportam IPv6 no Windows e parecem não mantidos. Alguém descobriu como obter endereços IPv6 no Windows?
Jean-Paul Calderone
3
O netifaces não suporta py3k e requer um compilador C, que é um PITA no Windows.
Matt Joiner
4
@MattJoiner Nada disso é verdade (a versão mais recente possui binários do Windows no PyPI e suporta o Py3K).
Alastair
4
@ Jean-PaulCalderone FWIW, a última versão do netifaces faz suporte IPv6 no Windows.
Alastair
47

Método API Socket

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

Desvantagens:

  • Não é multiplataforma.
  • Requer mais código de fallback, vinculado à existência de endereços específicos na internet
  • Isso também não funcionará se você estiver atrás de um NAT
  • Provavelmente cria uma conexão UDP, não independente da disponibilidade de DNS (geralmente do provedor de serviços de Internet) (consulte outras respostas para obter idéias como usar 8.8.8.8: servidor do Google (coincidentemente também DNS))
  • Certifique-se de tornar o endereço de destino inacessível, como um endereço IP numérico cuja garantia é especificada como não utilizada. NÃO use algum domínio como fakesubdomain.google.com ou somefakewebsite.com; você ainda estará enviando spam para essa parte (agora ou no futuro) e enviando spam para suas próprias caixas de rede também no processo.

Método refletor

(Observe que isso não responde à pergunta do OP sobre o endereço IP local, por exemplo, 192.168 ...; fornece seu endereço IP público, o que pode ser mais desejável dependendo do caso de uso.)

Você pode consultar algum site como whatismyip.com (mas com uma 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)

ou se estiver usando 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)

Vantagens:

  • Uma vantagem desse método é que é multiplataforma
  • Funciona por trás de NATs feias (por exemplo, seu roteador doméstico).

Desvantagens (e soluções alternativas):

  • Requer que este site seja instalado, que o formato não seja alterado (quase certamente não será) e que os servidores DNS estejam funcionando. Pode-se atenuar esse problema consultando outros refletores de endereços IP de terceiros em caso de falha.
  • Possível vetor de ataque se você não consultar vários refletores (para impedir que um refletor comprometido lhe diga que seu endereço é algo que não é) ou se você não usar HTTPS (para impedir um ataque do tipo intermediário que finge ser o servidor)

editar : Embora inicialmente eu tenha pensado que esses métodos eram realmente ruins (a menos que você use muitos fallbacks, o código pode ser irrelevante daqui a muitos anos), ele coloca a pergunta "o que é a internet?". Um computador pode ter muitas interfaces apontando para muitas redes diferentes. Para uma descrição mais completa do tópico, o google paragateways and routes . Um computador pode acessar uma rede interna por meio de um gateway interno ou acessar a Internet através de um gateway, por exemplo, em um roteador (geralmente o caso). O endereço IP local que o OP pergunta apenas é bem definido em relação a uma única camada de link, então você precisa especificar que ("é a placa de rede ou o cabo Ethernet, do que estamos falando?") . Pode haver várias respostas não exclusivas para esta pergunta, conforme colocadas. No entanto, o endereço IP global na rede mundial de computadores provavelmente está bem definido (na ausência de fragmentação maciça da rede): provavelmente o caminho de retorno através do gateway que pode acessar os TLDs.

ninjagecko
fonte
Isso retornará seu endereço para toda a LAN se você estiver atrás de um NAT. Se você estiver se conectando à Internet, poderá se conectar a um serviço da Web que retorne um dos seus endereços IP públicos.
Phihag
Ele não cria uma conexão TCP porque cria uma conexão UDP.
Anuj Gupta
2
Como alternativa na versão da API do soquete, substitua s.connect (('INSERIR ALGUNS WEBSITE DO TARGET WEBSITE.com', 0)) por s.setsockopt (socket.SOL_SOCKET, socket.SO_BROADCAST, 1); s.connect (('< broadcast> ', 0)) para evitar a pesquisa de DNS. (Eu acho que pode haver um problema com a transmissão se houver um firewall)
DLM
45

Se o computador tiver uma rota para a Internet, isso sempre funcionará para obter o endereço IP local preferido, mesmo que o / etc / hosts não esteja definido corretamente.

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
fonte
Como é que isso funciona ? , 8.8.8.8é um servidor DNS do Google? Podemos fazê-lo com um servidor DNS local?
Ciasto piekarz 06/04
39

No 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
fonte
Portanto, isso efetivamente abre um soquete com o qual ele não faz nada e você verifica os dados brutos sobre esse soquete para obter o IP local?
Dave
1
O soquete é aberto para que um fd se comunique com o kernel (via ioctl). O soquete não está vinculado à interface para a qual você deseja informações adicionais - é apenas um mecanismo de comunicação entre o espaço do usuário e o kernel. en.wikipedia.org/wiki/Ioctl lxr.free-electrons.com/source/net/socket.c
TMC
2
Funciona no Python3 com uma modificação: struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)deve ser substituído porstruct.pack('16sH14s', iface.encode('utf-8'), socket.AF_INET, b'\x00'*14)
pepoluan
1
@ChristianFischer ioctlé uma interface herdada que não acredito que seja compatível com IPv6 e provavelmente nunca será. Eu acho que o caminho "certo" é via Netlink, o que não é muito simples em Python. Acho libc deve ter a função getifaddrsque pode ser acessado via pítons ctypesmódulo que pode funcionar - man7.org/linux/man-pages/man3/getifaddrs.3.html
TMC
1
@ Maddy ioctl é uma interface herdada que não acredito que suporte IPv6 e provavelmente nunca será. Eu acho que o caminho "certo" é via Netlink, o que não é muito simples em Python. Acho libc deve ter as getifaddrs função que pode ser acessado via pítons ctypes módulo que pode funcionar - man7.org/linux/man-pages/man3/getifaddrs.3.html
TMC
27

estou usando o seguinte 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

Testado com Windows e Linux (e não requer módulos adicionais para esses) destinados ao uso em sistemas que estão em uma única LAN baseada em IPv4.

A lista fixa de nomes de interface não funciona nas versões recentes do Linux, que adotaram a alteração systemd v197 em relação aos nomes de interface previsíveis, conforme apontado por Alexander . Nesses casos, você precisa substituir manualmente a lista pelos nomes de interface em seu sistema ou usar outra solução, como netifaces .

smerlin
fonte
2
Isso é incompatível com os novos nomes de interface previsíveis do Linux, como enp0s25. Para obter mais informações, consulte wiki.archlinux.org/index.php/Network_Configuration#Device_names
Alexander
2
Eu estava usando python 3.4 e a parte 'struct.pack (...)' precisava ser alterada para 'struct.pack (' 256s ', bytes (ifname [: 15],' utf-8 '))'. Veja esta pergunta: stackoverflow.com/q/27391167/76010
Bakanekobrain 6/15
1
no Raspbian com Python 2.7.3 - bytes () não gostou do segundo argumento. Mas isso funcionou: struct.pack('256s', bytes(ifname[:15]))
colm.anseo
24

Eu uso isso nas minhas máquinas ubuntu:

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

Isso não funciona.

shino
fonte
Agradável e simples. Funciona também no Linux AMI da Amazon, mas apenas se eu for root. Caso contrário, eu receberia um erro: 'sh: ifconfig: command not found'
IgorGanapolsky 10/11
Então você deve usar "/ sbin / ifconfig" como o gavaletz disse. Também funciona no Red Hat 4.1.2-48.
IgorGanapolsky
7
Descontinuado desde 2.6. Use o módulo de subprocesso para executar comandos.
Colin Dunklau
5
E o ifconfig também está obsoleto. Use iproute2.
Helmut Grohne
Obtenha todos os ips: import sh; [ip.split () [1] [5:] para ip no filtro (lambda x: 'inet addr' em x, sh.ifconfig (). split ("\ n"))]
Gabriel Littman em
20

Se você não quiser usar pacotes externos e não quiser contar com servidores externos da Internet, isso pode ajudar. É um exemplo de código que encontrei na Pesquisa de código do Google e modifiquei para retornar as informações necessárias:

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 baseia windll, isso funcionará apenas no Windows.

DzinX
fonte
A solução de um liner acima geralmente funciona no Windows. É o Linux que está sendo um problema.
Ricree 18/06/09
14
+1 Esta técnica pelo menos tenta retornar todos os endereços na máquina.
21119 Jason R. Coombs
1
Este script falha na minha máquina depois de retornar o primeiro endereço. O erro é "AttributeError: 'LP_IP_ADDR_STRING' objeto não tem atributo 'ipAddress'" "Eu suspeito que tenha algo a ver com o endereço IPv6.
21139 Jason R. Coombs
1
Acontece que o problema é que, para qualquer coisa, menos o primeiro endereço IP, o adNode não é desreferenciado. Adicionar mais uma linha para o exemplo no loop while e ele funciona para mim: adNode = adNode.contents
Jason R. Coombs
18

No Debian (testado) e suspeito que a maioria dos Linux ..

import commands

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

No MS Windows (testado)

import socket

socket.gethostbyname(socket.gethostname())
www-0av-Com
fonte
1
Não funciona no macOS:hostname: illegal option -- I\nusage: hostname [-fs] [name-of-host]
Derek,
18

Uma versão que eu não acredito que tenha sido publicada ainda. Eu testei com python 2.7 no Ubuntu 12.04.

Encontre esta solução em: 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 exemplo:

>>> get_ip_address('eth0')
'38.113.228.130'
Graham Chap
fonte
2
Funciona em Python3, Ubuntu 18.04; A cadeia precisa ser de bytes: >>> socket.inet_ntoa (fcntl.ioctl (s.fileno (), 0x8915, struct.pack ('256s', 'enp0s31f6' [: 15] .encode ('utf-8')) )) [20:24]) '192.168.1.1'
cessador
12

Variação na resposta de ninjagecko. Isso deve funcionar em qualquer LAN que permita a transmissão UDP e não exija acesso a um endereço na LAN ou na 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
fonte
Espere, como é <broadcast>um nome de host válido? !! Quantos desses tipos de nomes de host verbais são válidos?
Dev Aggarwal
Isso funciona para mim no Ubuntu 20.04 - obtendo 192.168.0.24 e não 127.0.0.1
Lewis Morris
8

Receio que não haja boas maneiras independentes de plataforma para fazer isso, além de conectar-se a outro computador e fazer com que ele envie seu endereço IP. Por exemplo: findmyipaddress . Observe que isso não funcionará se você precisar de um endereço IP atrás do NAT, a menos que o computador ao qual está se conectando também esteja atrás do NAT.

Aqui está uma solução que funciona no Linux: obtenha o endereço IP associado a uma interface de rede .

Jason Baker
fonte
8

Uma maneira simples de produzir saída "limpa" por meio de utilitários de linha de comando:

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

Ele mostrará todos os endereços IPv4 no sistema.

viker
fonte
1
Ele não mostrará todos os endereços IPv4, porque o ifconfig apenas informa sobre os principais. Você precisa usar "ip" do iproute2 para ver todos os endereços.
Helmut Grohne
É um monte de shell para uma pergunta que pede a biblioteca padrão ... Além disso, a análise do ifconfig não é portátil e nem funcionará de maneira confiável em uma máquina.
Dominik George
7

Para sua informação, posso verificar se o método:

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

Funciona no OS X (10.6,10.5), Windows XP e em um servidor de departamento RHEL bem administrado. Não funcionou em uma VM CentOS muito mínima na qual eu apenas fiz alguns hackers no kernel. Portanto, nessa instância, você pode apenas procurar um endereço 127.0.0.1 e, nesse caso, fazer o seguinte:

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

E, em seguida, analise o endereço IP da saída. Deve-se notar que ifconfig não está no PATH de um usuário normal por padrão e é por isso que dou o caminho completo no comando. Eu espero que isso ajude.

gavaletz
fonte
7

Essa é uma variante da resposta da UnkwnTech - fornece uma get_local_addr()função que retorna o endereço IP da LAN principal do host. Estou publicando porque adiciona várias coisas: suporte ao ipv6, tratamento de erros, ignorando localhost / linklocal addrs e usa um endereço TESTNET (rfc5737) para conectar-se.

# 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
fonte
Eu pensei que esta poderia ter sido uma resposta muito boa .. mas sempre voltaNone
Jamie Lindsey
@JamieLindsey Você tem alguns detalhes sobre seu sistema operacional, configuração de rede? Além disso, o que algo como get_local_addr(remove="www.google.com")retorno? Registrar o socket.errorlançado por _get_local_addr () pode ajudar no diagnóstico.
Eli Collins
6
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
Nakilon
fonte
1
Hmm ... em um servidor com duas NICs, isso fornece um dos endereços IP atribuídos, mas repetido três vezes. No meu laptop, dá '127.0.1.1' (repetido três vezes ...) ...
bryn
Dá-me ['fe80::34e8:fe19:1459:2cde%22','fe80::d528:99fb:d572:e289%12', '192.168.56.1', '192.168.1.2']na área de trabalho do Windows.
Nakilon
5

Isso funcionará na maioria das caixas 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
fonte
5

Esta resposta é minha tentativa pessoal de resolver o problema de obter o IP da LAN, já que socket.gethostbyname(socket.gethostname())também retornou 127.0.0.1. Este método não requer da Internet apenas uma conexão LAN. O código é para Python 3.x, mas pode ser facilmente convertido para 2.x. Usando transmissão 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
fonte
1
O que acontece se você executar isso simultaneamente em duas máquinas na mesma rede? À medida que você transmite sua mensagem na rede, todas as máquinas receberão o 'Qual é o meu endereço IP da LAN. Seu udp_listening_server pode responder 'seu endereço IP é xxx' para a mensagem.
Nicolas Defranoux
4

127.0.1.1 é o seu endereço IP real. De um modo mais geral, um computador pode ter qualquer número de endereços IP. Você pode filtrá-los para redes privadas - 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12 e 192.168.0.0/16.

No entanto, não existe uma maneira multiplataforma de obter todos os endereços IP. No Linux, você pode usar o SIOCGIFCONFioctl.

phihag
fonte
2
Ele quer dizer seu IP visível externamente. O intervalo 127. *. *. * Normalmente refere-se ao host local ou a uma rede interna, o que claramente não é o que ele deseja.
Cerin
4

Um ligeiro refinamento da versão dos comandos que usa o comando IP e retorna os endereços 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
fonte
4

Bem, você pode usar o comando "ip route" no GNU / Linux para saber seu endereço IP atual.

Isso mostra o IP fornecido à interface pelo servidor DHCP em execução no roteador / modem. Geralmente "192.168.1.1/24" é o IP da rede local, onde "24" significa o intervalo de endereços IP possíveis fornecidos pelo servidor DHCP dentro do intervalo da máscara.

Aqui está um exemplo: Observe que o PyNotify é apenas uma adição para esclarecer meu ponto e não é necessário.

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

A vantagem disso é que você não precisa especificar a interface de rede. Isso é bastante útil ao executar um servidor de soquete

Você pode instalar o PyNotify usando o easy_install ou até o Pip:

easy_install py-notify

ou

pip install py-notify

ou no script / interpretador python

from pip import main

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

Se você está procurando um endereço IPV4 diferente do endereço IP do host local 127.0.0.1, aqui está uma parte interessante dos códigos python:

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

O que também pode ser escrito em uma única linha:

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

Mesmo se você colocar localhostem /etc/hostname, o código ainda vai dar o seu endereço IP local.

hmofrad
fonte
4

Para linux, você pode apenas usar check_outputo hostname -Icomando system da seguinte maneira:

from subprocess import check_output
check_output(['hostname', '-I'])
Kasper Skytte Andersen
fonte
para Googlers, eu sei que a pergunta era para uma solução de plataforma cruzada
Kasper Andersen Skytte
3

Nota: Isso não está usando a biblioteca padrão, mas é bastante simples.

$ pip install pif

from pif import get_public_ip
get_public_ip()
Artur Barseghyan
fonte
3
as perguntas foi sobre encontrar o IP usando stdlib
Alexandru Chirila
3

O netifaces está disponível via pip e easy_install. (Eu sei, não está na base, mas pode valer a instalação.)

O netifaces tem algumas esquisitices nas plataformas:

  • A interface localhost / loopback nem sempre pode ser incluída (Cygwin).
  • Os endereços são listados por protocolo (por exemplo, IPv4, IPv6) e os protocolos são listados por interface. Em alguns sistemas (Linux), cada par de interface de protocolo possui sua própria interface associada (usando a notação interface_name: n), enquanto em outros sistemas (Windows) uma única interface possui uma lista de endereços para cada protocolo. Nos dois casos, há uma lista de protocolos, mas pode conter apenas um único elemento.

Aqui estão alguns códigos de netifaces para brincar:

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.

O código acima não mapeia um endereço de volta ao seu nome de interface (útil para gerar regras ebtables / iptables em tempo real). Então, aqui está uma versão que mantém as informações acima com o nome da interface em uma 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]

E não, não estou apaixonada por compreensões de lista. É assim que meu cérebro funciona hoje em dia.

O seguinte trecho imprimirá tudo:

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)

Aproveitar!

user3712955
fonte
O netifaces realmente facilita muito a vida ao lidar com esse problema.
Drake Guan
3

Uma versão do Python 3.4 utilizando o pacote asyncio recém-introduzido.

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

Isso se baseia na excelente resposta da UnkwnTech .

Frederik Aalund
fonte
3

Para obter o endereço IP, você pode usar um comando shell diretamente no 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
fonte