Finding local IP addresses using Python's stdlib

后端 未结 30 2438
北恋
北恋 2020-11-21 23:54

How can I find local IP addresses (i.e. 192.168.x.x or 10.0.x.x) in Python platform independently and using only the standard library?

相关标签:
30条回答
  • 2020-11-22 00:24

    FYI I can verify that the method:

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

    Works in OS X (10.6,10.5), Windows XP, and on a well administered RHEL department server. It did not work on a very minimal CentOS VM that I just do some kernel hacking on. So for that instance you can just check for a 127.0.0.1 address and in that case do the following:

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

    And then parse the ip address from the output. It should be noted that ifconfig is not in a normal user's PATH by default and that is why I give the full path in the command. I hope this helps.

    0 讨论(0)
  • 2020-11-22 00:26

    You can use the netifaces module. Just type:

    pip install netifaces
    

    in your command shell and it will install itself on default Python installation.

    Then you can use it like this:

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

    On my computer it printed:

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

    Author of this module claims it should work on Windows, UNIX and Mac OS X.

    0 讨论(0)
  • 2020-11-22 00:27

    Socket API method

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

    Downsides:

    • Not cross-platform.
    • Requires more fallback code, tied to existence of particular addresses on the internet
    • This will also not work if you're behind a NAT
    • Probably creates a UDP connection, not independent of (usually ISP's) DNS availability (see other answers for ideas like using 8.8.8.8: Google's (coincidentally also DNS) server)
    • Make sure you make the destination address UNREACHABLE, like a numeric IP address that is spec-guaranteed to be unused. Do NOT use some domain like fakesubdomain.google.com or somefakewebsite.com; you'll still be spamming that party (now or in the future), and spamming your own network boxes as well in the process.

    Reflector method

    (Do note that this does not answer the OP's question of the local IP address, e.g. 192.168...; it gives you your public IP address, which might be more desirable depending on use case.)

    You can query some site like whatismyip.com (but with an API), such as:

    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)
    

    or if using 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)
    

    Advantages:

    • One upside of this method is it's cross-platform
    • It works from behind ugly NATs (e.g. your home router).

    Disadvantages (and workarounds):

    • Requires this website to be up, the format to not change (almost certainly won't), and your DNS servers to be working. One can mitigate this issue by also querying other third-party IP address reflectors in case of failure.
    • Possible attack vector if you don't query multiple reflectors (to prevent a compromised reflector from telling you that your address is something it's not), or if you don't use HTTPS (to prevent a man-in-the-middle attack pretending to be the server)

    edit: Though initially I thought these methods were really bad (unless you use many fallbacks, the code may be irrelevant many years from now), it does pose the question "what is the internet?". A computer may have many interfaces pointing to many different networks. For a more thorough description of the topic, google for gateways and routes. A computer may be able to access an internal network via an internal gateway, or access the world-wide web via a gateway on for example a router (usually the case). The local IP address that the OP asks about is only well-defined with respect to a single link layer, so you have to specify that ("is it the network card, or the ethernet cable, which we're talking about?"). There may be multiple non-unique answers to this question as posed. However the global IP address on the world-wide web is probably well-defined (in the absence of massive network fragmentation): probably the return path via the gateway which can access the TLDs.

    0 讨论(0)
  • 2020-11-22 00:27

    This is a variant of UnkwnTech's answer -- it provides a get_local_addr() function, which returns the primary LAN ip address of the host. I'm posting it because this adds a number of things: ipv6 support, error handling, ignoring localhost/linklocal addrs, and uses a TESTNET addr (rfc5737) to connect to.

    # 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
    
    0 讨论(0)
  • 2020-11-22 00:28

    I use this on my ubuntu machines:

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

    This doesn't work.

    0 讨论(0)
  • 2020-11-22 00:29

    Well you can use the command "ip route" on GNU/Linux to know your current IP address.

    This shows the IP given to the interface by the DHCP server running on the router/modem. Usually "192.168.1.1/24" is the IP for local network where "24" means the range of posible IP addresses given by the DHCP server within the mask range.

    Here's an example: Note that PyNotify is just an addition to get my point straight and is not required at all

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

    The advantage of this is that you don't need to specify the network interface. That's pretty useful when running a socket server

    You can install PyNotify using easy_install or even Pip:

    easy_install py-notify
    

    or

    pip install py-notify
    

    or within python script/interpreter

    from pip import main
    
    main(['install', 'py-notify'])
    
    0 讨论(0)
提交回复
热议问题