如何使用Python 3连接Windows上的WiFi网络?

时间:2019-06-23 07:04:15

标签: python windows network-programming wifi wireless

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

但是这不起作用,因为它基于python2。有没有办法使用Python 3连接到wifi?

3 个答案:

答案 0 :(得分:3)

成为图书馆的迫切需要吗?无需一个即可轻松实现(或将其另存为模块并导入)。

如果无线接口为wlan,并且SSID和配置文件名称相同(通常为true),则只需使用

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,以带动所有者转向其应用程序(因此向他们提供了打包和出售的用户数据)。

答案 1 :(得分:2)

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

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

从此处安装Winwifi后:https://pypi.org/project/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 文件即可。

答案 2 :(得分:1)

注释(约[PyPI]: wireless 0.3.2]):

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

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

好的,经过大量浏览:

,我能够提出一些建议。

code.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 {:s} on {:s}\n".format(sys.version, sys.platform))
    main(sys.argv[1:])
    print("\nDone.")

script.bat

time <nul
ping www.google.com

"e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code.py
ping www.google.com

"e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code.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_064_03.07.03_test0\Scripts\python.exe" code.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 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_064_03.07.03_test0\Scripts\python.exe" code.py {0C58E048-BC0B-4D5F-A21F-FCD4E4B31806}
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 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 ),这会增加复杂性。
    BTW ,代码比我最初预期的要复杂得多(这就是为什么我不费心去解释它-因为这太过分了),但是我看不到修剪的方式
  • script.bat (和time <nul)只是为了在控制台中证明代码正在与无线网络连接/断开连接(并且我不是从连接)并行获胜
    • 我不知道time 0<nul(在输出中)的“ 0 ”部分来自哪里
  • 正如我所指定的,这更像是 POC ,(我和 Win32Wifi )代码中有一些限制。如果没有(小的)代码更改,某些情况(网络)可能无法工作
  • 尽管网络连接成功(并且正常工作),但是在系统任务栏中,网络状态仍然显示为断开状态(实际上会出现几分之一秒)连接,但它会自动更改)。另外,系统托盘网络图标显示为已连接。我不确定这是否代表我(我忘记以某种方式通知 Win -尽管这没有多大意义),或者 Win 不喜欢“别人”以连接到无线网络
  • 最重要的一个:由于 Win32Wifi 有问题,因此上述代码无法使用 OOTB >。我发现了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)修补 utrunner 部分),了解如何在 Win 上应用补丁(基本上,每行以开头)一个“ +” 符号进入,以一个“-” 符号开头的每一行都熄灭)。我正在使用 Cygwin btw