目的
书接上文,学以致用——植物信息录入(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 Image
class 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.5
rangle = (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 = 0
total = len(os.listdir(folder))
for file in os.listdir(folder):
count += 1
filepath = 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)
@staticmethod
def 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()
break
return info
if __name__ == '__main__':
a = Main()
当勉
程序当如诗一般,简单、简约。当然也不会一蹴而就,不断打磨才成精品。

该文章介绍了一个使用Python结合Selenium、Pandas、OS和Tkinter库实现的自动化登录和植物信息录入系统。系统包括自动登录网页、选择文件夹、验证码识别和数据精确匹配等功能,旨在提高操作效率并减少错误。通过优化,实现了只需登录一次即可反复操作不同文件夹。作者还讨论了项目中遇到的问题,如网络波动影响Selenium定位元素,以及可能的解决方案。
1043

被折叠的 条评论
为什么被折叠?



