当前位置: 首页 > news >正文

学以致用——植物信息录入1.0(selenium+pandas+os+tkinter)

目的

书接上文,学以致用——植物信息录入(selenium+pandas+os+tkinter)

更新要点:

  • tkinter界面:自动登录、新增(核心功能)、文件夹选择、流程台
  • selenium自动化操作:验证码识别
  • excel数据:精准匹配key,获取相应value

操作优化:

  • 只需登录一次即可,操作员可以在当前文件夹完成后,继续选择新的文件夹运行,循环往复。
  • 验证码识别解放操作员手动登录
  • 录入表单的数据准确无误

后续:

  • 对物种信息的正确性验证
  • 自动跳转至属级提交表单
  • tkinter添加用户选择

回头看:

  • ddddocr库搭配PIL库,可以便捷地进行验证码图片识别,在本项目中完美地承担了读取验证码的任务。对于更复杂的验证码还没有验证。
  • selenium自动化操作网页时,在本项目后期运行期间,由于网络波动导致了大量的定位元素失败。由此我认识到WebDriverWait(详情请看代码中)在等待页面元素加载是至关重要的,且应用尽用。
  • selenium在定位frame等元素时,总是无法通过id、name、link_text等方法实现,具体原因未知。但可以采用遍历所有frame元素,再通过索引定位。
  • tkinter图形化是我比较常用的,也比较熟悉。或许将selenium内嵌在tkinter图形化里面有更好的用户体验。未来可以多试验试验。

整体代码

import pandas as pd
import time
import os
from tkinter import *
from tkinter.filedialog import askdirectory
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.keys import Keys
import ddddocr
from PIL import Imageclass Main:def __init__(self):self.root = Tk()self.root.attributes('-topmost', 1)  # 保持tkinter界面处于顶层,方便点击操作self.signin = Button(self.root, text='登录系统', command=self.sign_in) # 登录按钮self.signin.grid(row=0, column=0)self.addnew = Button(self.root, text='新增', command=self.add_new, state=DISABLED) # 新增按钮self.addnew.grid(row=0, column=1)self.folder = StringVar()Label(self.root, textvariable=self.folder).grid(row=1, column=0) # 显示文件夹路径self.choose_folder = Button(self.root, text='选择操作文件夹', command=self.inspect_folder, state=DISABLED) # 选择按钮self.choose_folder.grid(row=1, column=1)self.console = Text(self.root) # 显示台self.console.grid(row=2, column=0, columnspan=2)self.console.insert(0.0, '首先点击《登录》按钮,完成用户登录后,再点击《选择操作文件夹--属级文件夹》,最后点击《新增》\n') # 初始显示简易操作流程self.driver = webdriver.Chrome(service=Service(r"D:\ALL_Softwares\Python 3.10.4\Scripts\chromedriver.exe"))self.driver.maximize_window()self.wait = WebDriverWait(self.driver, 100)self.root.mainloop()def sign_in(self):try:self.driver.get('http://www.cn-flora.ac.cn:28080/plantonline/frame/toLoginPage')self.wait_for_send("_loginName", 'id', user)  # 用户名self.wait_for_send("_loginPwd", 'id', pwd)  # 密码self.wait.until(lambda d: d.find_element(By.ID, 'authImage')) # 等待验证码图片加载完成self.driver.save_screenshot(r'screenshot.png') # 截屏img_element = self.driver.find_element(By.ID, 'authImage') #定位到验证码元素location = img_element.location # 获取验证码坐标size = img_element.size # 获取验证码长宽zoom = 1.2 # 我的电脑是1.5rangle = (int(location['x'] * zoom), int(location['y'] * zoom), int(location['x'] * zoom + size['width'] * zoom),int(location['y'] * zoom + size['height'] * zoom)) # 根据电脑显示不同配置合适的裁剪大小i = Image.open(r'screenshot.png') # 打开截屏frame4 = i.crop(rangle) # 裁剪frame4 = frame4.convert('RGB')frame4.save(r'img.png') # 保存验证码图片ocr = ddddocr.DdddOcr() # 实例化验证码识别类with open(r'img.png', 'rb') as r:img_bytes = r.read()res = ocr.classification(img_bytes) # 完成识别self.wait_for_send('loginValidCode', 'name', res) # 验证码self.driver.find_element(By.NAME, 'loginValidCode').send_keys(Keys.ENTER) # 回车登录self.send_to_console('自动登录成功!选择操作文件夹已激活,请点击操作!')self.choose_folder.config(state=ACTIVE) # 激活选择操作文件夹self.signin.config(state=DISABLED) # 登录按钮失活self.root.update()os.remove(r'img.png')os.remove(r'screenshot.png')except:self.send_to_console('登录过程出现错误,请检查网络连接后重新尝试登录')def send_to_console(self, message, end='\n'):self.console.insert(END, message+end)self.console.see(END)self.root.update()def wait_for_send(self, element, element_type, content):if element_type == 'name':self.wait.until(lambda d: d.find_element(By.NAME, element))self.driver.find_element(By.NAME, element).send_keys(u'%s' % content)elif element_type == 'tag':self.wait.until(lambda d: d.find_element(By.TAG_NAME, element))self.driver.find_element(By.TAG_NAME, element).send_keys(u'%s' % content)elif element_type == 'id':self.wait.until(lambda d: d.find_element(By.ID, element))self.driver.find_element(By.ID, element).send_keys(u'%s' % content)def wait_for_click(self, element, element_type):if element_type == 'name':self.wait.until(lambda d: d.find_element(By.NAME, element))self.driver.find_element(By.NAME, element).click()elif element_type == 'tag':self.wait.until(lambda d: d.find_element(By.TAG_NAME, element))self.driver.find_element(By.TAG_NAME, element).click()elif element_type == 'id':self.wait.until(lambda d: d.find_element(By.ID, element))self.driver.find_element(By.ID, element).click()elif element_type == 'link':self.wait.until(lambda d: d.find_element(By.LINK_TEXT), element)self.driver.find_element(By.LINK_TEXT, element).click()elif element_type == 'xpath':self.wait.until(lambda d: d.find_element(By.XPATH, element))self.driver.find_element(By.XPATH, element).click()def inspect_folder(self):folder = askdirectory()if folder:self.folder.set(folder)self.send_to_console(f'成功读取 {folder}')self.send_to_console(f'请在网页中选择至{folder.split("/")[-1]}')self.choose_folder.config(state=DISABLED)filelist = os.listdir(folder)self.send_to_console(f'读取到{len(filelist)}个文件,请点击新增开始运行!')self.addnew.config(state=ACTIVE)else:self.send_to_console('未检测到选择操作文件夹,请选择')def add_new(self):folder = self.folder.get()count = 0total = len(os.listdir(folder))for file in os.listdir(folder):count += 1filepath = os.path.join(folder, file)self.send_to_console(f'{count} / {total} {file} 处理中...', end='--')self.send_to_console('处理数据中...', end='--')info = self.read_species(filepath)self.send_to_console(f'{info}', end='--')self.driver.switch_to.default_content()n = self.driver.find_elements(By.TAG_NAME, 'iframe')[1]self.driver.switch_to.frame(n)o = self.driver.find_elements(By.TAG_NAME, 'frame')[1]self.driver.switch_to.frame(o)p = self.driver.find_elements(By.TAG_NAME, 'button')p[2].click()# 定位到录入表单self.driver.switch_to.default_content()q = self.driver.find_elements(By.TAG_NAME, 'iframe')self.driver.switch_to.frame(q[1])r = self.driver.find_elements(By.TAG_NAME, 'frame')[1]self.driver.switch_to.frame(r)wait = WebDriverWait(self.driver, 100)wait.until(lambda d: d.find_element(By.NAME, 'acName'))if info['物种中文名'] != '':self.driver.find_element(By.NAME, 'acName').send_keys(u'%s' % info['物种中文名'])else:self.driver.find_element(By.NAME, 'acName').send_keys(u'%s' % info['物种学名'])wait.until(lambda d: d.find_element(By.NAME, 'acKeywords'))self.driver.find_element(By.NAME, 'acKeywords').send_keys(u'%s' % info['物种学名'])wait.until(lambda d: d.find_element(By.NAME, 'acExtendProperties'))self.driver.find_element(By.NAME, 'acExtendProperties').send_keys(u'%s' % info['俗名信息'])wait.until(lambda d: d.find_element(By.NAME, 'acRemark'))self.driver.find_element(By.NAME, 'acRemark').send_keys(u'%s' % info['分类概念依据'])wait.until(lambda d: d.find_element(By.TAG_NAME, 'iframe'))self.driver.switch_to.frame(self.driver.find_element(By.TAG_NAME, 'iframe'))self.driver.find_element(By.TAG_NAME, 'p').send_keys(u'%s' % info['形态特征'])self.driver.switch_to.parent_frame()wait.until(lambda d: d.find_element(By.NAME, 'acKuozhan3'))self.driver.find_element(By.NAME, 'acKuozhan3').send_keys(u'%s' % info['国内分布'])wait.until(lambda d: d.find_element(By.NAME, 'acKuozhan4'))self.driver.find_element(By.NAME, 'acKuozhan4').send_keys(u'%s' % info['国外分布'])wait.until(lambda d: d.find_element(By.NAME, 'acKuozhan5'))self.driver.find_element(By.NAME, 'acKuozhan5').send_keys(u'%s' % info['生境'])self.driver.find_element(By.TAG_NAME, 'button').submit()time.sleep(1)# wait = WebDriverWait(driver, 10)wait.until(EC.alert_is_present())  # 等待录入成功弹窗出现self.driver.switch_to.alert.accept()  # 确定成功time.sleep(1)  # 等待页面刷新os.remove(filepath)  # 防止意外终止后无法区分是否录入,成功后删除EXCEL文件self.root.update()self.send_to_console('新建成功并已删除文件!')self.send_to_console(f'已完成{folder}。可能存在文件无法检测到的情况,请检查文件夹中是否有未读取文件,如有请更改文件名后再次操作!继续选择操作文件夹或关闭界面!')self.choose_folder.config(state=ACTIVE)@staticmethoddef read_species(filepath):try:df = pd.read_excel(filepath, sheet_name='物种百科', keep_default_na=False)except:df = pd.read_excel(filepath, sheet_name='Sheet1', keep_default_na=False)info = {'物种学名': '', '物种中文名': '', '分类概念依据': '', '俗名信息': '', '形态特征': '', '生境': '', '国内分布': '', '国外分布': ''}for key, item in info.items():for row in df.itertuples():for value in row:if key == str(value).strip():info[key] = row[4].strip()breakreturn infoif __name__ == '__main__':a = Main()

当勉

程序当如诗一般,简单、简约。当然也不会一蹴而就,不断打磨才成精品。

相关文章:

学以致用——植物信息录入1.0(selenium+pandas+os+tkinter)

目的 书接上文,学以致用——植物信息录入(seleniumpandasostkinter) 更新要点: tkinter界面:自动登录、新增(核心功能)、文件夹选择、流程台selenium自动化操作:验证码识别excel数据&#xf…...

什么是压敏电阻

下面的这些都是压敏电阻,常常用在一些电源和信号的浪涌防护电路中。这个是它的电路符号,电路中常用RV表示。当压敏电阻两端电压小于压敏电压时,压敏电阻相当于一个阻值非常大的电阻。当压敏电阻两端电压大于压敏电压时,压敏电阻相…...

Leetcode.901 股票价格跨度

题目链接 Leetcode.901 股票价格跨度 Rating : 1709 题目描述 设计一个算法收集某些股票的每日报价,并返回该股票当日价格的 跨度 。 当日股票价格的 跨度 被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,…...

vue入门(四)组件基础,$emits简单用法

上一篇&#xff1a;vue入门&#xff08;三&#xff09;事件&#xff08;方法&#xff09;处理、侦听器、模板引用 1.组件最基础的用法&#xff1a; 首先有一个button.vue的组件&#xff0c;里面只画了一个按钮 button.vue: <script> export default({data(){return{but…...

VBA提高篇_27 OptionBOX_CheckBox_Frame_Image_VBA附加控件

文章目录1.单选按钮OptionBOX:2.复选框CheckBox:3.框架Frame:4.图像Image: (loadPictrue)5. VBA附加控件:6. 适用于很多控件的重要属性:1.单选按钮OptionBOX: 默认时,同一窗体的所有单选按钮均属于同一组,只能选中一个 可通过Frame控件进行分组解决. 2.复选框CheckBox: 一次可以…...

STM32开发(11)----CubeMX配置独立看门狗(IWDG)

CubeMX配置独立看门狗&#xff08;IWDG&#xff09;前言一、独立看门狗的介绍二、实验过程1.STM32CubeMX配置独立看门狗2.代码实现3.硬件连接4.实验结果总结前言 本章介绍使用STM32CubeMX对独立看门狗定时器进行配置的方法。门狗本质上是一个定时器&#xff0c;提供了更高的安…...

医疗方案 | 星辰天合入选“2022智慧新医信优秀解决方案”

近日&#xff0c;由 HC3i数字医疗网主办的《数字化转型驱动下的医院高质量发展论坛》暨 2022 智慧新医信优秀解决方案发布仪式在线上召开。XSKY星辰天合的“智慧医疗软件定义数据基础设施”解决方案成功入选 2022 智慧新医信优秀解决方案&#xff0c;。此次论坛由 HC3i 数字医疗…...

【系统服务实战】tomcat服务的安装实战

未来要更新的专栏&#xff08;此表格后面会继续完善&#xff09; 专栏系列学习路线完成情况云原生系列linux基本功系列-基础命令汇总已更新51个命令云原生系列linux基本功系列-系统服务实战正在更新文章目录前言一. tomcat的概述1.1 什么是tomcat1.2 tomcat的官网二. tomcat单…...

【图文详解】Unity存储游戏数据的几种方法

Unity3D存储游戏数据的方式1 PlayerPrefs: Unity自带的一种简单的键值存储系统2 ScriptableObject: Unity中最灵活的数据管理工具2.1 如何手动创建和修改数据文件2.2 ScriptableObject优缺点总结3 JSON: 轻量级的数据交换格式3.1 序列化与反序列化3.2 用JsonUtility对对象进行序…...

SESAM 安装教程

SESAM &#xff08;Super Element Structure Analysis Module&#xff09;是由挪威船级社&#xff08;DNV-GL&#xff09;开发的一款有限元分析&#xff08;FEA&#xff09;系统&#xff0c;它以 GeniE、HydroD 和 DeepC 等模块为核心&#xff0c;主要用于海工结构的强度评估、…...

语言文件操作

&#x1f331;博客主页&#xff1a;大寄一场. &#x1f331;系列专栏&#xff1a;C语言学习笔记 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 目录 前言 C语言中的文件打开和关闭 文件指针 文件的打开和关闭 fclose 文件的顺序读写 fseek ftell …...

Java面试题--熔断和降级的区别

熔断和降级都是系统自我保护的一种机制&#xff0c;但二者又有所不同&#xff0c;它们的区别主要体现在以下几点&#xff1a; 概念不同 触发条件不同 归属关系不同 1.概念不同 1.1熔断概念 “熔断”一词早期来自股票市场。熔断&#xff08;Circuit Breaker&#xff09;也…...

阅读笔记5——深度可分离卷积

一、标准卷积 标准卷积在卷积时&#xff0c;同时考虑了特征图的区域和通道信息。 标准卷积的过程如图1-1所示&#xff0c;假设输入特征图的channel3&#xff0c;则每个卷积核的channel都为3&#xff0c;每个卷积核的3个channel对应提取输入特征图的3个channel的特征&#xff08…...

Microsoft Dynamics 365:导入License到服务层,通过Business Central Administration Shell

本文主要是Microsoft Dynamics 365的License导入的图解干货&#xff0c;不多赘述&#xff0c;直接上图&#xff1a;第一步&#xff1a;准备好的License文件放在你喜欢的目录下第二步&#xff1a;到开始程序里找到并打开 Business Central Administration Shell3.第三步&#xf…...

centos6.10安装FastDfs出错的问题

在centos6.10虚拟机安装dfs文件服务器时&#xff0c;安装报错&#xff0c;报错为&#xff1a; gcc: error trying to exec cc1’: execvp: 没有那个文件或目录 1.ping www.baidu.con 排查网络是否通 2.yum update 排查yum源是否可用 3.yum源地址不可用时&#xff0c;修改yu…...

基础组件之内存池

内存池技术 操作系统在运行进程的过程中&#xff0c;会产生内存碎片&#xff0c;降低了内存的使用率。内存池技术就是为了解决/减少内存碎片的一种方法&#xff0c;内部底层的具体实现根据不同业务场景使用不要的方式&#xff0c;以下是一种好理解的方式&#xff0c;供大家一起…...

前端面试题--了解并简单介绍一下typescript

前端面试题–了解并简单介绍一下typescript TypeScript是JavaScript的超集&#xff0c;具有可选的类型并可以编译为纯JavaScript。 从技术上讲TypeScript就是具有静态类型的 JavaScript 。 向JavaScript添加静态类型的原因是什么&#xff1f;我想原因至少有三个&#xff1a; …...

【pytorch】ModuleList 与 ModuleDict

ModuleList 与 ModuleDict1、ModuleList2、ModuleDict3、总结1、ModuleList 1&#xff09;ModuleList 接收一个子模块的列表作为输入&#xff0c;然后也可以类似 List 那样进行 append 和 extend 操作: net nn.ModuleList([nn.Linear(784, 256), nn.ReLU()]) net.append(nn.…...

Hive窗口函数语法规则、窗口聚合函数、窗口表达式、窗口排序函数 - ROW NUMBER 、口排序函数 - NTILE、窗口分析函数

Hive窗口函数 文章目录Hive窗口函数语法规则窗口聚合函数窗口表达式窗口排序函数 - ROW NUMBER窗口排序函数 - NTILE窗口分析函数窗口函数也叫开窗函数、OLAP函数其最大特点&#xff1a;输入值是从SELECT语句的结果集中的一行或多行的“窗口”中获取的。如果函数具有OVER子句&a…...

Go设计模式之函数选项模式

目录引入函数选项模式&#xff08;functional options pattern&#xff09;可选参数默认值接口类型版本引入 假设现在需要定义一个包含多个配置项的结构体&#xff0c;具体定义如下&#xff1a; // DoSomethingOption 定义配置项 type DoSomethingOption struct {// a 配置aa…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...