【Python】通過cmd的shell命令獲取局域網內所有IP、MAC地址,通過主機名獲取IP
更新以gitee為準:
gitee
文章目錄
- cmd命令獲取IP
- ping主機名
- 獲取IP的主機名
- socket獲取當前網關
- 運行效果
- 附錄:列表的賦值類型和py打包
- 列表賦值
- BUG復現
- 代碼改進
- 優化
- 總結
- py打包
cmd命令獲取IP
通過cmd命令可以查看出自身ip、網關下的所有設備等
所用的命令有:
ipconfig/all
、arp -a
等
其中 arp命令實際上是從網絡緩存區去讀取 如果重置了緩存區 則無法搜索到設備
故 可以通過ping命令將各個網關下的設備 然后再通過arp -a
讀取即可
代碼層面上 可以通過多線程來加快ping的速度 同時也起到初步篩選作用
def ping_ip(ip):global current_ip_listtry:result = subprocess.run(['ping', '-n','3',ip], capture_output=True, text=True,creationflags=subprocess.CREATE_NO_WINDOW)if "TTL" in str(result.stdout):current_ip_list.append(ip)except:pass
同時 使用arp命令加強篩選
result = subprocess.run(['arp', '-a'], capture_output=True, text=True,creationflags=subprocess.CREATE_NO_WINDOW) self.device_list = []li = (str(result).split(current_ip+" ---")[1].split(":")[0]).split("\\n")for i in li:if ip in i:match = re.search(r'(\d+\.\d+\.\d+\.\d+)\s+([0-9a-fA-F-]+)', i)if match: ip_addr, mac = match.groups() for j in current_ip_list:if j == ip_addr: mac = mac.upper()current_ip_list.remove(j)self.device_list.append([ip_addr,mac,'',''])break
代碼邏輯即為 讀取arp下的所有IP 然后篩選出正常通信的
ping主機名
通過ping命令可以獲取主機名對應的IP 但是只能返回一個IP(通常是最近的一個)
代碼實現如下:
result = subprocess.run(['ping', '-4','-n', '1', hostname],capture_output=True,text=True,creationflags=subprocess.CREATE_NO_WINDOW)# 匹配帶方括號的IP地址ip_match = re.search(r'\[([\d.]+)\]', result.stdout)if ip_match:ip = ip_match.group(1)self.text_area.insert(tk.END, f"解析成功: {hostname} -> {ip}\n")i=1else:# 備用匹配模式ip_match = re.search(r'\d+\.\d+\.\d+\.\d+', result.stdout)if ip_match:ip = ip_match.group()self.text_area.insert(tk.END, f"解析成功: {hostname} -> {ip}\n")i=1else:self.text_area.insert(tk.END, f"無法解析主機名: {hostname}\n")if i == 1:# 在表格中查找IPfound = Falsefor item in self.tree.get_children():if self.tree.item(item, 'values')[0] == ip:# 標紅匹配行self.tree.tag_configure('found', background='#ff9999')self.tree.item(item, tags=('found',))self.tree.see(item) # 滾動到該行found = Truebreakif not found:# 插入新行(MAC設為未知)new_item = self.tree.insert('', 'end', values=(ip, "請重新掃描","",""),tags=('found',))self.tree.tag_configure('found', background='#ff9999')self.tree.see(new_item) # 滾動到新條目
獲取IP的主機名
通過nbtstat -A
命令可以獲取IP對應的主機名和工作組
對應代碼:
def ping_name(num,current_ip):global current_ip_nameip = current_ip_name[num][0]name = ""group = ""r_list = []flag = 0try:result = subprocess.run(['nbtstat', '-A',ip], capture_output=True, text=True,creationflags=subprocess.CREATE_NO_WINDOW)s = result.stdoutif current_ip not in s:returns_list = s.split("\n")for i in s_list:if current_ip in i:flag = 1if flag: if "0.0.0.0" in i:breakif "<" in i:r_list.append(i.split("<")[0].replace(" ",""))if not r_list:returnname = r_list[0]for i in r_list:if i == name:continueelse:group = iif group == "":group = namecurrent_ip_name[num][2] = namecurrent_ip_name[num][3] = groupexcept:pass
socket獲取當前網關
在Python中可以直接通過socket獲取當前所有網卡鄂網關及自身IP地址
代碼為:
def get_local_ip(self):try:ips = []# 獲取所有網絡接口的IPhostname = socket.gethostname()ip_list = socket.gethostbyname_ex(hostname)[2]# 新增UDP方式獲取的活動接口IPwith socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:s.connect(("8.8.8.8", 80))active_ip = s.getsockname()[0]if active_ip not in ip_list:ip_list.append(active_ip)# 篩選私有地址并排序for ip in sorted(ip_list, key=lambda x: x != active_ip):if any([ip.startswith('192.168.'),ip.startswith('10.'),(ip.startswith('172.') and 15 < int(ip.split('.')[1]) < 32)]):ips.append(ip)# 更新下拉框self.ip_combobox['values'] = ipsif ips:self.ip_combobox.set(ips[0])self.update_gateway()else:self.ip_label.config(text="No valid IP found")except Exception as e:self.text_area.insert(tk.END, f"Network detection failed: {str(e)}\n")
運行效果
tkinter直接封裝
附錄:列表的賦值類型和py打包
列表賦值
BUG復現
閑來無事寫了個小程序 代碼如下:
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 19 19:47:01 2021@author: 16016
"""a_list = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15']
#print(len(a_list))
#b_list = ['','','','','','','','','','','','','','','','']
c_list = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
#for i in range(16):
if len(a_list):for j in range(16):a_list[j]=str(a_list[j])+'_'+str(j)print("序號:",j)print('a_list:\n',a_list)c_list[j]=a_listprint('c_list[0]:\n',c_list[0])print('\n')
# b_list[j]=a_list[7],a_list[8]
# print(b_list[j])# 寫入到Excel:
#print(c_list,'\n')
我在程序中 做了一個16次的for循環 把列表a的每個值后面依次加上"_"和循環序號
比如循環第x次 就是把第x位加上_x 這一位變成x_x 我在輸出測試中 列表a的每一次輸出也是對的
循環16次后列表a應該變成[‘0_0’, ‘1_1’, ‘2_2’, ‘3_3’, ‘4_4’, ‘5_5’, ‘6_6’, ‘7_7’, ‘8_8’, ‘9_9’, ‘10_10’, ‘11_11’, ‘12_12’, ‘13_13’, ‘14_14’, ‘15_15’] 這也是對的
同時 我將每一次循環時列表a的值 寫入到空列表c中 比如第x次循環 就是把更改以后的列表a的值 寫入到列表c的第x位
第0次循環后 c[0]的值應該是[‘0_0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’, ‘10’, ‘11’, ‘12’, ‘13’, ‘14’, ‘15’] 這也是對的
但是在第1次循環以后 c[0]的值就一直在變 變成了c[x]的值
相當于把c_list[0]變成了c_list[1]…以此類推 最后得出的列表c的值也是每一項完全一樣
我不明白這是怎么回事
我的c[0]只在第0次循環時被賦值了 但是后面它的值跟著在改變
如圖:
第一次老出bug 賦值以后 每次循環都改變c[0]的值 搞了半天都沒搞出來
無論是用appen函數添加 還是用二維數組定義 或者增加第三個空數組來過渡 都無法解決
代碼改進
后來在我華科同學的指導下 突然想到賦值可以賦的是個地址 地址里面的值一直變化 導致賦值也一直變化 于是用第二張圖的循環套循環深度復制實現了
代碼如下:
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 19 19:47:01 2021@author: 16016
"""a_list = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15']
#print(len(a_list))
#b_list = ['','','','','','','','','','','','','','','','']
c_list = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
#for i in range(16):
if len(a_list):for j in range(16):a_list[j]=str(a_list[j])+'_'+str(j)print("序號:",j)print('a_list:\n',a_list)for i in range(16):c_list[j].append(a_list[i])print('c_list[0]:\n',c_list[0])print('\n')
# b_list[j]=a_list[7],a_list[8]
# print(b_list[j])# 寫入到Excel:
print(c_list,'\n')
解決了問題
優化
第三次是請教了老師 用copy函數來賦真值
代碼如下:
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 19 19:47:01 2021@author: 16016
"""a_list = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15']
#print(len(a_list))
#b_list = ['','','','','','','','','','','','','','','','']
c_list = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]
#for i in range(16):
if len(a_list):for j in range(16):a_list[j]=str(a_list[j])+'_'+str(j)print("序號:",j)print('a_list:\n',a_list)c_list[j]=a_list.copy()print('c_list[0]:\n',c_list[0])print('\n')
# b_list[j]=a_list[7],a_list[8]
# print(b_list[j])# 寫入到Excel:
#print(c_list,'\n')
同樣能解決問題
最后得出問題 就是指針惹的禍!
a_list指向的是個地址 而不是值 a_list[i]指向的才是單個的值 copy()函數也是復制值而不是地址
如果這個用C語言來寫 就直觀一些了 難怪C語言是基礎 光學Python不學C 遇到這樣的問題就解決不了
C語言yyds Python是什么垃圾弱智語言
總結
由于Python無法單獨定義一個值為指針或者獨立的值 所以只能用列表來傳送
只要賦值是指向一個列表整體的 那么就是指向的一個指針內存地址 解決方法只有一個 那就是將每個值深度復制賦值(子列表內的元素提取出來重新依次連接) 或者用copy函數單獨賦值
如圖測試:
部分代碼:
# -*- coding: utf-8 -*-
"""
Created on Sat Nov 20 16:45:48 2021@author: 16016
"""def text1():A=[1,2,3]B=[[],[],[]]for i in range(len(A)):A[i]=A[i]+iB[i]=Aprint(B)def text2():A=[1,2,3]B=[[],[],[]]A[0]=A[0]+0B[0]=Aprint(B)A[1]=A[1]+1B[1]=Aprint(B)A[2]=A[2]+2B[2]=Aprint(B)if __name__ == '__main__':text1()print('\n')text2()
py打包
Pyinstaller打包exe(包括打包資源文件 絕不出錯版)
依賴包及其對應的版本號
PyQt5 5.10.1
PyQt5-Qt5 5.15.2
PyQt5-sip 12.9.0
pyinstaller 4.5.1
pyinstaller-hooks-contrib 2021.3
Pyinstaller -F setup.py 打包exe
Pyinstaller -F -w setup.py 不帶控制臺的打包
Pyinstaller -F -i xx.ico setup.py 打包指定exe圖標打包
打包exe參數說明:
-F:打包后只生成單個exe格式文件;
-D:默認選項,創建一個目錄,包含exe文件以及大量依賴文件;
-c:默認選項,使用控制臺(就是類似cmd的黑框);
-w:不使用控制臺;
-p:添加搜索路徑,讓其找到對應的庫;
-i:改變生成程序的icon圖標。
如果要打包資源文件
則需要對代碼中的路徑進行轉換處理
另外要注意的是 如果要打包資源文件 則py程序里面的路徑要從./xxx/yy換成xxx/yy 并且進行路徑轉換
但如果不打包資源文件的話 最好路徑還是用作./xxx/yy 并且不進行路徑轉換
def get_resource_path(relative_path):if hasattr(sys, '_MEIPASS'):return os.path.join(sys._MEIPASS, relative_path)return os.path.join(os.path.abspath("."), relative_path)
而后再spec文件中的datas部分加入目錄
如:
a = Analysis(['cxk.py'],pathex=['D:\\Python Test\\cxk'],binaries=[],datas=[('root','root')],hiddenimports=[],hookspath=[],hooksconfig={},runtime_hooks=[],excludes=[],win_no_prefer_redirects=False,win_private_assemblies=False,cipher=block_cipher,noarchive=False)
而后直接Pyinstaller -F setup.spec即可
如果打包的文件過大則更改spec文件中的excludes 把不需要的庫寫進去(但是已經在環境中安裝了的)就行
這些不要了的庫在上一次編譯時的shell里面輸出
比如:
然后用pyinstaller --clean -F 某某.spec