如何在Windows上连接WiFi网络?

3

我正在尝试用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?

3个回答

4

在Windows中使用Python连接WiFi,更好的选择是使用winwifi模块:

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

安装完plumbum后,从https://pypi.org/project/winwifi/这里安装winwifi。最好将其安装在32位Python文件夹中。

安装完成后,您可以通过以下代码检查模块(这是用于连接先前连接到设备的路由器):

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 文件夹中的 main.py 文件可以找到更多。


仅供参考,关于wifi故障排除。有时路由器会将我的电脑从互联网中断开。我有ssid和密码。然而,当尝试winwifi.WinWiFi.connect('network_ssid', 'network_pwd')时,它无法重新连接。但是,如果我只做winwifi.WinWiFi.connect('network_ssid') -没有pwd- 它将重新连接到wifi。我有Python 3.8、Windows 10和PyCharm Community Edition 2020.2。 - xiaxio
2
兄弟,如果你在尝试这个之前已经在电脑上连接了WiFi,Windows会保存你的SSID和密码。然后你只需要使用:winwifi.WinWiFi.connect('network_ssid')。 - Ashiq Firoz

4

这个必须要做成一个库吗?即使不用库也可以很容易地实现它(或者,如果你愿意,将其保存为模块并导入)。

如果无线接口是wlan,SSID和配置文件名称相同(通常是这样),那么只需使用

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

在上面的代码段中,tt 是你想要连接的SSID的名称 - 在我的代码中它是一个变量,因为它会依次连接到不同的SSID。
我知道使用subprocess有点笨拙,但上面的代码段没有增加任何显著的开销。
这是我编写的一个脚本的一部分,用于从我父母的太阳能逆变器中“收集”数据:逆变器有自己的SSID,所以该脚本必须依次连接到每个逆变器;使用requests获取实时数据;将数据存储在PostgreSQL数据库中;然后重新连接到家庭wifi。
我知道这些设备也存储历史数据,但是制造商在大约2018年停用了数据API,以引导所有用户使用他们的应用程序(因此给他们提供用户数据以打包和出售)。

我应该在哪里提供密码? - user11004963
类型错误:__init __()收到了一个意外的关键字参数'capture_output'。 - user11004963
1
@GBDGBDA - 通常情况下不需要密码(实际上唯一需要的是配置文件,即“名称”参数)。在命令提示符下运行“netsh wlan show profile”以获取配置文件列表。至于“意外关键字”错误:可能是许多原因之一-可能是您的无线设备不是“wlan”;可能是您将“tt”设置为与您要连接的SSID不同的内容。 - GT.

2

注释(关于 [PyPI]: 无线电):

  • 目前不支持Python 3:因为在代码中使用了cmp函数(该函数仅在Python 2中可用)。

    • 我想提交一个拉取请求(因为修复很简单),但显然在GitHub上已经修复,然而PyPI存储库没有更新(自2016年以来)。
  • 不适用于Win(主页上只列出了Nix驱动程序 - 基本上只启动shell命令)。

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

最近遇到的一个是Win32Wifi:([PyPI]:win32wifi,不过这个也没有更新自2017年以来)。更多细节请参见[SO]:无法使用Python的WlanGetAvailableNetworkList获取所有可用网络(@CristiFati的答案)(还可以查看[SO]:使用WlanScan在Python中强制进行wifi扫描(@CristiFati的答案))。

,我成功找到了一些内容。

code00.py:

#!/usr/bin/env python3

import ctypes as ct
import sys
import time
import traceback

import comtypes

sys.path.insert(0, "e:\Work\Dev\GitHub\CristiFati\win32wifi")  # To load the module from here
from win32wifi import Win32Wifi as ww


ERROR_SUCCESS = 0

WLAN_CONNECTION_HIDDEN_NETWORK = 0x00000001


class WLANException(Exception): pass


class ConnectCallbackContext(ct.Structure):
    _fields_ = [
        ("guid", ct.c_wchar_p),
        ("start", ct.c_byte),
        ("end", ct.c_byte),
        ("fail", ct.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=ct.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 = "DIGI_6e2a70"
            pwd = "3ff091d5"
            guid = wireless_connect(ssid, pwd)
            print("Connected interface {:s}".format(guid))
        except:
            traceback.print_exc()


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

script00.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

Output:

[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]> script00.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)] 064bit 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)] 064bit 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),这些代码并不一定与问题相关,但增加了复杂性。顺便说一句,代码比我最初预期的要复杂得多(这就是为什么我没费心去解释它——因为这会导致过度),但我看不到任何简化它的方法。
script00.bat(和time
正如我所指定的那样,这更像是一个POC,(我的和Win32Wifi的)代码存在一些限制。某些情况下(网络)可能需要进行(小)代码更改才能正常工作。
尽管连接到网络成功(并且有效),但在系统托盘中,网络状态仍显示为已断开连接(实际上在短暂的一秒钟内它会显示为已连接,但然后它会自动更改)。此外,系统托盘中的网络图标显示为已连接。我不确定这是我忘记以某种方式通知Win(虽然这没有太多意义),还是Win不喜欢“其他人”连接到无线网络。
最重要的一个:上述代码将无法直接使用,因为Win32Wifi存在缺陷。我发现了两个致命的(关键)错误,这对于这种情况来说是致命的,并且还有一堆其他较小的错误。我刚刚提交了kedos/win32wifi - Fixes(一些关键)和改进(已合并于200906年),但PyPI上没有更新。
作为替代方案,您可以下载补丁并在本地应用更改。请参阅Run/Debug a Django application's UnitTests from the mouse right-click context menu in PyCharm Community Edition?(@CristiFati的答案)(Patching utrunner部分)以了解如何在Win上应用补丁(基本上,每行以一个“+”号开头的行都会进入,而每行以一个“-”号开头的行都会退出)。

@CristiFati 在 Python 2 中有多个用于连接到无线网络的包。为什么在 Python 3 中没有一个呢?你能建议我如何在 Python 中创建一个 wifi 包吗?我应该学习哪些知识并如何开始。哪些东西将帮助我在 Python 中与 Windows 的 wifi 驱动程序进行通信。 - Himanshuman
很不幸(对你来说),这似乎是唯一的选择。但是通过我的补丁,你可以在这里实现非常好的东西。 - CristiFati
Python 2 到底在做什么?连接到无线网络?显然,人类不知道如何从 Python 中实现这一点(在 C 中没有问题)。 - CristiFati
那么您的意思是目前没有办法制作一个Python 3软件包来连接到无线WiFi网络? - Himanshuman
让我们在聊天中继续这个讨论 - CristiFati
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接