如何查看 Python 中是否有可用和活动的网络连接?

我想看看我是否可以访问在线 API,但为此,我需要有互联网接入。

如何使用 Python 查看是否有可用和活动的连接?

245159 次浏览

您可以尝试下载数据,如果连接失败,您将知道与连接的东西是不好的。

基本上你不能检查计算机是否连接到互联网。失败的原因有很多,比如错误的 DNS 配置、防火墙、 NAT。因此,即使进行了一些测试,也不能保证在尝试之前能够与 API 建立连接。

也许你可以用这样的东西:

import urllib2


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

目前,216.58.192.142是 google.com. http://216.58.192.142更改为任何可以期望快速响应的站点的 IP 地址之一。

这个固定的 IP 不会永远映射到 google.com 不健壮——它需要不断的维护才能正常工作。

上面的代码使用固定 IP 地址而不是完全限定域名(FQDN)的原因是因为 FQDN 需要 DNS 查找。当机器没有工作的互联网连接时,DNS 查找本身可能会阻塞对 urllib_request.urlopen的调用超过一秒钟。感谢@rzetterberg 指出这一点。


如果上面的固定 IP 地址不起作用,您可以通过运行以下命令找到 google.com (在 unix 上)的当前 IP 地址

% dig google.com  +trace
...
google.com.     300 IN  A   216.58.192.142

不管怎样,试试你想做的手术。如果它失败了,python 应该抛出一个异常让你知道。

首先尝试一些简单的操作来检测连接将会引入竞态条件。如果互联网连接是有效的,当你测试,但下降之前,你需要做实际工作?

只是为了更新 unutbu 对 Python 3.2中新代码的说明

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

需要注意的是,这里的输入(参考)是您想要检查的 URL: 我建议选择一些快速连接您所居住的地方的东西——也就是说,我住在韩国,所以我可能会设置参考 http://www.naver.com

作为 ubutnu/Kevin C 答案的替代方案,我使用的 requests软件包如下:

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

奖励: 这可以扩展到这个功能,ping 一个网站。

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

只发送一个 HEAD 请求会更快,因此不会获取 HTML。

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()
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!' )

对于 python3,使用 urllib.request.urlopen(host)

Unutbu 的回答为起点,过去由于“静态”IP 地址的改变,我已经创建了一个简单的类,使用 DNS 查找(即使用 URL“ https://www.google.com”)进行一次检查,然后存储响应服务器的 IP 地址,以便在后续检查中使用。这样,IP 地址始终是最新的(假设类至少每隔几年重新初始化一次)。我还赞扬了 Gawry 的 这个答案,它向我展示了如何获取服务器的 IP 地址(在任何重定向之后,等等)。请忽略这个解决方案明显的骇客行为,我将在这里举一个最小的工作示例。:)

以下是我所知道的:

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

如果我们可以连接到一些互联网服务器,那么我们确实有连接。然而,为了最快和最可靠的方法,所有的解决方案至少应符合以下要求:

  • 避免 DNS 解析(我们将需要一个众所周知的 IP,并保证可用的大部分时间)
  • 避免应用层连接(连接到 HTTP/FTP/IMAP 服务)
  • 避免从 Python 或其他选择的语言调用外部实用程序(我们需要提出一个不依赖于第三方解决方案的语言无关的解决方案)

为了满足这些要求,一种方法是检查是否可以访问其中的一个 Google 的公共 DNS 服务器。这些服务器的 IPv4地址是 8.8.8.88.8.4.4。我们可以试着联系他们中的任何一个。

主机 8.8.8.8的快速 Nmap 显示如下结果:

$ 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

正如我们所看到的,53/tcp是开放和非过滤的。如果您是非 root 用户,请记住使用 sudo或 Nmap 的 -Pn参数来发送精心制作的探测数据包,并确定主机是否已启动。

在我们尝试使用 Python 之前,让我们使用一个外部工具 Netcat 来测试连接性:

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

Netcat 确认我们可以通过 53/tcp到达 8.8.8.8。现在我们可以在 Python 中设置到 8.8.8.8:53/tcp的套接字连接来检查连接:

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

另一种方法是将手工制作的 DNS 探测器发送到这些服务器之一,并等待响应。但是,我认为,由于数据包丢失、 DNS 解析失败等原因,它可能比较慢。如果你不这么认为,请发表评论。

更新 # 4: 公共名称服务器的 这个列表对于测试 IP 是一个很好的参考。

更新 # 3: 在异常处理更改后再次测试:

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

更新 # 2: 我进行了快速测试,以确定对这个问题的所有有效答案的最快和最通用的实现。总结如下:

$ 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

再说一遍:

$ 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表示来自各个作者的所有这些实现正确地识别到 Internet 的连接。时间以毫秒分辨率显示。

更新 # 1: 感谢@theamk 的注释,超时现在是一个参数,默认情况下初始化为 3s

如果从 127.0.0.1更改了本地主机,这可能无法工作 试试看

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 )

除非经过编辑,您的计算机 IP 将是127.0.0.1时,没有连接到互联网。 这段代码主要获取 IP 地址,然后询问它是否是本地主机 IP 地址。 希望能帮上忙

当在集群上运行脚本或者不运行脚本时,这是我最喜欢的一个

import subprocess


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

这会安静地运行 wget,不会下载任何东西,只是检查给定的远程文件是否存在于 Web 上

对于 Six 的回答,我认为我们可以以某种方式简化,这是一个重要的问题,因为新来者会迷失在高度技术性的问题中。

在这里,我最终将使用等待我的连接(3G,慢)建立一天一次,我的 PV 监测。

在 Pyth3下使用 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)

最大运行时间: 5分钟,如果达到我会尝试在一个小时的时间,但它的另一位脚本!

最好的方法是让它检查一个 IP 地址,Python 总是给出,如果它不能找到网站。在这种情况下,这是我的代码:

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

当我的路由器发送 IP 地址192.168.0.1时,如果没有互联网连接,返回一个 head,那么就需要进行一些额外的检查。

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

这是我的版本

import requests


try:
if requests.get('https://google.com').ok:
print("You're Online")
except:
print("You're Offline")

这在 Python 3.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")

使用 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

或者,提出一个例外的版本:

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

我在 Joel 的代码里加了一些。

    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

对于我的项目,我使用 脚本修改来 ping Google Public DNS 服务器8.8.8.8。使用1秒的超时和没有外部依赖关系的核心 python 库:

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

选择超时值为1,但是可以选择浮点数,这样比本例中的1秒更容易失败。

导入请求并尝试这个简单的 python 代码。

def check_internet():
url = 'http://www.google.com/'
timeout = 5
try:
_ = requests.get(url, timeout=timeout)
return True
except requests.ConnectionError:
return False

通过运行确保您的 pip 是最新的

pip install --upgrade pip

使用

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

我只是想参考伊夫林的解决方案,因为我不能在这里发表评论。

在使用旧 SSL 证书的 python 2.7中(在我的例子中,不可能更新,这是另一个故事) ,存在证书错误的可能性。在这种情况下,将’8.8.8.8’替换为’dns.google’或’8888。谷歌能帮上忙。

希望有人能帮上忙。

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