Menemukan alamat IP lokal menggunakan stdlib Python


547

Bagaimana saya bisa menemukan alamat IP lokal (yaitu 192.168.xx atau 10.0.xx) dalam platform Python secara independen dan hanya menggunakan perpustakaan standar?


7
IP lokal? Atau IP publik? Bagaimana Anda akan berurusan dengan sistem dengan banyak IP?
Sargun Dhillon

gunakan ifconfig -adan gunakan output dari sana ...
Fredrik Pihl

16
@ Fredrik Itu ide yang buruk. Pertama-tama, Anda tidak perlu mengambil proses baru, dan itu dapat mencegah program Anda bekerja dalam konfigurasi yang dikunci dengan ketat (atau, Anda harus mengizinkan hak-hak yang tidak dibutuhkan oleh program Anda). Kedua, Anda akan memperkenalkan bug untuk pengguna dari berbagai daerah. Ketiga, jika Anda memutuskan untuk memulai program baru sama sekali, Anda seharusnya tidak memulai yang sudah usang - ip addrjauh lebih cocok (dan lebih mudah untuk mengurai, untuk boot).
phihag

12
@ Phihag Anda benar-benar benar, terima kasih telah memperbaiki kebodohan saya
Fredrik Pihl

1
Masalah yang lebih mendasar di sini adalah bahwa dalam program jaringan modern yang ditulis dengan benar, hak (set) alamat IP lokal tergantung pada rekan, atau set rekan potensial. Jika alamat IP lokal diperlukan untuk bindsoket ke antarmuka tertentu, maka itu adalah masalah kebijakan. Jika alamat IP lokal diperlukan untuk menyerahkannya ke peer sehingga peer dapat "menelepon kembali", yaitu untuk membuka koneksi kembali ke mesin lokal, maka situasinya tergantung pada apakah ada NAT (Terjemahan Alamat Jaringan) kotak di antara keduanya. Jika tidak ada NAT, getsocknameadalah pilihan yang baik.
Pekka Nikander

Jawaban:


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

Ini tidak akan bekerja selalu (mengembalikan 127.0.0.1pada mesin yang memiliki nama host /etc/hostssebagai 127.0.0.1), paliative akan menjadi apa yang ditampilkan gimel, gunakan socket.getfqdn()saja. Tentu saja mesin Anda membutuhkan nama host yang dapat diatasi.


43
Orang harus mencatat bahwa ini bukan solusi platform independen. Banyak Linux akan mengembalikan 127.0.0.1 sebagai alamat IP Anda menggunakan metode ini.
Jason Baker

20
Variasi: socket.gethostbyname (socket.getfqdn ())
gimel

55
Tampaknya ini hanya mengembalikan satu alamat IP. Bagaimana jika mesin memiliki beberapa alamat?
Jason R. Coombs

26
Di Ubuntu ini mengembalikan 127.0.1.1 untuk beberapa alasan.
slikts

13
@Jason R. Coombs, gunakan kode berikut untuk mengambil daftar alamat IPv4 milik mesin host:socket.gethostbyname_ex(socket.gethostname())[-1]
Barmaley

460

Saya baru saja menemukan ini tetapi tampaknya agak retas, tetapi mereka mengatakan mencobanya di * nix dan saya lakukan di windows dan berhasil.

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

Ini mengasumsikan Anda memiliki akses internet, dan bahwa tidak ada proxy lokal.


31
Bagus jika Anda memiliki beberapa antarmuka pada mesin, dan membutuhkan yang menghubungkan ke misalnya gmail.com
elzapp

4
Ini mungkin ide yang bagus untuk menangkap pengecualian socket.error yang dapat dinaikkan oleh s.connect ()!
phobie

39
Akan lebih baik menggunakan alamat IP daripada nama domain - itu harus lebih cepat dan independen dari ketersediaan DNS. Misalnya kita dapat menggunakan 8.8.8.8 IP - server DNS publik Google.
wobmene

10
Sangat pintar, bekerja dengan sempurna. Alih-alih gmail atau 8.8.8.8, Anda juga dapat menggunakan IP atau alamat server yang ingin Anda lihat, jika itu berlaku.
Kontrak Prof. Falken dilanggar

3
Contoh ini memiliki ketergantungan eksternal untuk dapat benar-benar menyelesaikan gmail.com. Jika Anda mengaturnya ke alamat ip yang tidak ada di lan lokal Anda (tidak masalah apakah itu naik atau turun) itu akan berfungsi tanpa dependensi dan tanpa lalu lintas jaringan.
muram

254

Metode ini mengembalikan IP "primer" pada kotak lokal (yang dengan rute default) .

  • TIDAK memerlukan akses internet yang dapat dialihkan atau koneksi apa pun.
  • Bekerja bahkan jika semua antarmuka dicabut dari jaringan.
  • TIDAK perlu atau bahkan mencoba untuk pergi ke tempat lain .
  • Bekerja dengan NAT, IP publik, privat, eksternal, dan internal
  • Pure Python 2 (atau 3) tanpa ketergantungan eksternal.
  • Bekerja di Linux, Windows, dan OSX.

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

Ini mengembalikan satu IP yang merupakan primer (yang dengan rute default). Jika Anda membutuhkan semua IP yang terpasang pada semua antarmuka (termasuk localhost, dll), lihat jawaban ini .

Jika Anda berada di belakang firewall NAT seperti kotak wifi Anda di rumah, maka ini tidak akan menunjukkan IP NAT publik Anda, melainkan IP pribadi Anda di jaringan lokal yang memiliki rute default ke router WIFI lokal Anda; mendapatkan IP eksternal wifi router Anda akan mengharuskan menjalankan ini pada kotak ITU, atau menyambung ke layanan eksternal seperti whatismyip.com/whatismyipaddress.com yang dapat memantulkan kembali IP ... tapi itu sama sekali berbeda dari pertanyaan awal. :)


7
Bekerja di Raspbian dengan Python 2 dan 3!
pierce.jason

3
Cemerlang. Bekerja pada Win7,8,8.1 + Linux Mint & Arch, termasuk VM.
shermy

2
Bekerja di Windows 10 Pro! Terima kasih, Jamieson Becker!
menjamin

3
Untuk beberapa alasan ini tidak berfungsi pada Mac OS X El Capitan 10.11.6 (ini menghasilkan kesalahan OS pengecualian: [Errno 49] Tidak dapat menetapkan alamat yang diminta). Mengubah port dari '0' ke '1': s.connect (('10.255.255.255', 1)) bekerja untuk saya baik di Mac OS X dan Linux Ubuntu 17.04
Pedro Scarapicchia Junior

10
Ini harus menjadi jawaban yang diterima. socket.gethostbyname(socket.gethostname())memberikan hasil yang mengerikan.
Jason Floyd

142

Sebagai alias dipanggil myip, itu harus bekerja di mana saja:

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])'"
  • Bekerja dengan benar dengan Python 2.x, Python 3.x, distro Linux modern dan lama, OSX / macOS dan Windows untuk menemukan alamat IPv4 saat ini.
  • Tidak akan mengembalikan hasil yang benar untuk mesin dengan beberapa alamat IP, IPv6, tidak ada alamat IP yang dikonfigurasi atau tidak ada akses internet.

Sama seperti di atas, tetapi hanya kode 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])
  • Ini akan mengeluarkan pengecualian jika tidak ada alamat IP yang dikonfigurasi.

Versi yang juga akan berfungsi pada LAN tanpa koneksi 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])

(terima kasih @ccpizza )


Latar Belakang :

Penggunaan socket.gethostbyname(socket.gethostname())tidak berfungsi di sini, karena salah satu komputer yang saya gunakan memiliki /etc/hostsentri duplikat dan referensi untuk dirinya sendiri. socket.gethostbyname()hanya mengembalikan entri terakhir ke/etc/hosts .

Ini adalah upaya awal saya, yang menyingkirkan semua alamat yang dimulai dengan "127.":

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

Ini berfungsi dengan Python 2 dan 3, di Linux dan Windows, tetapi tidak berurusan dengan beberapa perangkat jaringan atau IPv6. Namun, itu berhenti bekerja pada distro Linux baru-baru ini, jadi saya mencoba teknik alternatif ini sebagai gantinya. Mencoba menghubungkan ke server DNS Google di 8.8.8.8at port 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])

Lalu saya menggabungkan kedua teknik di atas menjadi satu-liner yang harus bekerja di mana-mana, dan menciptakan myip potongan alias dan Python di bagian atas jawaban ini.

Dengan semakin populernya IPv6, dan untuk server dengan banyak antarmuka jaringan, menggunakan modul Python pihak ketiga untuk menemukan alamat IP mungkin lebih kuat dan dapat diandalkan daripada metode apa pun yang tercantum di sini.


2
@Alexander: Hanya mengatakan bahwa jawaban ini jauh kurang berguna daripada dulu (dan tidak seperti menyaring duplikat adalah masalah besar;). Menurut dokumentasi socket.getaddrinfo()harus bekerja secara konsisten di seluruh platform - tetapi saya hanya memeriksanya di Linux, tidak peduli tentang sistem operasi lain.
Wladimir Palant

1
@Alexander, /etc/resolve.conf: No such file or directorydan saya memiliki alamat IPv4 lokal yang ditunjukkan oleh ifconfig.
anatoly techtonik

2
Saya dapat mengonfirmasi bahwa versi yang diperbarui berfungsi dengan Ubuntu 14.04 dengan Python2 dan Py3k.
Uli Köhler

4
"Pembaruan" menunjukkan trik yang bagus dengan menghubungkan () pada soket UDP. Ini tidak mengirimkan lalu lintas tetapi tidak membiarkan Anda menemukan apa yang akan menjadi alamat pengirim untuk paket ke penerima yang ditentukan. Port tersebut kemungkinan tidak relevan (bahkan 0 harus berfungsi). Pada host multihomed, penting untuk memilih alamat di subnet yang tepat.
Peter Hansen

17
hanya karena Anda dapat menulis kode itu dalam satu baris, itu tidak berarti bahwa Anda harus ...
JackLeo

91

Anda dapat menggunakan modul netifaces . Ketik saja:

pip install netifaces

di shell perintah Anda dan itu akan menginstal sendiri pada instalasi Python default.

Maka Anda dapat menggunakannya seperti ini:

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

Di komputer saya itu dicetak:

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

Penulis modul ini mengklaim itu harus bekerja pada Windows, UNIX dan Mac OS X.


20
Seperti yang dinyatakan dalam pertanyaan saya ingin sesuatu dari instalasi default, karena tidak diperlukan instalasi tambahan.
UnkwnTech

4
Ini akan menjadi jawaban favorit saya, kecuali bahwa netifaces tidak mendukung IPv6 di Windows dan tampak tidak terawat. Adakah yang tahu cara mendapatkan alamat IPv6 di Windows?
Jean-Paul Calderone

3
netifaces tidak mendukung py3k, dan membutuhkan kompiler C yang merupakan PITA di windows.
Matt Joiner

4
@MattJoiner Tak satu pun dari hal ini benar lagi (versi terbaru memiliki binari Windows di PyPI dan tidak mendukung Py3K).
alastair

4
@ Jean-PaulCalderone FWIW, versi terbaru dari netifaces melakukan dukungan IPv6 pada Windows.
alastair

47

Metode Socket API

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

Kerugian:

  • Bukan lintas platform.
  • Membutuhkan lebih banyak kode cadangan, terkait dengan keberadaan alamat tertentu di internet
  • Ini juga tidak akan berfungsi jika Anda berada di belakang NAT
  • Mungkin membuat koneksi UDP, tidak terlepas dari ketersediaan DNS (biasanya ISP) (lihat jawaban lain untuk ide-ide seperti menggunakan 8.8.8.8: server Google (kebetulan juga DNS))
  • Pastikan Anda membuat alamat tujuan tidak dapat dijangkau, seperti alamat IP numerik yang dijamin tidak akan digunakan. JANGAN gunakan beberapa domain seperti fakesubdomain.google.com atau somefakewebsite.com; Anda masih akan melakukan spamming pada pihak tersebut (sekarang atau di masa depan), dan juga meng-spam kotak jaringan Anda sendiri dalam prosesnya.

Metode reflektor

(Harap perhatikan bahwa ini tidak menjawab pertanyaan OP tentang alamat IP lokal, mis. 192.168 ...; ini memberi Anda alamat IP publik Anda, yang mungkin lebih diinginkan tergantung pada kasus penggunaan.)

Anda dapat menanyakan beberapa situs seperti whatismyip.com (tetapi dengan API), seperti:

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)

atau jika menggunakan 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)

Keuntungan:

  • Salah satu kelebihan dari metode ini adalah cross-platform
  • Ini bekerja dari belakang NAT jelek (mis. Router rumah Anda).

Kerugian (dan solusi):

  • Memerlukan situs web ini untuk beroperasi, formatnya tidak berubah (hampir pasti tidak), dan server DNS Anda berfungsi. Satu dapat mengurangi masalah ini dengan juga meminta reflektor alamat IP pihak ketiga lainnya jika terjadi kegagalan.
  • Kemungkinan vektor serangan jika Anda tidak meminta beberapa reflektor (untuk mencegah reflektor yang terganggu memberi tahu Anda bahwa alamat Anda bukan sesuatu), atau jika Anda tidak menggunakan HTTPS (untuk mencegah serangan man-in-the-middle berpura-pura menjadi server)

sunting : Meskipun pada awalnya saya pikir metode ini benar-benar buruk (kecuali jika Anda menggunakan banyak fallback, kode mungkin tidak relevan bertahun-tahun dari sekarang), itu menimbulkan pertanyaan "apa itu internet?". Komputer mungkin memiliki banyak antarmuka yang menunjuk ke banyak jaringan yang berbeda. Untuk deskripsi topik yang lebih menyeluruh, google forgateways and routes . Komputer mungkin dapat mengakses jaringan internal melalui gateway internal, atau mengakses web di seluruh dunia melalui gateway misalnya router (biasanya casing). Alamat IP lokal yang ditanyakan OP hanya didefinisikan dengan baik sehubungan dengan satu lapisan tautan, jadi Anda harus menentukan bahwa ("apakah itu kartu jaringan, atau kabel ethernet, yang sedang kita bicarakan?") . Mungkin ada beberapa jawaban non-unik untuk pertanyaan ini sebagaimana diajukan. Namun alamat IP global di web di seluruh dunia mungkin didefinisikan dengan baik (tanpa adanya fragmentasi jaringan besar-besaran): mungkin jalur balik melalui gateway yang dapat mengakses TLD.


Ini akan mengembalikan alamat LAN-wide Anda jika Anda berada di belakang NAT. Jika Anda terhubung ke Internet, Anda dapat terhubung ke layanan web yang mengembalikan salah satu alamat IP publik Anda.
phihag

Itu tidak membuat koneksi TCP karena itu membuat koneksi UDP.
Anuj Gupta

2
Sebagai alternatif dalam versi API socket, ganti s.connect (('INSERT BEBERAPA TARGET WEBSITE.com', 0)) dengan s.setsockopt (socket.SOL_SOCKET, socket.SO_BROADCAST, 1); s.connect ((<< broadcast> ', 0)) untuk menghindari pencarian DNS. (Saya kira mungkin ada masalah dengan siaran jika ada firewall)
dlm

45

Jika komputer memiliki rute ke Internet, ini akan selalu berfungsi untuk mendapatkan alamat ip lokal yang disukai, bahkan jika / etc / hosts tidak diatur dengan benar.

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]

bagaimana cara kerjanya? , 8.8.8.8apakah server google dns dapatkah kita melakukannya dengan server dns lokal?
Ciasto piekarz

39

Di 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'
>>> 

Jadi ini secara efektif membuka soket yang tidak melakukan apa-apa dan Anda memeriksa data mentah tentang soket itu untuk mendapatkan IP lokal?
Dave

1
Soket dibuka untuk membuat fd berkomunikasi dengan kernel (via ioctl). Soket tidak terikat antarmuka yang Anda inginkan addr info tentang - itu hanya mekanisme komunikasi antara userspace dan kernel. en.wikipedia.org/wiki/Ioctl lxr.free-electrons.com/source/net/socket.c
tMC

2
Bekerja pada Python3 dengan satu modifikasi: struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)harus diganti denganstruct.pack('16sH14s', iface.encode('utf-8'), socket.AF_INET, b'\x00'*14)
pepoluan

1
@ChristianFischer ioctladalah antarmuka lama yang saya tidak percaya mendukung IPv6 dan kemungkinan tidak akan pernah. Saya pikir cara yang 'Benar' adalah melalui Netlink yang tidak terlalu mudah dengan Python. Saya pikir libc harus memiliki fungsi getifaddrsyang dapat diakses melalui ctypesmodul python yang dapat berfungsi - man7.org/linux/man-pages/man3/getifaddrs.3.html
tMC

1
@Maddy ioctl adalah antarmuka lama yang saya rasa tidak mendukung IPv6 dan sepertinya tidak akan pernah. Saya pikir cara yang 'Benar' adalah melalui Netlink yang tidak terlalu mudah dengan Python. Saya pikir libc harus memiliki fungsi getifaddrs yang dapat diakses melalui modul pytons ctypes yang dapat berfungsi - man7.org/linux/man-pages/man3/getifaddrs.3.html
tMC

27

Saya menggunakan modul berikut:

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

Diuji dengan windows dan linux (dan tidak memerlukan modul tambahan untuk mereka) yang dimaksudkan untuk digunakan pada sistem yang berada dalam LAN berbasis IPv4 tunggal.

Daftar tetap nama antarmuka tidak berfungsi untuk versi linux terbaru, yang telah mengadopsi perubahan systemd v197 tentang nama antarmuka yang dapat diprediksi seperti yang ditunjukkan oleh Alexander . Dalam kasus seperti itu, Anda perlu mengganti daftar secara manual dengan nama antarmuka pada sistem Anda, atau menggunakan solusi lain seperti netifaces .


2
Ini tidak kompatibel dengan nama antarmuka Linux yang baru dapat diprediksi, seperti enp0s25. Untuk info lebih lanjut, lihat wiki.archlinux.org/index.php/Network_Configuration#Device_names
Alexander

2
Saya menggunakan python 3.4 dan bagian 'struct.pack (...)' perlu diubah menjadi 'struct.pack (' 256s ', byte (ifname [: 15],' utf-8 '))'. Lihat pertanyaan ini: stackoverflow.com/q/27391167/76010
Bakanekobrain

1
pada Raspbian w / Python 2.7.3 - byte () tidak suka argumen ke-2. Tetapi ini berhasil: struct.pack('256s', bytes(ifname[:15]))
colm.anseo

24

Saya menggunakan ini di mesin ubuntu saya:

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

Ini tidak berhasil.


Bagus dan sederhana. Bekerja di Linux Amazon AMI juga, tetapi hanya jika saya root. Kalau tidak, saya akan mendapatkan kesalahan: 'sh: ifconfig: command not found'
IgorGanapolsky

Jadi Anda harus menggunakan "/ sbin / ifconfig" seperti kata gavaletz. Ini juga berfungsi pada Red Hat 4.1.2-48.
IgorGanapolsky

7
Sudah usang sejak 2.6. Gunakan modul subproses untuk menjalankan perintah.
Colin Dunklau

5
Dan ifconfig sudah usang juga. Gunakan iproute2.
Helmut Grohne

Dapatkan semua ips: import sh; [ip.split () [1] [5:] untuk filter ip in (lambda x: 'inet addr' dalam x, sh.ifconfig (). split ("\ n"))]
Gabriel Littman

20

Jika Anda tidak ingin menggunakan paket eksternal dan tidak ingin bergantung pada server Internet luar, ini mungkin membantu. Ini adalah contoh kode yang saya temukan di Google Code Search dan dimodifikasi untuk mengembalikan informasi yang diperlukan:

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

Pemakaian:

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

Karena bergantung windll, ini hanya akan berfungsi pada Windows.


Solusi satu liner di atas umumnya bekerja di windows. Itu Linux yang menjadi masalah.
ricree

14
+1 Teknik ini setidaknya berupaya mengembalikan semua alamat pada mesin.
Jason R. Coombs

1
Skrip ini gagal pada mesin saya setelah mengembalikan alamat pertama. Kesalahannya adalah "AttributeError: objek 'LP_IP_ADDR_STRING' tidak memiliki atribut 'ipAddress'" Saya menduga itu ada hubungannya dengan alamat IPv6.
Jason R. Coombs

1
Ternyata masalahnya adalah bahwa untuk apa pun selain alamat IP pertama, adNode tidak dereferensi. Tambahkan satu baris lagi ke contoh di loop sementara dan itu berfungsi untuk saya: adNode = adNode.contents
Jason R. Coombs

18

Pada Debian (diuji) dan saya curiga kebanyakan Linux ..

import commands

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

Pada MS Windows (diuji)

import socket

socket.gethostbyname(socket.gethostname())

1
Tidak berfungsi di macOS:hostname: illegal option -- I\nusage: hostname [-fs] [name-of-host]
Derek 朕 會 功夫

18

Versi yang saya tidak percaya yang telah diposting. Saya diuji dengan python 2.7 di Ubuntu 12.04.

Menemukan solusi ini di: 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])

Hasil Contoh:

>>> get_ip_address('eth0')
'38.113.228.130'

2
Bekerja pada Python3, Ubuntu 18.04; String harus berupa byte: >>> socket.inet_ntoa (fcntl.ioctl (s.fileno (), 0x8915, struct.pack ('256s', 'enp0s31f6' [: 15] .encode ('utf-8')) )) [20:24]) '192.168.1.1'
cessor

12

Variasi pada jawaban ninjagecko. Ini harus bekerja pada LAN apa pun yang memungkinkan siaran UDP dan tidak memerlukan akses ke alamat di LAN atau 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())

Tunggu, bagaimana <broadcast>nama host yang valid? !! Berapa banyak dari nama host verbal ini yang valid?
Dev Aggarwal

Ini berfungsi untuk saya di Ubuntu 20.04 - mendapatkan 192.168.0.24 bukan 127.0.0.1
Lewis Morris

8

Saya khawatir tidak ada platform independen yang bagus untuk melakukan hal ini selain menghubungkan ke komputer lain dan mengirimkannya ke alamat IP Anda. Misalnya: findmyipaddress . Perhatikan bahwa ini tidak akan berfungsi jika Anda memerlukan alamat IP yang berada di belakang NAT kecuali komputer yang Anda sambungkan berada di belakang NAT.

Inilah salah satu solusi yang berfungsi di Linux: dapatkan alamat IP yang terkait dengan antarmuka jaringan .


8

Salah satu cara sederhana untuk menghasilkan output "bersih" melalui utilitas baris perintah:

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

Ini akan menampilkan semua alamat IPv4 pada sistem.


1
Itu tidak akan menampilkan semua alamat IPv4, karena ifconfig hanya memberi tahu Anda tentang yang utama. Anda perlu menggunakan "ip" dari iproute2 untuk melihat semua alamat.
Helmut Grohne

Itu banyak sekali pertanyaan untuk menanyakan pustaka standar ... Juga, parsing ifconfig tidak portabel dan bahkan tidak akan bekerja dengan andal pada satu mesin.
Dominik George

7

FYI Saya dapat memverifikasi bahwa metode:

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

Bekerja di OS X (10.6,10.5), Windows XP, dan pada server departemen RHEL yang dikelola dengan baik. Itu tidak bekerja pada CentOS VM yang sangat minim yang baru saja saya lakukan peretasan kernel. Jadi untuk contoh itu Anda bisa memeriksa alamat 127.0.0.1 dan dalam hal ini lakukan hal berikut:

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

Dan kemudian parsing alamat ip dari output. Perlu dicatat bahwa ifconfig tidak dalam PATH pengguna normal secara default dan itulah sebabnya saya memberikan path lengkap dalam perintah. Saya harap ini membantu.


7

Ini adalah varian dari jawaban UnkwnTech - ini menyediakan get_local_addr()fungsi, yang mengembalikan alamat IP LAN utama dari host. Saya mempostingnya karena ini menambahkan sejumlah hal: dukungan ipv6, penanganan kesalahan, mengabaikan localhost / linklocal addrs, dan menggunakan addr TESTNET (rfc5737) untuk terhubung.

# 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

Saya pikir ini bisa menjadi jawaban yang sangat bagus .. tetapi selalu kembaliNone
Jamie Lindsey

@ JamieLindsey Apakah Anda memiliki detail tentang OS Anda, konfigurasi jaringan? Juga, apa yang sesuatu seperti get_local_addr(remove="www.google.com")pengembalian? Logging yang socket.errordilempar oleh _get_local_addr () mungkin membantu diagnosa.
Eli Collins

6
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]

1
Hmm ... pada server dengan dua NIC ini memberikan salah satu alamat IP yang ditugaskan, tetapi diulang tiga kali. Di laptop saya itu memberikan '127.0.1.1' (diulang tiga kali ...) ...
bryn

Memberiku ['fe80::34e8:fe19:1459:2cde%22','fe80::d528:99fb:d572:e289%12', '192.168.56.1', '192.168.1.2']di desktop Windows.
Nakilon

5

Ini akan berfungsi pada sebagian besar kotak linux:

import socket, subprocess, re
def get_ipv4_address():
    """
    Returns IP address(es) of current machine.
    :return:
    """
    p = subprocess.Popen(["ifconfig"], stdout=subprocess.PIPE)
    ifc_resp = p.communicate()
    patt = re.compile(r'inet\s*\w*\S*:\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
    resp = patt.findall(ifc_resp[0])
    print resp

get_ipv4_address()

5

Jawaban ini adalah upaya pribadi saya untuk memecahkan masalah mendapatkan IP LAN, karena socket.gethostbyname(socket.gethostname())juga mengembalikan 127.0.0.1. Metode ini tidak memerlukan Internet hanya koneksi LAN. Kode untuk Python 3.x tetapi dapat dengan mudah dikonversi untuk 2.x. Menggunakan Siaran UDP:

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

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

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

if __name__ == '__main__':
    print(get_local_ip())

1
Apa yang terjadi jika Anda menjalankan ini secara bersamaan pada dua mesin di jaringan yang sama? Saat Anda menyiarkan pesan Anda di jaringan, semua mesin akan menerima 'Apa alamat IP LAN saya. Udp_listening_server Anda dapat membalas 'alamat IP Anda adalah xxx' untuk pesan tersebut.
Nicolas Defranoux

4

127.0.1.1 adalah alamat IP asli Anda. Secara umum, komputer dapat memiliki sejumlah alamat IP. Anda dapat memfilternya untuk jaringan pribadi - 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12 dan 192.168.0.0/16.

Namun, tidak ada cara lintas platform untuk mendapatkan semua alamat IP. Di Linux, Anda dapat menggunakan SIOCGIFCONFioctl.


2
Dia berarti IP yang terlihat secara eksternal. Rentang 127. *. *. * Biasanya merujuk ke localhost atau jaringan internal, yang jelas bukan yang diinginkannya.
Cerin

4

Perbaikan versi perintah yang menggunakan perintah IP, dan mengembalikan alamat IPv4 dan IPv6:

import commands,re,socket

#A generator that returns stripped lines of output from "ip address show"
iplines=(line.strip() for line in commands.getoutput("ip address show").split('\n'))

#Turn that into a list of IPv4 and IPv6 address/mask strings
addresses1=reduce(lambda a,v:a+v,(re.findall(r"inet ([\d.]+/\d+)",line)+re.findall(r"inet6 ([\:\da-f]+/\d+)",line) for line in iplines))
#addresses1 now looks like ['127.0.0.1/8', '::1/128', '10.160.114.60/23', 'fe80::1031:3fff:fe00:6dce/64']

#Get a list of IPv4 addresses as (IPstring,subnetsize) tuples
ipv4s=[(ip,int(subnet)) for ip,subnet in (addr.split('/') for addr in addresses1 if '.' in addr)]
#ipv4s now looks like [('127.0.0.1', 8), ('10.160.114.60', 23)]

#Get IPv6 addresses
ipv6s=[(ip,int(subnet)) for ip,subnet in (addr.split('/') for addr in addresses1 if ':' in addr)]

4

Anda dapat menggunakan perintah "ip route" di GNU / Linux untuk mengetahui alamat IP Anda saat ini.

Ini menunjukkan IP yang diberikan ke antarmuka oleh server DHCP yang berjalan pada router / modem. Biasanya "192.168.1.1/24" adalah IP untuk jaringan lokal di mana "24" berarti kisaran alamat IP posible yang diberikan oleh server DHCP dalam kisaran mask.

Berikut ini sebuah contoh: Perhatikan bahwa PyNotify hanyalah tambahan untuk menjelaskan maksud saya dan tidak diperlukan sama sekali

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

Keuntungannya adalah Anda tidak perlu menentukan antarmuka jaringan. Itu cukup berguna saat menjalankan server soket

Anda dapat menginstal PyNotify menggunakan easy_install atau bahkan Pip:

easy_install py-notify

atau

pip install py-notify

atau dalam skrip / penerjemah python

from pip import main

main(['install', 'py-notify'])

4

Jika Anda mencari alamat IPV4 yang berbeda dari alamat IP localhost Anda 127.0.0.1, berikut adalah potongan kode python yang rapi:

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

Yang juga dapat ditulis dalam satu baris:

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

Bahkan jika Anda menempatkan localhostdi /etc/hostname, kode masih akan memberikan alamat IP lokal Anda.


4

Untuk linux, Anda hanya dapat menggunakan check_outputsatu hostname -Iperintah sistem seperti:

from subprocess import check_output
check_output(['hostname', '-I'])

untuk googler, saya tahu pertanyaannya adalah untuk solusi lintas platform
Kasper Skytte Andersen

3

Catatan: Ini tidak menggunakan perpustakaan standar, tetapi cukup sederhana.

$ pip pasang pif

from pif import get_public_ip
get_public_ip()

3
pertanyaannya adalah tentang menemukan IP menggunakan stdlib
Alexandru Chirila

3

netifaces tersedia melalui pip dan easy_install. (Saya tahu, itu tidak ada di pangkalan, tetapi ini mungkin layak untuk dipasang.)

netifaces memang memiliki beberapa keanehan di seluruh platform:

  • Antarmuka localhost / loop-back mungkin tidak selalu termasuk (Cygwin).
  • Alamat terdaftar per-protokol (misalnya, IPv4, IPv6) dan protokol terdaftar per-antarmuka. Pada beberapa sistem (Linux) setiap pasangan protokol-antarmuka memiliki antarmuka terkait sendiri (menggunakan notasi interface_name) sementara pada sistem lain (Windows) satu antarmuka akan memiliki daftar alamat untuk setiap protokol. Dalam kedua kasus ada daftar protokol, tetapi mungkin hanya mengandung satu elemen.

Berikut beberapa kode netifaces untuk dimainkan:

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.

Kode di atas tidak memetakan alamat kembali ke nama antarmuka (berguna untuk menghasilkan aturan ebtables / iptables dengan cepat). Jadi, inilah versi yang menyimpan informasi di atas dengan nama antarmuka dalam sebuah 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]

Dan, tidak, saya tidak jatuh cinta dengan pemahaman daftar. Hanya cara otak saya bekerja hari ini.

Cuplikan berikut akan mencetak semuanya:

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)

Nikmati!


netifaces benar-benar memudahkan kehidupan saat menangani masalah ini.
Drake Guan

3

Versi Python 3.4 menggunakan paket asyncio yang baru diperkenalkan.

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

Ini didasarkan pada jawaban UnkwnTech yang luar biasa .


3

Untuk mendapatkan alamat ip, Anda dapat menggunakan perintah shell secara langsung dengan 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()
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.