173

I want to see if I can access an online API, but for that, I need to have Internet access.

How can I see if there's a connection available and active using Python?

MendelG
  • 14,885
  • 4
  • 25
  • 52
aF.
  • 64,980
  • 43
  • 135
  • 198
  • if you're in python, it will almost certainly throw an exception in the event of a connection failure or timeout. You can either try/catch around that, or just let it surface. – jdizzle Sep 21 '10 at 20:47
  • 1
    @Triptych: I hope that was a joke, because it doesn't work – inspectorG4dget Sep 21 '10 at 21:48
  • 2
    @inspectorG4dget: `easy_install system_of_tubes` – S.Lott Sep 21 '10 at 23:09
  • 2
    @aF: Look at Unutbu's solution. It shows (in the fastest possible way) how to check if you are able to access google.com. If your problem concerns accessing the API, then why not try accessing it, setting timeout=1? That will tell you exactly if the API that you want to access is accessible. Once you know that, then you can go ahead and access the API or wait for a time when you are able to access it. – inspectorG4dget Sep 22 '10 at 01:56
  • Like I said, I just needed a simple thing like unutbu said. You don't need to make such a fuss with this.. – aF. Sep 22 '10 at 08:54
  • @aF: Your question doesn't make sense. It's not a "fuss". You're asking for something that cannot possibly exist. You can't check for things which can't exist (like a "connection available and active"). You can only make the query which works or fails. Since the question cannot be answered (because such a think cannot exist) it might be good to consider updating the question so that it does make sense and reflects something you could **really** implement. – S.Lott Sep 22 '10 at 14:56
  • One could suggest a particular request which could be made, and tested for success, as proxy to the original intent of the question. For example, you want to skip some tests if there is no wifi or ethernet connection? That's a perfectly reasonable desire. In which case, a good request might be to ping google, as suggested above, or to try something more local, like find out your current gateway IP and try to ping that? Does that sound reasonable? – Jonathan Hartley Jun 29 '11 at 06:12

22 Answers22

151

If we can connect to some Internet server, then we indeed have connectivity. However, for the fastest and most reliable approach, all solutions should comply with the following requirements, at the very least:

  • Avoid DNS resolution (we will need an IP that is well-known and guaranteed to be available for most of the time)
  • Avoid application layer connections (connecting to an HTTP/FTP/IMAP service)
  • Avoid calls to external utilities from Python or other language of choice (we need to come up with a language-agnostic solution that doesn't rely on third-party solutions)

To comply with these, one approach could be to, check if one of the Google's public DNS servers is reachable. The IPv4 addresses for these servers are 8.8.8.8 and 8.8.4.4. We can try connecting to any of them.

A quick Nmap of the host 8.8.8.8 gave below result:

$ sudo nmap 8.8.8.8

Starting Nmap 6.40 ( http://nmap.org ) at 2015-10-14 10:17 IST
Nmap scan report for google-public-dns-a.google.com (8.8.8.8)
Host is up (0.0048s latency).
Not shown: 999 filtered ports
PORT   STATE SERVICE
53/tcp open  domain

Nmap done: 1 IP address (1 host up) scanned in 23.81 seconds

As we can see, 53/tcp is open and non-filtered. If you are a non-root user, remember to use sudo or the -Pn argument for Nmap to send crafted probe packets and determine if a host is up.

Before we try with Python, let's test connectivity using an external tool, Netcat:

$ nc 8.8.8.8 53 -zv
Connection to 8.8.8.8 53 port [tcp/domain] succeeded!

Netcat confirms that we can reach 8.8.8.8 over 53/tcp. Now we can set up a socket connection to 8.8.8.8:53/tcp in Python to check connection:

import socket

def internet(host="8.8.8.8", port=53, timeout=3):
    """
    Host: 8.8.8.8 (google-public-dns-a.google.com)
    OpenPort: 53/tcp
    Service: domain (DNS/TCP)
    """
    try:
        socket.setdefaulttimeout(timeout)
        socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
        return True
    except socket.error as ex:
        print(ex)
        return False

internet()

Another approach could be to send a manually crafted DNS probe to one of these servers and wait for a response. But, I assume, it might prove slower in comparison due to packet drops, DNS resolution failure, etc. Please comment if you think otherwise.

UPDATE #4: This listing of public nameservers is a good reference for IPs to test against.

UPDATE #3: Tested again after the exception handling change:

defos.py
True
00:00:00:00.410

iamaziz.py
True
00:00:00:00.240

ivelin.py
True
00:00:00:00.109

jaredb.py
True
00:00:00:00.520

kevinc.py
True
00:00:00:00.317

unutbu.py
True
00:00:00:00.436

7h3rAm.py
True
00:00:00:00.030

UPDATE #2: I did quick tests to identify the fastest and most generic implementation of all valid answers to this question. Here's the summary:

$ ls *.py | sort -n | xargs -I % sh -c 'echo %; ./timeit.sh %; echo'
defos.py
True
00:00:00:00.487

iamaziz.py
True
00:00:00:00.335

ivelin.py
True
00:00:00:00.105

jaredb.py
True
00:00:00:00.533

kevinc.py
True
00:00:00:00.295

unutbu.py
True
00:00:00:00.546

7h3rAm.py
True
00:00:00:00.032

And once more:

$ ls *.py | sort -n | xargs -I % sh -c 'echo %; ./timeit.sh %; echo'
defos.py
True
00:00:00:00.450

iamaziz.py
True
00:00:00:00.358

ivelin.py
True
00:00:00:00.099

jaredb.py
True
00:00:00:00.585

kevinc.py
True
00:00:00:00.492

unutbu.py
True
00:00:00:00.485

7h3rAm.py
True
00:00:00:00.035

True in the above output signifies that all these implementations from respective authors correctly identify connectivity to the Internet. Time is shown with milliseconds resolution.

UPDATE #1: Thanks to @theamk's comment, timeout is now an argument and initialized to 3s by default.

7h3rAm
  • 1,775
  • 2
  • 15
  • 17
  • 8
    This is the best answer, avoiding DNS resolution, and ironically, consulting Google's DNS service on port 53. – m3nda Dec 20 '15 at 09:59
  • kudos for the answer. Brilliant. Just a quick question, does google DNS server changes IP ? or its fixed ? – avimehenwal Feb 08 '16 at 11:54
  • 1
    @AviMehenwal Google provides this IP as part of its free to use DNS name resolution service. The IP is not supposed to change unless Google decides otherwise. I've been using it as an alternative DNS server for few years now without any issues. – 7h3rAm Apr 21 '16 at 15:11
  • @7h3rAm `except Exception as ex: pass` please don't do this __ever__. Especially not in a stackoverflow answer. – Lukas Juhrich Jun 09 '16 at 19:44
  • 1
    @Luke Thanks for pointing that out. I've updated answer to inform users about the exception and return False as a default answer. – 7h3rAm Jun 10 '16 at 06:10
  • Your first requirement seems counter-productive. If the high level purpose of this is to check for a working internet connection in order to know if you can access some other online server, then in virtually 100% of situations you'd probably want to know if DNS is working. Your second requirement is theoretically reasonable but in reality is extremely difficult to avoid. The *best* solution, though, really, would be to attempt to connect to whatever server you are interested in using whatever method you'll be using and see if it succeeds. Then you cover your goal precisely. – Jason C Nov 01 '16 at 16:28
  • Also btw, testing for TCP port 53 being open using 8.8.8.8 also violates your second requirement. DNS is an application level protocol, and even though you aren't making a full query, checking if its port is open *is* basically relying on an application-level protocol, in disguise. There's no other reason for port 53 to be open. And besides, if you're checking for access to a DNS server in the first place, then just do a normal DNS lookup with the DNS server the machine is actually using... it's more robust (instead of hard-coding an arbitrary DNS server IP) and basically the same method. – Jason C Nov 01 '16 at 16:32
  • In other words: This solution is silly. If you're checking for open DNS protocol ports on a hard-coded DNS server, just ... do a DNS query. It's the same thing. – Jason C Nov 01 '16 at 16:35
  • @JasonC Sure, you might not like it, but your comments don't justify why the answer is inaccurate and silly. – 7h3rAm Nov 04 '16 at 09:31
  • @JasonC Your idea that if DNS is working then virtually 100% of cases, internet connection is working is incorrect. Example: Recently a group of compromised IoT systems were used to DDoS DYN DNS servers. This caused a lot of their customers intermittent issues due to which they could not connect to popular sites. This however doesn't mean that those customers didn't had internet connectivity. They had internet access, just that their domains were not being resolved. DNS being down doesn't mean internet downtime. https://krebsonsecurity.com/2016/10/ddos-on-dyn-impacts-twitter-spotify-reddit/ – 7h3rAm Nov 04 '16 at 09:37
  • @JasonC Your best solution of connecting to whatever server using whatever method is unreliable. If the server goes down, you will be under the assumption that your internet is down. – 7h3rAm Nov 04 '16 at 09:40
  • 2
    @JasonC Connecting to TCP/53 is different than DNS connection over port 53. Going by your analogy, every TCP port that is used by an application will be classified as application level protocol connection. No, that's not true. Connecting over a TCP socket to a port IS NOT application level connection. I'm not doing a DNS lookup, rather just a half-TCP handshake. It's not the same thing as doing a full DNS lookup. – 7h3rAm Nov 04 '16 at 09:46
  • @JasonC BTW, 8.8.8.8 is not an arbitrary IP as you mentioned. It is quite popular and used widely. http://superuser.com/questions/424316/why-should-i-use-dns-8-8-8-8 – 7h3rAm Nov 04 '16 at 09:51
  • @7h3rAm Yeah, except no application connects to an online API by IP address. Nobody does that. You virtually always want DNS in a real situation. I'm not sure why you aren't grasping that checking for open port 53 is an application level protocol *in disguise*: Answer this: Why would a server have port 53 open? If you sampled random IP addresses and connected to port 53, what % of those would also be running DNS? Probably all. All you're doing is essentially making a DNS query but pretending you aren't. You relying on port 53 *is equivalent to you relying on it being a DNS server*. – Jason C Nov 04 '16 at 13:23
  • And yes, 8.8.8.8 is as arbitrary as google.com. It's pretty reliable, but it is still an assumption. If you are checking for an internet connection so you can connect to your server, and your server is down, who cares if you think the whole internet is down? On the other hand, if Google DNS is down but your server isn't, it would be silly to prevent a connection. – Jason C Nov 04 '16 at 13:26
  • @JasonC I agree with your point that a majority of port 53 applications would be DNS. However, you should try to understand that a port and the application bound to it are separate entities. Please allow me to clarify: – 7h3rAm Nov 07 '16 at 05:38
  • 1
    1. TCP/IP uses a stacked architecture, where a higher layer uses services of lower layers. In this case, DNS will use UDP and IP for its (forward or reverse) name resolution operations. – 7h3rAm Nov 07 '16 at 05:38
  • 1
    2. While setting up a raw socket connection, you have to individually craft packets for each layer and embed them as payload for lower layer. In this case, UDP will have DNS packet as it data. If you wish to perform DNS zone transfers, TCP will be used for encapsulating DNS data. – 7h3rAm Nov 07 '16 at 05:39
  • 1
    3. Port 53 is just a port and it doesn't matter what application is using it because it has it own handler process that replies to packets received transport layer. When I setup a new TCP connection, the TCP/IP stack on my host will handle it and complete the 3-way handshake with the server. Once that is done, server would expect me to send a request over this newly setup TCP connection. Until I submit some data over this connection, transport layer handler on the server has nothing to submit to the application layer handler. – 7h3rAm Nov 07 '16 at 05:39
  • 1
    4. If you look closely at my answer, you will see that I am setting up an AF_INET+SOCK_STREAM socket which implies a TCP/IP connection (to host 8.8.8.8 and port 53 in this case). Once the TCP handshake is complete and a connection is setup, I simply return True and exit, which will cause a teardown of this newly setup connection. This is what setting up a raw TCP connection implies. – 7h3rAm Nov 07 '16 at 05:39
  • 1
    5. I am leveraging the fact that if a new TCP connection between my system and some server is setup, it implies that both these systems are connected over a reliable connection medium. All that's left now is to select a good server to try to connect to and a port on that server which will be guaranteed to be open for most of the time. Like you mentioned in you previous comments, DNS servers are the backbone of the Internet and as such most reliable source of testing connectivity. You could very well use any other host/port mapping as per your liking but the idea would still remain the same. – 7h3rAm Nov 07 '16 at 05:39
  • 1
    6. Most importantly, please understand that I have never sent any data over this TCP connection, which means that whatever application is bound to TCP port 53 on 8.8.8.8 never receives any data to process. Essentially, my request never goes beyond the TCP handler on 8.8.8.8 and as such the application layer never comes into picture. This is what @erm3nda also mentioned in the first comment on my answer. – 7h3rAm Nov 07 '16 at 05:40
  • If you run my program and take a packet capture you will find nothing related to DNS in there. I have uploaded a pcap for your reference [here](http://www.pcapr.net/view/ankurt.20/2016/10/0/21/7h3rAm.pcap.html). If you still feel that the answer is silly, well, I have nothing else to say. Peace. – 7h3rAm Nov 07 '16 at 05:41
  • The only real way to check and say "i am connected" is to connect. About @JasonC comments, wanna say that there's a little point on what you say to check "what you need to" instead "another kind of test", but in case your http query fails, u would like to know if internet is working to discard level application fails, so opening a socket to "whatever you can" is good enough to say "we have internet". About using 8.8.8.8, well, as DNS provider they won't change it's easy ip for nothing. Btw, a real test should include at leat 3 DNS provider ip's for result consistency. Relax. – m3nda Nov 07 '16 at 12:37
  • @7h3rAm Why would port 53 be open on a machine? Because it's a DNS server. You're looking for DNS services. Just because you don't complete the query doesn't mean you aren't relying on the server being a DNS server. And that's why you break your second rule while pretending you don't. And if you're port testing DNS servers for a DNS port your first rule becomes unnecessary as well. *Your method checks if a DNS server is up.* Just because you don't do a full lookup doesn't mean you're actually avoiding DNS. There's no reason, then, not to just actually continue and do a real DNS lookup. – Jason C Nov 07 '16 at 16:40
  • 1
    @JasonC No need for full lookup for two reasons: a. Getting a reply from the server implies successful connection and satisfies the requirements of this question. There is no need to send additional packets for name lookup. b. Because name lookups happen over UDP and they are unreliable. – 7h3rAm Nov 07 '16 at 17:32
  • @7h3rAm Why would you need to send extra data for a name lookup? Just connect to "Google.com" or your API server by name. The lookup is implied. It'd be silly to construct a lookup manually. And it uses the currently configured name server instead of 8.8.8.8 (which makes your solution doubly wrong btw, if 8.8.8.8 is up but the locally configured dns server isnt then you falsely indicate a usable internet connection). – Jason C Nov 08 '16 at 16:52
  • @JasonC *if 8.8.8.8 is up but the locally configured dns server isn't then you falsely indicate a usable internet connection* - really :) well, enjoy your Internet downtime then. – 7h3rAm Nov 08 '16 at 18:11
  • 1
    @JasonC "Why would port 53 be open on a machine? Because it's a DNS server". Falacy, it's a standard not a RULE. Second, a DNS lookup requires by design "lookup" for a domain name (i guess you know what dns means") so if you don't send data (the domain name) and just try to open a socket to an ip you're just contacting and what's under that service don't really matter. That test over 8.8.8.8 dns server port will be same as doing it to any of those ip (http://stackoverflow.com/a/40206938/2480481), but 8.8.8.8 seems easy to remember. U guys broke my mouse scroll key ... – m3nda Nov 16 '16 at 16:25
  • @erm3nda Except you're connecting to 8.8.8.8, which you've explicitly chosen because it's a DNS server, and thus you've picked port 53, which you've explicitly chosen because you're relying on the service being provided. So if you'd like, rephrase the question to "Why would port 53 be open on 8.8.8.8? Because it's a DNS server." Everything about the IP address and TCP port selection criteria here revolves around "it's a DNS server". This is complete reliance on that assumption, and doing a basic lookup of a name instead *adds no additional assumptions that this answer does not already make*. – Jason C Nov 16 '16 at 19:48
  • 4
    Shouldn't we call `close()` on the socket? – kbridge4096 Jul 05 '17 at 05:53
  • To monitor connectivity, I am using your code to hit 8.8.8.8 every second. Is this an anti-social thing to do? Could Google object? – user258279 Feb 09 '18 at 03:46
  • @user258279 could be, I am not sure. Try reaching out to Google and let us know what they recommend. I will also suggest following steps: 1. Reduce frequency to 15 mins or so, if possible. 1s is too noisy. 2. And, update code to accept a list of IPs. Select one randomly everytime and check with it instead of a fixed IP. – 7h3rAm Feb 10 '18 at 05:22
  • This code is not compatible with Python 3. Print should be formatted as `print()`, and the exception didn't have an attribute `message` – Stevoisiak Jun 21 '19 at 17:51
  • you shouldn't set socket.setdefaulttimeout(timeout) since it's a global setting. Better would be to use socket.settimeout(). (see this question: https://stackoverflow.com/questions/57167357) – Frode Akselsen Dec 12 '19 at 21:31
  • Won't checking internet connection by accessing port `53` on `8.8.8.8` every 5 seconds lead possibly to getting client's `IP` blocked by Google (get marked as abuse)? – W.M. Jan 01 '20 at 19:43
  • 2
    Thanks again for this answer. Turned this into a windows taskbar icon: https://gitlab.com/room150/always-on-indicator – snitch182 Feb 10 '20 at 14:46
151

Perhaps you could use something like this:

from urllib import request

def internet_on():
    try:
        request.urlopen('http://216.58.192.142', timeout=1)
        return True
    except request.URLError as err: 
        return False

For Python 2.x replace the import statement by import urllib2 as request:

Currently, 8.8.8.8 is one of the IP addresses from Google. Change http://8.8.8.8 to whatever site can be expected to respond quickly.

This fixed IP will not map to google.com forever. So this code is not robust -- it will need constant maintenance to keep it working.

The reason why the code above uses a fixed IP address instead of fully qualified domain name (FQDN) is because a FQDN would require a DNS lookup. When the machine does not have a working internet connection, the DNS lookup itself may block the call to urllib_request.urlopen for more than a second. Thanks to @rzetterberg for pointing this out.


If the fixed IP address above is not working, you can find a current IP address for google.com (on unix) by running

% dig google.com  +trace 
...
google.com.     300 IN  A   216.58.192.142
Joooeey
  • 3,394
  • 1
  • 35
  • 49
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • 1
    Just a note on the "the call to `urlopen` will not take much longer than 1 second even if the internet is not "on"."-quote. This is not true if the url supplied is invalid, then the DNS lookup will block. It is only true for the actual connection to the web-server. The simplest way to avoid this DNS lookup block is to use the IP-adress instead, then it's guaranteed to only take 1 second :) – rzetterberg Dec 31 '11 at 11:56
  • 9
    THIS NO LONGER WORKS. As of Sep 2013, http://74.125.113.99 times out after long time, so this function will always return False. I suppose Google has changed their network is set up. – theamk Sep 18 '13 at 12:00
  • @theamk: Thanks for the heads-up. I've changed it to http://74.125.228.100 which should work for now. – unutbu Sep 18 '13 at 12:20
  • 4
    Now the rest is simple. Google for "74.125.113.99 urllib". It will return thousands of open-source projects which incorporated the incorrect code. (for me, just the first page contains at least 5: nvpy, sweekychebot, malteseDict, upy, checkConnection). They are all broken now. – theamk Sep 19 '13 at 19:02
  • 4
    If it is not clear, I DO NOT RECOMMEND using this method. Old IP was good for less than 3 years. The new IP works today. Put it in your project, and it might mysteriously break in less than 3 years, again. – theamk Sep 19 '13 at 19:04
  • Fair enough. Then how do you propose to check if a machine has a network connection? – unutbu Sep 19 '13 at 19:06
  • The best way is not to do it (see Tomasz Wysocki's answer below). If you do it, use hostname (I double google.com will disappear in 2 years), and long timeout (1 second is way too short. I was at starbucks yesterday, and had ping of 2.5 seconds) – theamk Sep 20 '13 at 17:56
  • Using a fully qualified domain name results in a 30--40 second delay regardless of the `timeout` value when there is no network connection. If one had to use that solution once a day for 3 years, that would amount to 9--12 hours of waiting. I agree that the solution I posted above is not perfect, but fixing the numerical IP address every so many years is far quicker than the 9--12 hours you pay for a more robust solution. – unutbu Sep 20 '13 at 21:01
  • I found out that you still have to catch socket timeout exception, otherwise it will crash somehow if there is a problem on socket – Russj Sep 26 '13 at 00:29
  • [12]: %timeit internet_on() 1 loops, best of 3: 1 s per loop In [13]: %timeit have_internet() 100 loops, best of 3: 19.9 ms per loop – Wally Apr 06 '16 at 13:59
  • Takes 1 second to complete. Why not get just the header which takes just 20 milliseconds. Like @Ivelin posted. – Wally Apr 06 '16 at 14:00
  • @aF. Check the latest edit to my answer below. It shows a generic method that is independent of DNS, HTTP and issues with changing IP. – 7h3rAm Apr 22 '16 at 06:51
  • there is no need to use memory to save the response to variable `response`, just call the function – Stam Kaly Oct 22 '16 at 12:53
  • You could also reduce last 2 lines into 1: `except urllib2.URLError: return False` – Stam Kaly Oct 22 '16 at 13:15
  • 2
    Er... just open `http://google.com` and then you solve all the problems everybody keeps talking about here. There's no need to use an ip address... – Jason C Oct 31 '16 at 13:30
  • Is there any other way to check internet running status. Because if you pining `google.com` again again, then they will block our IP. So Is there any other way.? – Sanjiv Aug 21 '19 at 08:32
  • 1
    What about 1.1.1.1, I think it's a DNS Server and shouldn't change however it also results in a webpage – Raymond Jun 26 '20 at 21:35
  • why do you need `as err`? – Sapphire_Brick Jul 23 '20 at 12:57
104

It will be faster to just make a HEAD request so no HTML will be fetched.

try:
    import httplib  # python < 3.0
except:
    import http.client as httplib


def have_internet() -> bool:
    conn = httplib.HTTPSConnection("8.8.8.8", timeout=5)
    try:
        conn.request("HEAD", "/")
        return True
    except Exception:
        return False
    finally:
        conn.close()
Ivelin
  • 12,293
  • 5
  • 37
  • 35
30

As an alternative to ubutnu's/Kevin C answers, I use the requests package like this:

import requests

def connected_to_internet(url='http://www.google.com/', timeout=5):
    try:
        _ = requests.head(url, timeout=timeout)
        return True
    except requests.ConnectionError:
        print("No internet connection available.")
    return False

Bonus: this can be extended to this function that pings a website.

def web_site_online(url='http://www.google.com/', timeout=5):
    try:
        req = requests.head(url, timeout=timeout)
        # HTTP errors are not raised by default, this statement does that
        req.raise_for_status()
        return True
    except requests.HTTPError as e:
        print("Checking internet connection failed, status code {0}.".format(
        e.response.status_code))
    except requests.ConnectionError:
        print("No internet connection available.")
    return False
Def_Os
  • 5,301
  • 5
  • 34
  • 63
  • 3
    Request is a great library. However unlike many examples here I would use [IANA's example.com](https://www.iana.org/domains/reserved) or example.org as a more reliable long-term choice since it's fully controlled by [ICANN](https://en.wikipedia.org/wiki/ICANN). – chirale May 06 '17 at 15:05
  • Is there any other way to check internet running status. Because if you pining `google.com` again again, then they will block our IP. So Is there any other way.? – Sanjiv Aug 21 '19 at 08:32
  • 1
    Very nice. I modified this using `requests.head()` method instead to only get a HEAD response. – howdoicode Nov 12 '20 at 18:42
  • @howdoicode, edited my answer accordingly. much faster, indeed – Def_Os Nov 12 '20 at 20:21
  • Is there any reason for the assignment? Can `_ = requests.head...` be replaced with just `requests.head...` ? – forresthopkinsa Mar 31 '22 at 20:34
17

Just to update what unutbu said for new code in Python 3.2

def check_connectivity(reference):
    try:
        urllib.request.urlopen(reference, timeout=1)
        return True
    except urllib.request.URLError:
        return False

And, just to note, the input here (reference) is the url that you want to check: I suggest choosing something that connects fast where you live -- i.e. I live in South Korea, so I would probably set reference to http://www.naver.com.

Kevin C
  • 337
  • 3
  • 5
10

You can just try to download data, and if connection fail you will know that somethings with connection isn't fine.

Basically you can't check if computer is connected to internet. There can be many reasons for failure, like wrong DNS configuration, firewalls, NAT. So even if you make some tests, you can't have guaranteed that you will have connection with your API until you try.

Tomasz Wysocki
  • 11,170
  • 6
  • 47
  • 62
  • 2
    "It's easier to ask for forgiveness than permission" – cobbal Sep 21 '10 at 20:45
  • this doesn't need to be that good. I simply need to try to connect to a site or ping something and if it gives timeout voilá. How can I do that? – aF. Sep 21 '10 at 20:45
  • Is there any reason you can't just try to connect to API? Why you must to check it before real connection? – Tomasz Wysocki Sep 21 '10 at 20:51
  • I make an html page using python. The html page deppends on the arguments that I give in the python program. I only need to put some html code if I can access to the api. That's why I need to know before. – aF. Sep 21 '10 at 20:58
6
import urllib

def connected(host='http://google.com'):
    try:
        urllib.urlopen(host)
        return True
    except:
        return False

# test
print( 'connected' if connected() else 'no internet!' )

For python 3, use urllib.request.urlopen(host)

Aziz Alto
  • 19,057
  • 5
  • 77
  • 60
5

Try the operation you were attempting to do anyway. If it fails python should throw you an exception to let you know.

To try some trivial operation first to detect a connection will be introducing a race condition. What if the internet connection is valid when you test but goes down before you need to do actual work?

mluebke
  • 8,588
  • 7
  • 35
  • 31
5

Here's my version

import requests

try:
    if requests.get('https://google.com').ok:
        print("You're Online")
except:
    print("You're Offline")
Mujeeb Ishaque
  • 2,259
  • 24
  • 16
3

This might not work if the localhost has been changed from 127.0.0.1 Try

import socket
ipaddress=socket.gethostbyname(socket.gethostname())
if ipaddress=="127.0.0.1":
    print("You are not connected to the internet!")
else:
    print("You are connected to the internet with the IP address of "+ ipaddress )

Unless edited , your computers IP will be 127.0.0.1 when not connected to the internet. This code basically gets the IP address and then asks if it is the localhost IP address . Hope that helps

  • That's an interesting check, though it will only identify if you are connected to a network. Whether it is the Internet or a NAT'd subnet will still remain a question. – 7h3rAm Apr 22 '16 at 05:42
  • @7h3rAm , that's a good point , the DCHP doesn't require internet connection , my mistake. –  Apr 23 '16 at 06:16
  • This just requires a network NIC connect that has received been assigned an IP. Unlike all the other answers, it does not require a connection to the LAN, router, firewall, ISP all the way up to another system. It still fails to show that the NIC is connected if it's a non DHCP assigned address (static) – user150471 Mar 05 '19 at 18:45
  • @user150471, already pointed out, thank you for your contribution. –  Mar 07 '19 at 19:43
2

A modern portable solution with requests:

import requests

def internet():
    """Detect an internet connection."""

    connection = None
    try:
        r = requests.get("https://google.com")
        r.raise_for_status()
        print("Internet connection detected.")
        connection = True
    except:
        print("Internet connection not detected.")
        connection = False
    finally:
        return connection

Or, a version that raises an exception:

import requests
from requests.exceptions import ConnectionError

def internet():
    """Detect an internet connection."""

    try:
        r = requests.get("https://google.com")
        r.raise_for_status()
        print("Internet connection detected.")
    except ConnectionError as e:
        print("Internet connection not detected.")
        raise e
Adam Erickson
  • 6,027
  • 2
  • 46
  • 33
1

my favorite one, when running scripts on a cluster or not

import subprocess

def online(timeout):
    try:
        return subprocess.run(
            ['wget', '-q', '--spider', 'google.com'],
            timeout=timeout
        ).returncode == 0
    except subprocess.TimeoutExpired:
        return False

this runs wget quietly, not downloading anything but checking that the given remote file exists on the web

t-bltg
  • 854
  • 9
  • 17
1

Best way to do this is to make it check against an IP address that python always gives if it can't find the website. In this case this is my code:

import socket

print("website connection checker")
while True:
    website = input("please input website: ")
    print("")
    print(socket.gethostbyname(website))
    if socket.gethostbyname(website) == "92.242.140.2":
        print("Website could be experiencing an issue/Doesn't exist")
    else:
        socket.gethostbyname(website)
        print("Website is operational!")
        print("")
0

Taking unutbu's answer as a starting point, and having been burned in the past by a "static" IP address changing, I've made a simple class that checks once using a DNS lookup (i.e., using the URL "https://www.google.com"), and then stores the IP address of the responding server for use on subsequent checks. That way, the IP address is always up to date (assuming the class is re-initialized at least once every few years or so). I also give credit to gawry for this answer, which showed me how to get the server's IP address (after any redirection, etc.). Please disregard the apparent hackiness of this solution, I'm going for a minimal working example here. :)

Here is what I have:

import socket

try:
    from urllib2 import urlopen, URLError
    from urlparse import urlparse
except ImportError:  # Python 3
    from urllib.parse import urlparse
    from urllib.request import urlopen, URLError

class InternetChecker(object):
    conn_url = 'https://www.google.com/'

    def __init__(self):
        pass

    def test_internet(self):
        try:
            data = urlopen(self.conn_url, timeout=5)
        except URLError:
            return False

        try:
            host = data.fp._sock.fp._sock.getpeername()
        except AttributeError:  # Python 3
            host = data.fp.raw._sock.getpeername()

        # Ensure conn_url is an IPv4 address otherwise future queries will fail
        self.conn_url = 'http://' + (host[0] if len(host) == 2 else
                                     socket.gethostbyname(urlparse(data.geturl()).hostname))

        return True

# Usage example
checker = InternetChecker()
checker.test_internet()
Community
  • 1
  • 1
Jared B.
  • 59
  • 1
  • 5
0

Taking Six' answer I think we could simplify somehow, an important issue as newcomers are lost in highly technical matters.

Here what I finally will use to wait for my connection (3G, slow) to be established once a day for my PV monitoring.

Works under Pyth3 with Raspbian 3.4.2

from urllib.request import urlopen
from time import sleep
urltotest=http://www.lsdx.eu             # my own web page
nboftrials=0
answer='NO'
while answer=='NO' and nboftrials<10:
    try:
        urlopen(urltotest)
        answer='YES'
    except:
        essai='NO'
        nboftrials+=1
        sleep(30)       

maximum running: 5 minutes if reached I will try in one hour's time but its another bit of script!

Seylione
  • 47
  • 2
  • 6
0

Taking Ivelin's answer and add some extra check as my router delivers its ip address 192.168.0.1 and returns a head if it has no internet connection when querying google.com.

import socket

def haveInternet():
    try:
        # first check if we get the correct IP-Address or just the router's IP-Address
        info = socket.getaddrinfo("www.google.com", None)[0]
        ipAddr = info[4][0]
        if ipAddr == "192.168.0.1" :
            return False
    except:
        return False

    conn = httplib.HTTPConnection("www.google.com", timeout=5)
    try:
        conn.request("HEAD", "/")
        conn.close()
        return True
    except:
        conn.close()
        return False
Stevoisiak
  • 23,794
  • 27
  • 122
  • 225
monok
  • 494
  • 5
  • 16
0

This works for me in Python3.6

import urllib
from urllib.request import urlopen


def is_internet():
    """
    Query internet using python
    :return:
    """
    try:
        urlopen('https://www.google.com', timeout=1)
        return True
    except urllib.error.URLError as Error:
        print(Error)
        return False


if is_internet():
    print("Internet is active")
else:
    print("Internet disconnected")
Rajiv Sharma
  • 6,746
  • 1
  • 52
  • 54
0

I added a few to Joel's code.

    import socket,time
    mem1 = 0
    while True:
        try:
                host = socket.gethostbyname("www.google.com") #Change to personal choice of site
                s = socket.create_connection((host, 80), 2)
                s.close()
                mem2 = 1
                if (mem2 == mem1):
                    pass #Add commands to be executed on every check
                else:
                    mem1 = mem2
                    print ("Internet is working") #Will be executed on state change

        except Exception as e:
                mem2 = 0
                if (mem2 == mem1):
                    pass
                else:
                    mem1 = mem2
                    print ("Internet is down")
        time.sleep(10) #timeInterval for checking
Community
  • 1
  • 1
0

For my projects I use script modified to ping the google public DNS server 8.8.8.8. Using a timeout of 1 second and core python libraries with no external dependencies:

import struct
import socket
import select


def send_one_ping(to='8.8.8.8'):
   ping_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname('icmp'))
   checksum = 49410
   header = struct.pack('!BBHHH', 8, 0, checksum, 0x123, 1)
   data = b'BCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwx'
   header = struct.pack(
      '!BBHHH', 8, 0, checksum, 0x123, 1
   )
   packet = header + data
   ping_socket.sendto(packet, (to, 1))
   inputready, _, _ = select.select([ping_socket], [], [], 1.0)
   if inputready == []:
      raise Exception('No internet') ## or return False
   _, address = ping_socket.recvfrom(2048)
   print(address) ## or return True


send_one_ping()

The select timeout value is 1, but can be a floating point number of choice to fail more readily than the 1 second in this example.

Luv
  • 386
  • 4
  • 15
0

import requests and try this simple python code.

def check_internet():
    url = 'http://www.google.com/'
    timeout = 5
    try:
        _ = requests.get(url, timeout=timeout)
        return True
    except requests.ConnectionError:
        return False
Git.Coach
  • 3,032
  • 2
  • 37
  • 54
Raushan Kumar
  • 324
  • 2
  • 5
0

Make sure your pip is up to date by running

pip install --upgrade pip

Install the requests package using

pip install requests  
    import requests
    import webbrowser
    url = "http://www.youtube.com"

    timeout = 6
    try:
        request = requests.get(url, timeout=timeout)
        print("Connected to the Internet")
        print("browser is loading url")
        webbrowser.open(url)
    except (requests.ConnectionError, requests.Timeout) as exception:
       print("poor or no internet connection.")
Salma Elshahawy
  • 1,112
  • 2
  • 11
  • 21
0

I just want to refer to Ivelin's solution, because I can't comment there.

In python 2.7 with an old SSL certificate (in my case, not possible to update, which is another story), there is a possibility of a Certificate Error. In that case, replacing '8.8.8.8' with 'dns.google' or '8888.google' can help.

Hope this will someone helps too.

try:
    import httplib  # python < 3.0
except:
    import http.client as httplib


def have_internet():
    conn = httplib.HTTPSConnection("8888.google", timeout=5)
    try:
        conn.request("HEAD", "/")
        return True
    except Exception:
        return False
    finally:
        conn.close()
CncMess
  • 23
  • 5