簡體   English   中英

如何使用 Python 3 在 Windows 上連接到 WiFi 網絡?

[英]How to connect to WiFi network on Windows using Python 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

是否迫切需要將其作為圖書館? 它可以在沒有一個的情況下輕松實現(或者,如果您願意,可以將其另存為模塊並導入)。

如果無線接口是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,以推動所有者使用他們的應用程序(因此為他們提供用戶數據以進行打包和銷售)。

在windows下使用python連接wifi,更好的選擇是使用winwifi模塊:

我建議您在安裝 winwifi 之前先安裝 plumbum。 這是下載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文件夾中的 main.py 文件。

注釋(關於[PyPI]:無線 0.3.2 ]):

  • (還)支持Python 3 :因為在代碼中的某個地方它使用了cmp函數(僅在Python 2 中可用)
    • 我想提交一個拉取請求(因為修復很簡單),但顯然在GitHub 上它已經修復了,但PyPI存儲庫沒有更新(自 2016 年以來)
  • 不適用於Win (主頁上僅列出Nix驅動程序 - 基本上它只啟動shell命令)

因此,我建議尋找替代方案:

好吧,經過大量瀏覽:

,我能夠想出一些東西。

代碼00.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.")

腳本.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 <nul )只是為了在控制台中證明代碼正在連接/斷開無線網絡(並且我沒有從Win並行連接)
    • 我不知道time 0<nul (在輸出中)的“ 0 ”部分來自哪里
  • 正如我所指出的,這更像是一個POC ,(我的和Win32Wifi )代碼有一些限制。 如果沒有(小)代碼更改,某些場景(網絡)可能無法工作
  • 盡管連接到網絡成功(並正常工作),但在系統托盤中,網絡狀態仍顯示為已斷開連接(實際上在幾分之一秒內顯示已連接,但隨后會自動更改)。 此外,系統托盤網絡圖標顯示為Connected 我不確定這是否在我這邊(我忘了以某種方式通知Win - 盡管這沒有多大意義),或者Win不喜歡“其他人”連接到無線網絡
  • 最重要的一點:上面的代碼不能在OOTB 中工作,因為Win32Wifi有問題 我發現了 2 個對於這種情況來說是致命的(關鍵的)錯誤,以及一些其他較小的錯誤。
    我剛剛提交了[GitHub]:kedos/win32wifi - 修復(一些關鍵)和改進 不確定它會是什么結果(考慮到不活動期)。

    作為替代方案,您可以下載補丁,並在本地應用更改。 檢查[SO]:從 PyCharm 社區版中的鼠標右鍵單擊上下文菜單運行/調試 Django 應用程序的單元測試? (@CristiFati 的回答)Patching utrunner部分)關於如何在Win上應用補丁(基本上,以一個“+”號開頭的每一行都進入,以一個“-”號開頭的每一行都退出)。 我正在使用Cygwin順便說一句

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM