【问题标题】:How to connect to WiFi network on Windows?如何在 Windows 上连接到 WiFi 网络?
【发布时间】:2019-11-05 09:16:19
【问题描述】:

我正在尝试用 Python 3 编写脚本,但目前所有可用的模块都可以在 Python 2 上运行,这将使我能够搜索无线网络并连接到它们。有没有为此的 Python 3 库?

我为 python 2 尝试的代码

from wireless import Wireless
wireless = Wireless()
wireless.connect(ssid='ssid', password='password')

这给了我一个错误

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\Himanshu Poddar\AppData\Local\Programs\Python\Python36-32\lib\site-packages\wireless\Wireless.py", line 23, in __init__
    self._driver_name = self._detectDriver()
  File "C:\Users\Himanshu Poddar\AppData\Local\Programs\Python\Python36-32\lib\site-packages\wireless\Wireless.py", line 50, in _detectDriver
    compare = self.vercmp(ver, "0.9.9.0")
  File "C:\Users\Himanshu Poddar\AppData\Local\Programs\Python\Python36-32\lib\site-packages\wireless\Wireless.py", line 71, in vercmp
    return cmp(normalize(actual), normalize(test))
NameError: name 'cmp' is not defined

但这不起作用,因为它基于 python 2。有什么方法可以使用 Python 3 连接到 wifi

【问题讨论】:

    标签: python windows network-programming wifi wireless


    【解决方案1】:

    这是一个图书馆的迫切需要吗?无需一个也可以轻松实现(或者,如果您愿意,可以将其保存为模块并导入)。

    如果无线接口是wlan 并且 SSID 和 Profile 名称相同(通常为 true),那么只需使用

    cmd = "netsh wlan connect name={0} ssid={0}".format(tt)
    k = subprocess.run(cmd, capture_output=True, text=True).stdout
    

    在上面的 sn-p 中,tt 是您希望连接的 SSID 的名称 - 它是我的代码中的一个变量,因为它依次连接到不同的 SSID。

    我知道使用subprocess 有点麻烦,但是上面的 sn-p 并没有增加任何显着的开销。

    这是我为从我父母的太阳能逆变器中“收集”数据而编写的脚本的一部分:逆变器有自己的 SSID,因此脚本必须依次连接到每个逆变器;使用requests获取实时数据;将数据存储在 PostgreSQL 数据库中;然后重新连接到家庭wifi。

    我知道这些设备还存储历史数据,但是制造商在 2018 年左右禁用了数据 API,以促使所有者使用他们的应用程序(因此向他们提供用户数据以打包和销售)。

    【讨论】:

    • 我在哪里提供密码?
    • TypeError: __init__() 得到了一个意外的关键字参数“capture_output”
    • @GBDGBDA - 通常不需要密码(实际上唯一需要的是配置文件,即name 参数)。在命令提示符处运行 netsh wlan show profile 以获取配置文件列表。至于“意外关键字”错误:可能是一堆东西——可能你的无线设备不是wlan;可能您已将 tt 设置为您尝试连接的 SSID 以外的其他内容。
    【解决方案2】:

    要在windows中使用python连接wifi,更好的选择是使用winwifi模块:

    我建议你在安装 winwifi 之前先安装 plumbum。 这是铅的下载链接:https://pypi.org/project/plumbum/

    之后从这里安装winwifi:https://pypi.org/project/winwifi/ 最好安装在32位的python文件夹中。

    安装后可以通过以下代码(This is to connect router which was connected to the device before)查看模块:

    import winwifi
    winwifi.WinWiFi.connect('the_exact_ssid_or_name_of_your_known_wifi_router')
    

    在您的 IDLE 上运行此代码时,您可以看到 wifi 已连接到您的设备。 如果您想连接新设备,您可以在添加配置文件后使用代码:

    import winwifi
    winwifi.WinWiFi.addprofile('ssid_of_router')
    winwifi.WinWiFi.connect('the_ssid_of_router', 'password')
    

    您可以使用以下命令断开当前 Wifi:

    import winwifi
    winwifi.WinWiFi.disconnect()
    

    这个模块还有更多的命令,尝试探索它们。 更多内容请参考 winwifi 文件夹中的 ma​​in.py 文件。

    【讨论】:

    • 关于 wifi 故障排除仅供参考。有时路由器会断开我的计算机与互联网的连接。我有ssid和密码。但是,当尝试winwifi.WinWiFi.connect('network_ssid', 'network_pwd') 时,它不会重新连接。但如果我只做winwifi.WinWiFi.connect('network_ssid') - 没有密码 - 它会重新连接到wifi。我有 Python 3.8、Windows 10 和 PyCharm Community Edition 2020.2
    • 兄弟,如果您在尝试此操作之前已连接到您的电脑中的 wifi,Windows 将保存您的 ssid 和密码。然后你只需要使用:winwifi.WinWiFi.connect('network_ssid')
    【解决方案3】:

    备注(关于[PyPI]: wireless 0.3.2]):

    • (尚)是否支持Python 3:在代码中某处它使用cmp 函数(仅在 Python 2 中可用)
      • 我想提交一个拉取请求(因为修复很简单),但显然在 GitHub 上它已经修复了,但是 PyPI 存储库没有更新(因为2016)
    • Win 上工作吗(主页上只列出了 Nix 驱动程序 - 基本上它只启动 shell 命令)

    因此,我建议寻找替代方案:

    好的,经过大量浏览:

    ,我有办法了。

    code00.py

    #!/usr/bin/env python3
    
    import sys
    import time
    import ctypes
    import comtypes
    import traceback
    from win32wifi import Win32Wifi as ww
    
    
    ERROR_SUCCESS = 0
    
    WLAN_CONNECTION_HIDDEN_NETWORK = 0x00000001
    
    
    class WLANException(Exception): pass
    
    
    class ConnectCallbackContext(ctypes.Structure):
        _fields_ = [
            ("guid", ctypes.c_wchar_p),
            ("start", ctypes.c_byte),
            ("end", ctypes.c_byte),
            ("fail", ctypes.c_byte),
        ]
    
    
    def _wlan_connect_callback(data, context_addr):
        if context_addr:
            context = ConnectCallbackContext.from_address(context_addr)
            if str(data.interfaceGuid) == context.guid and data.notificationSource == ww.WLAN_NOTIFICATION_SOURCE_DICT[ww.WLAN_NOTIFICATION_SOURCE_ACM]:
                if data.notificationCode == ww.WLAN_NOTIFICATION_ACM_ENUM.wlan_notification_acm_connection_start.name:
                    context.start += 1
                elif context.start:
                    if data.notificationCode == ww.WLAN_NOTIFICATION_ACM_ENUM.wlan_notification_acm_connection_complete.name:
                        context.end += 1
                    elif data.notificationCode == ww.WLAN_NOTIFICATION_ACM_ENUM.wlan_notification_acm_connection_attempt_fail.name:
                        context.fail += 1
    
    
    def wireless_connect(
            ssid,
            password,
            timeout=15,  # secs
            authentication="WPA2PSK",  # "open", 
            encryption="AES",  # "WEP",
            key_type="passPhrase",  # "networkKey", 
            interface_index=0,  # Don't modify this (until PCs with more than 1 WLAN adapter arise :) )
        ):
        interfaces = ww.getWirelessInterfaces()
        if interface_index < 0 or len(interfaces) < interface_index:
            raise WLANException(-1, "No WLAN interface for given index")
        interface = interfaces[interface_index]
        profile_name = ssid + "_profile_tmp"
        ssid_hex = "".join((hex(ord(c))[2:] for c in ssid)).upper()
        profile_string = f"""<?xml version=\"1.0\"?>
            <WLANProfile xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v1\">
                <name>{profile_name}</name>
                <SSIDConfig>
                    <SSID>
                        <hex>{ssid_hex}</hex>
                        <name>{ssid}</name>
                    </SSID>
                </SSIDConfig>
                <connectionType>ESS</connectionType>
                <connectionMode>manual</connectionMode>
                <MSM>
                    <security>
                        <authEncryption>
                            <authentication>{authentication}</authentication>
                            <encryption>{encryption}</encryption>
                            <useOneX>false</useOneX>
                        </authEncryption>
                        <sharedKey>
                            <keyType>{key_type}</keyType>
                            <protected>false</protected>
                            <keyMaterial>{password}</keyMaterial>
                        </sharedKey>
                    </security>
                </MSM>
            </WLANProfile>
        """
        connection_params = {
            "connectionMode": "wlan_connection_mode_temporary_profile",
            "profile": profile_string,
            "ssid": None,
            "bssidList": None,
            "bssType": "dot11_BSS_type_infrastructure",
            "flags": WLAN_CONNECTION_HIDDEN_NETWORK,
        }
    
        ctx = ConnectCallbackContext(interface.guid_string, 0, 0, 0)
        notification_obj = ww.registerNotification(_wlan_connect_callback, context=ctypes.pointer(ctx))
    
        try:
            res = ww.connect(interface, connection_params)
        except Exception as e:
            ww.unregisterNotification(notification_obj)
            raise WLANException("WlanConnect failed") from e
    
        end_time = time.time() + timeout;
        while time.time() < end_time:
            time.sleep(0.5)
            if ctx.end:
                break
        ww.unregisterNotification(notification_obj)
        if ctx.end:
            if ctx.fail:
                raise WLANException(-2, "Connection failed")
        else:
            raise WLANException(-3, "Connection timed out")
        return interface.guid_string
    
    
    def wireless_disconnect(interface_guid):  # Borrowed (and improved) this func from win32wifi.Win32Wifi, to avoid creting the interface when only its guid is required
        handle = ww.WlanOpenHandle()
        try:
            ww.WlanDisconnect(handle, comtypes.GUID(interface_guid))
        except Exception as e:
            raise WLANException("WlanDisconnect failed") from e
        finally:
            ww.WlanCloseHandle(handle)
    
    
    def main(argv):
        if argv:
            try:
                guid = argv[0]
                print("Disconnecting wireless interface {:s} ...".format(guid))
                wireless_disconnect(guid)
            except:
                traceback.print_exc()
        else:
            try:
                print("Connecting to wireless network ...")
                ssid = "Network SSID"  # ssid and pwd here are (deliberately) dummy
                pwd = "Network password"
                guid = wireless_connect(ssid, pwd)
                print("Connected interface {:s}".format(guid))
            except:
                traceback.print_exc()
    
    
    if __name__ == "__main__":
        print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        main(sys.argv[1:])
        print("\nDone.")
    

    script.bat

    setlocal enableextensions enabledelayedexpansion
    
    set _EXE_PTYHON="e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe"
    
    time <nul
    
    ping www.google.com
    
    %_EXE_PTYHON% code00.py
    ping www.google.com
    
    %_EXE_PTYHON% code00.py {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806}
    ping www.google.com
    
    time <nul
    

    输出

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q056721759]> sopr.bat
    *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
    
    [prompt]> script.bat
    
    [prompt]> time  0<nul
    The current time is:  1:45:08.31
    Enter the new time:
    [prompt]> ping www.google.com
    Ping request could not find host www.google.com. Please check the name and try again.
    
    [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py
    Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    Connecting to wireless network ...
    Connected interface {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806}
    
    Done.
    
    [prompt]> ping www.google.com
    
    Pinging www.google.com [2a00:1450:400d:809::2004] with 32 bytes of data:
    Reply from 2a00:1450:400d:809::2004: time=11ms
    Reply from 2a00:1450:400d:809::2004: time=12ms
    Reply from 2a00:1450:400d:809::2004: time=12ms
    Reply from 2a00:1450:400d:809::2004: time=19ms
    
    Ping statistics for 2a00:1450:400d:809::2004:
        Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
    Approximate round trip times in milli-seconds:
        Minimum = 11ms, Maximum = 19ms, Average = 13ms
    
    [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806}
    Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    Disconnecting wireless interface {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806} ...
    
    Done.
    
    [prompt]> ping www.google.com
    Ping request could not find host www.google.com. Please check the name and try again.
    
    [prompt]> time  0<nul
    The current time is:  1:45:12.82
    Enter the new time:
    

    注意事项

    • 为了创建一个POC,我必须添加一些不一定与问题相关的代码(例如wireless_disconnect),这增加了复杂性。
      顺便说一句,代码比我最初预期的要复杂得多(这就是为什么我没有费心解释它的原因——因为这会有点矫枉过正),但我看不出有什么办法修剪它
    • script.bat(和time &lt;nul)只是为了在控制台中证明代码正在连接/断开无线网络(并且我没有从 Win 连接在平行下)
      • 我不知道time 0&lt;nul(输出中)的“ 0”部分来自哪里
    • 正如我所指定的,这更像是一个POC,在(我的和Win32Wifi)代码中有一些限制。如果没有(小的)代码更改,某些场景(网络)可能无法运行
    • 虽然连接到网络成功(并且工作),在系统托盘中,网络状态仍然显示为断开连接(实际上只有几分之一秒)已连接,但随后会自动更改)。此外,系统托盘网络图标显示为已连接。我不确定这是否在我身边(我忘了以某种方式通知 Win - 虽然这没有多大意义),或者 Win 不喜欢“其他人”以连接到无线网络
    • 最重要的一个:上面的代码将无法工作OOTB,因为Win32Wifi 有错误。我发现了 2 个对这种情况致命(严重) 的错误,以及一堆其他较小的错误。
      我刚刚提交了 [GitHub]: kedos/win32wifi - Fixes (some critical) and improvements。不确定它的结果是什么(考虑到不活动期)。

      作为替代方案,您可以下载补丁并在本地应用更改。查看[SO]: Run/Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? (@CristiFati's answer)Patching utrunner 部分)了解如何在 Win 上应用补丁(基本上,每一行都以 one 开头) +" 符号进入,以 一个 "-" 符号开头的每一行都退出)。我正在使用 Cygwinbtw

    【讨论】:

    • 这是主要的。我想连接到 wifi 网络。如果您知道我们可以做到这一点
    • 不,我勾选了它,因为在我看来你为这个问题做了很多研究
    • @PoddarHimanshu:对不起,评论的错误答案。无论如何,如果没有 3rd-party 补丁,您将无法实现您的目标。所以(这里没有看到滴答声:)):[SO]: What should I do when someone answers my question?。让我知道你在哪里遇到了麻烦(因为我感觉他们会来 - 尤其是在修补文件时)。
    • @CristiFati 在 Python 2 中有多个包可以连接到无线网络。为什么 Python 3 没有它们中的任何一个。你能建议我在 python 中创建一个 wifi 包吗?我应该学习什么以及我应该如何开始。什么东西可以帮助我在 Python 中与 windows 的 wifi 驱动程序交谈。
    • @HimanshuPoddar:你能说出一个(适用于Win)吗?
    猜你喜欢
    • 1970-01-01
    • 2021-03-29
    • 2015-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多