Python NFC数据采集与Excel自动化存储实战教程
1. 项目概述:当NFC遇见Excel,用Python和Mind+打造一个信息管理小工具
最近在整理一些实体卡片信息,比如门禁卡、会员卡,每次手动录入到电脑里都挺麻烦的。正好手头有个支持NFC的安卓手机和一堆Python库,就琢磨着能不能自己搞个小工具,用手机“嘀”一下卡片,信息就自动存到电脑的Excel表格里。这个想法听起来简单,但真做起来,从硬件交互到数据存储,中间有不少门道。这个项目,我把它叫做“NFC信息录入与Excel存储系统”,核心就是用Python作为桥梁,连接前端的NFC读取(这里我用Mind+图形化编程做了个简易的手机端界面)和后端的数据存储。对于想入门物联网数据采集或者自动化办公的朋友来说,这是个非常不错的练手项目,能一次性接触到硬件交互、数据解析、文件操作和简单的GUI设计。
2. 核心思路与技术选型解析
2.1 为什么选择“Mind+ + Python”的组合?
这个项目的核心是解决“移动端采集”与“桌面端存储”的联动问题。我选择Mind+和Python的组合,主要是基于快速原型开发和功能解耦的考虑。
Mind+的角色:它是一个对初学者非常友好的图形化编程软件,支持Python模式。我主要用它来快速构建运行在安卓手机上的NFC读取客户端。它的优势在于,通过拖拽积木块,可以免去直接编写复杂安卓原生代码的麻烦,快速实现调用手机NFC硬件、读取卡片ID(UID)或NDEF文本信息的功能。Mind+生成的实际上是一个用Kivy等框架打包的Python应用,可以在安卓上运行。这相当于我们用Python写了一个具有简单图形界面的手机App,专门负责“读卡”。
Python的角色:这是整个项目的大脑和数据处理中心。它负责两件事:一是与Mind+客户端进行通信(比如通过Socket网络通信或HTTP接口),接收从手机端发来的NFC数据;二是将接收到的数据,按照预设的格式(如卡号、读取时间、备注信息)写入到本地的Excel文件中。Python生态里处理Excel的库非常成熟,比如xlwt(写xls)、openpyxl(读写xlsx)、pandas(数据分析),选择很多。
这样拆解的好处:将采集端(手机)和数据处理端(电脑)分离。手机只负责读卡和发送数据,电脑上的Python脚本负责接收和存储。这种架构清晰,也便于后期扩展,比如可以更换其他采集设备(如USB NFC读卡器),或者将数据存储改为数据库,而不用大改采集端的代码。
2.2 NFC数据读取的层次与选择
NFC卡片类型很多,常见的有Mifare Classic、Mifare Ultralight、NTAG等。不同卡片存储的数据结构和安全级别不同。对于这个入门项目,我们的目标不是进行复杂的卡片数据读写或破解,而是实现一个通用的信息录入。因此,读取策略上我建议分两个层次:
读取卡片唯一标识符(UID):这是每张NFC卡片的“身份证号”,是全球唯一的。读取UID不需要密钥验证,兼容性最好,几乎所有类型的NFC卡片都能读到。我们可以用UID来区分不同的卡片。在Mind+中,这通常对应“读取NFC标签ID”之类的积木块。
尝试读取NDEF格式的文本信息:NDEF是NFC数据交换格式标准。很多门禁卡只存UID,但一些智能海报、名片卡会写入NDEF格式的网址或文本。如果我们的应用场景包括读取这类信息,那么可以在Mind+中增加“读取NFC文本记录”的逻辑。但要注意,读取NDEF信息可能需要卡片本身支持且已格式化。
实际操作中的策略:我会在Mind+程序中先尝试读取NDEF文本,如果读不到(比如卡片不是NDEF格式或为空),则降级为读取UID。这样既能处理信息丰富的卡片,也能兼容最普通的门禁卡。
2.3 Excel库选型:为何是xlwt?
网络热词里提到了xlwt,这是一个专门用于写入旧版Excel.xls格式文件的库。对于这个项目,选择它有几个现实的考虑:
- 简单直接:
xlwt的API非常直观,创建 workbook -> 添加 sheet -> 在指定单元格(row, col)写入数据 -> 保存文件。几行代码就能完成核心功能,学习成本低。 - 轻量级:项目目标只是存储录入的信息(卡号、时间、备注),不涉及复杂的公式、图表、样式。
xlwt完全够用,无需引入更庞大的pandas或功能全面的openpyxl。 - 兼容性:生成的
.xls文件可以被几乎所有版本的Microsoft Excel、WPS Office以及在线表格工具打开,确保数据的可访问性。
当然,xlwt的局限是只能写.xls,不能写.xlsx,且单个sheet最多支持65536行。对于这个信息录入工具,这个容量上限在绝大多数情况下都是绰绰有余的。如果未来数据量激增,可以平滑迁移到openpyxl库。
3. 环境准备与依赖安装
3.1 电脑端(Python数据处理中心)环境搭建
电脑端需要安装Python和必要的库。我强烈建议使用Python 3.6及以上版本。
安装Python:从Python官网下载安装包。安装时务必勾选“Add Python to PATH”,这样可以在命令行中直接使用
python和pip命令。安装完成后,打开命令行(CMD或PowerShell),输入python --version,能显示版本号即表示成功。安装xlwt库:在命令行中执行以下命令即可。
pip install xlwt如果下载速度慢,可以使用国内镜像源,例如:
pip install xlwt -i https://pypi.tuna.tsinghua.edu.cn/simple代码编辑器选择:任何文本编辑器都可以,但使用VS Code或PyCharm这类集成开发环境(IDE)体验会更好。它们提供代码高亮、智能提示和调试功能。网络热词中也提到了相关配置,其实很简单:安装Python扩展(如VS Code的Python扩展)后,编辑器通常能自动识别Python解释器。
3.2 手机端(Mind+ NFC读取端)环境准备
- 安装Mind+:访问Mind+官网,下载对应操作系统的版本(Windows/macOS)并安装。
- 切换为Python模式:打开Mind+,在左上角将编程模式从“图形化”切换为“Python”。这个模式下,我们既可以使用积木块,也可以直接查看和编辑生成的Python代码。
- 准备安卓手机:确保手机支持NFC功能(现在中高端机型基本都支持)。在手机设置中打开NFC开关。
- 连接手机调试:Mind+支持通过Wi-Fi或USB连接手机进行实时调试。这需要先在手机上安装一个叫“Mind+”的助手App(Mind+官网提供下载)。连接成功后,就可以将写好的程序“上传”到手机运行。
注意:Mind+的Python模式本质是将Python代码与Kivy等GUI框架打包成APK。在开发阶段,我们通过连接手机进行实时调试和运行,这比反复打包APK要高效得多。
4. Mind+手机端程序开发详解
4.1 界面设计与积木逻辑
我们的手机端程序需要一个非常简单的界面:一个显示读取结果的文本框(Label),或许再加一个“开始读取”的按钮。但在NFC读取场景下,更常见的交互是“靠近即读”,所以我们完全可以不用按钮,程序启动后自动监听NFC事件。
在Mind+的“Python模式”下,我们从左侧的“扩展”模块中添加“NFC”支持。然后,主要使用以下两个积木:
- “当NFC标签被发现时”:这是一个事件触发器积木。当手机检测到附近有NFC卡片时,自动执行其内部的代码块。
- “读取NFC标签的ID” / “读取NFC文本记录”:放在上述事件触发器内部的积木,用于执行具体的读取操作。
我的程序逻辑流程图如下(文字描述):程序启动 -> 初始化NFC监听 -> 用户将卡片贴近手机 -> 触发“标签被发现”事件 -> 尝试读取NDEF文本 -> 如果成功,则将文本内容作为“卡信息”;如果失败,则读取标签UID作为“卡信息” -> 将“卡信息”显示在手机屏幕上,同时通过网络发送给电脑端的Python服务。
4.2 关键代码与网络通信实现
Mind+积木背后会生成Python代码。理解这部分代码对调试和进阶学习很有帮助。核心是处理NFC事件和建立网络通信。
NFC读取部分伪代码思路:
# 这是一个基于Mind+生成代码的逻辑示意,非直接可运行代码 def on_nfc_tag_discovered(tag): # tag是发现的标签对象 try: # 尝试作为NDEF标签读取文本信息 ndef_record = tag.get_ndef_message().get_records()[0] card_info = ndef_record.text # 获取文本内容 info_type = "NDEF_TEXT" except: # 如果读取NDEF失败,则读取UID card_info = tag.get_uid().hex() # 获取16进制字符串格式的UID info_type = "UID" current_time = time.strftime("%Y-%m-%d %H:%M:%S") # 在手机界面显示 display_label.text = f"类型:{info_type}\n信息:{card_info}\n时间:{current_time}" # 调用函数,将数据发送到电脑 send_data_to_pc(card_info, info_type, current_time)网络通信实现:为了让手机和电脑上的Python程序对话,我们需要选择一个通信协议。对于这种小工具,使用TCP Socket是最简单直接的方式。
- 电脑端:运行一个TCP服务器(Server),绑定一个IP(如本机
127.0.0.1)和端口(如12345),并持续监听。 - 手机端:作为客户端(Client),在读取到NFC信息后,连接到电脑的IP和端口,将数据拼接成一条字符串(例如用逗号分隔
卡信息,类型,时间)发送过去。
在Mind+中,可以使用“网络”类别下的“连接TCP服务器”和“发送数据”积木来实现客户端功能。这里有个关键点:电脑和手机需要在同一个局域网(Wi-Fi)下。你需要知道电脑在局域网内的IP地址(在命令行输入ipconfig查看),并在Mind+程序中填写这个IP和约定的端口号。
实操心得:在调试网络通信时,务必先关闭电脑的防火墙,或者为你的Python程序在防火墙中创建允许规则,否则连接会被阻断。可以先在电脑上写一个简单的测试客户端和服务端脚本,确保通路正常,再集成到Mind+项目中。
5. 电脑端Python服务与Excel写入核心实现
5.1 构建TCP服务器接收数据
电脑端的Python脚本首要任务是作为一个稳定的服务器,等待手机连接并接收数据。这里使用Python标准库socket和threading来处理多客户端连接(虽然通常只有一个手机连接,但好的架构要考虑扩展性)。
import socket import threading import time from datetime import datetime def start_tcp_server(host='0.0.0.0', port=12345): """ 启动TCP服务器 """ server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置端口复用,避免重启时“Address already in use”错误 server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((host, port)) server_socket.listen(5) # 允许最多5个等待连接 print(f"[*] NFC数据接收服务器已启动,监听 {host}:{port}") while True: client_socket, client_addr = server_socket.accept() print(f"[+] 接收到来自 {client_addr} 的连接") # 为每个客户端连接创建一个新线程处理,避免阻塞 client_thread = threading.Thread(target=handle_client, args=(client_socket, client_addr)) client_thread.start() def handle_client(client_socket, client_addr): """ 处理单个客户端连接 """ try: data = client_socket.recv(1024).decode('utf-8') # 接收数据,假设是UTF-8编码 if data: print(f"[*] 来自 {client_addr} 的原始数据: {data}") # 解析数据,假设格式为“卡信息,类型,时间” parts = data.strip().split(',') if len(parts) >= 3: card_info, info_type, read_time = parts[0], parts[1], parts[2] # 调用Excel写入函数 save_to_excel(card_info, info_type, read_time) else: print(f"[-] 数据格式错误: {data}") except Exception as e: print(f"[-] 处理客户端 {client_addr} 时出错: {e}") finally: client_socket.close()5.2 使用xlwt库写入Excel文件
接下来是核心的数据存储部分。我们需要创建一个Excel文件,并在其中按行追加每次读取到的NFC信息。
import xlwt import os # 全局定义文件名和表头 EXCEL_FILE = 'nfc_records.xls' SHEET_NAME = 'NFC记录' def init_excel_file(): """ 初始化Excel文件,如果文件不存在则创建并写入表头 """ if not os.path.exists(EXCEL_FILE): workbook = xlwt.Workbook(encoding='utf-8') sheet = workbook.add_sheet(SHEET_NAME) # 定义表头样式(可选,加粗) header_style = xlwt.easyxf('font: bold on') headers = ['序号', '卡信息', '信息类型', '读取时间', '录入时间', '备注'] for col, header in enumerate(headers): sheet.write(0, col, header, header_style) # 设置列宽(可选) sheet.col(0).width = 256 * 8 # 序号列 sheet.col(1).width = 256 * 30 # 卡信息列 sheet.col(2).width = 256 * 15 # 类型列 sheet.col(3).width = 256 * 20 # 读取时间列 sheet.col(4).width = 256 * 20 # 录入时间列 sheet.col(5).width = 256 * 30 # 备注列 workbook.save(EXCEL_FILE) print(f"[*] 已创建新的Excel文件: {EXCEL_FILE}") def save_to_excel(card_info, info_type, read_time): """ 将一条NFC记录追加到Excel文件中 """ init_excel_file() # 确保文件存在 # 1. 打开现有工作簿(xlwt不能直接修改,需要先读取再写入,这里用xlrd读,xlwt写) # 为了简化,我们采用“读取-追加-保存”的方式。对于轻量级操作可以接受。 # 注意:xlwt不能直接打开现有文件修改,以下是一个通用处理模式 import xlrd from xlutils.copy import copy # 需要安装 xlutils: pip install xlutils # 打开现有文件读取 rb = xlrd.open_workbook(EXCEL_FILE, formatting_info=True) # 获取行数 r_sheet = rb.sheet_by_index(0) last_row_num = r_sheet.nrows # 复制一份用于写入 wb = copy(rb) sheet = wb.get_sheet(0) # 2. 准备写入的数据 entry_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") row_data = [ last_row_num, # 序号,接续上一行 card_info, info_type, read_time, # 手机端发送的读取时间 entry_time, # 电脑端接收到数据的时间 '' # 备注初始为空 ] # 3. 写入新行 for col, value in enumerate(row_data): sheet.write(last_row_num, col, value) # 4. 保存文件 wb.save(EXCEL_FILE) print(f"[+] 记录已保存: {card_info} ({info_type})")关键点解析:这里遇到了
xlwt的一个主要限制:它不能像openpyxl那样直接加载并修改现有工作簿。我们采用了变通方案:使用xlrd读取原文件,用xlutils.copy复制出一个可写的副本,然后追加数据并保存。虽然多了一步,但对于这个场景是稳定可靠的。确保安装了xlrd和xlutils库(pip install xlrd xlutils)。
5.3 主程序整合与运行
将服务器和Excel写入功能整合起来,并让程序能持续运行。
if __name__ == '__main__': # 初始化Excel文件(如果不存在) init_excel_file() print("[*] NFC信息录入系统准备就绪...") print("[*] 请确保手机与电脑在同一Wi-Fi网络下,并启动手机端Mind+应用。") # 启动TCP服务器(默认监听所有网卡,端口12345) start_tcp_server(host='0.0.0.0', port=12345)运行这个脚本,你的电脑就开始监听12345端口了。接下来,只要手机端程序成功连接并发送数据,信息就会自动记录到nfc_records.xls文件中。
6. 系统联调与优化实践
6.1 首次连接调试步骤
- 获取电脑IP:在电脑命令行输入
ipconfig(Windows)或ifconfig(macOS/Linux),找到无线局域网适配器的IPv4地址,例如192.168.1.100。 - 配置手机端:在Mind+程序中,将TCP客户端连接的目标地址修改为上一步获取的电脑IP,端口设为
12345。 - 启动顺序:先运行电脑端的Python服务器脚本,看到“监听中”的提示后,再在手机上运行Mind+程序。
- 测试:用一张NFC卡片贴近手机。观察手机屏幕是否显示读取到的信息,同时观察电脑命令行窗口是否有接收到数据的打印信息,并检查
nfc_records.xls文件是否新增了一行。
6.2 数据格式与错误处理增强
在实际使用中,网络传输可能不稳定,数据格式也可能出错。我们需要增强程序的健壮性。
- 定义通信协议:为了更可靠地解析数据,可以定义一个简单的协议。例如,每条数据以换行符
\n结束,字段间用特定分隔符(如竖线|)。手机端发送卡信息|类型|时间\n,电脑端按\n分割每条消息,再按|分割字段。 - 添加数据校验:在
handle_client函数中,增加更严格的数据格式校验和异常捕获。 - 心跳机制:可以让手机端定期发送一个心跳包(如
HEARTBEAT),电脑端收到后回复ACK。这有助于检测连接是否断开,并在手机端实现断线重连逻辑。
6.3 功能扩展思路
基础功能实现后,可以考虑以下扩展方向,让这个小工具更实用:
- 图形化界面(GUI):使用
tkinter、PyQt或DearPyGui为电脑端的服务器程序做一个简单的界面,显示连接状态、最新记录,甚至提供手动添加备注、导出数据等功能。 - 数据库集成:当记录成千上万条后,Excel的查询和管理会变慢。可以集成轻量级数据库如
SQLite。Python内置sqlite3模块,将数据存入数据库后,查询、统计都非常方便。 - 数据去重:基于卡片的UID,在写入前检查是否已存在相同记录,避免重复录入。
- 声音/提示音:在手机端,读取成功或发送成功时,播放一个简短的提示音,提升用户体验。
- 批量录入模式:在手机端增加一个“批量模式”开关,开启后连续读卡,直到手动关闭,期间所有记录自动发送。
7. 常见问题与排查实录
在实际搭建和运行过程中,你几乎一定会遇到下面这些问题。这里我把踩过的坑和解决方法记录下来。
7.1 手机无法连接电脑服务器
- 现象:Mind+程序提示连接失败或超时。
- 排查步骤:
- 检查IP和端口:确认电脑端脚本中绑定的IP是
0.0.0.0(监听所有网卡)或正确的本机局域网IP,端口是否一致。手机端填写的IP必须是电脑的局域网IP,不是127.0.0.1。 - 检查防火墙:这是最常见的原因。临时关闭电脑的防火墙(公共、专用网络都关掉)再试。如果连接成功,说明是防火墙拦截。需要在防火墙设置中为Python(或你使用的终端,如
python.exe)添加入站规则,允许TCP端口12345。 - 检查网络:确保手机和电脑连接的是同一个Wi-Fi网络。有时,如果电脑同时连着网线和Wi-Fi,可能导致IP路由混乱,尝试禁用其中一个网络适配器。
- 使用网络测试工具:在电脑上先用一个简单的Python Socket服务器测试,同时用手机上的网络调试助手App(如“TCP/UDP调试工具”)尝试连接,排除Mind+程序本身的问题。
- 检查IP和端口:确认电脑端脚本中绑定的IP是
7.2 NFC卡片读取失败或信息为空
- 现象:手机贴近卡片没反应,或读取到的UID是空。
- 排查步骤:
- 确认手机NFC已开启:并确认NFC感应区域(通常在手机背部上方)。
- 确认卡片类型:有些卡片(如银行卡、部分门禁卡)有安全保护,无法被普通APP读取UID。尝试用另一部手机或一个明确的NFC读写工具App测试该卡片。
- 检查Mind+ NFC扩展权限:确保Mind+生成的App已被授予NFC权限。可以在手机系统设置的应用权限管理中查看。
- 调整读取距离和位置:NFC感应距离很短(通常<4cm),且对位置敏感。慢慢移动卡片,找到最佳感应点。
7.3 Excel文件被锁定或写入失败
- 现象:Python脚本报错,提示文件被占用或权限不足。
- 原因与解决:
- 文件被打开:
nfc_records.xls文件正被Excel或WPS打开。写入前必须关闭这些程序。可以在代码中增加一个检查,如果文件被占用,则等待或提示用户。 - 权限问题:确保运行Python脚本的用户对当前目录有读写权限。尽量不要把脚本和Excel文件放在系统保护目录(如
C:\Program Files)下。 - xlutils兼容性问题:如果使用
xlutils.copy时出错,可能是库版本问题。可以尝试降级xlrd到1.2.0版本(因为新版xlrd不再支持xls格式),使用命令pip install xlrd==1.2.0。
- 文件被打开:
7.4 中文乱码问题
- 现象:接收到的数据或写入Excel的中文显示为乱码。
- 解决:
- 统一编码:确保整个数据流使用统一的编码,推荐UTF-8。在Socket发送和接收时,明确使用
encode('utf-8')和decode('utf-8')。 - xlwt编码设置:创建
xlwt.Workbook时指定encoding='utf-8'。 - 检查操作系统区域设置:虽然较少见,但可以检查一下。
- 统一编码:确保整个数据流使用统一的编码,推荐UTF-8。在Socket发送和接收时,明确使用
这个项目从构思到实现,最耗时的部分往往不是编码,而是环境调试和异常处理。特别是网络通信和软硬件交互部分,需要耐心地一步步排查。当第一次成功听到手机“嘀”的一声,看到电脑Excel里自动跳出一行新记录时,那种成就感是非常实在的。它不仅仅是一个工具,更是一个打通了移动硬件、网络通信和桌面应用的数据管道原型,理解了这套流程,很多类似的物联网或自动化采集需求你都能触类旁通。
