目录
一、功能需求
产线部门的远程服务器存储着产线大数据,现需要获取其中一些数据并进行分析。但由于距离、流程及权限等问题,无法一次性所需数据,只能通过其对外提供的一个功能简单(开发框架比较老旧低级)的下载页面进行下载,极其繁琐。每次下载时,面临以下问题:
- 每次下载,需要手动重复设置部分参数。
- 下载的数据起止区间有最大时长限制,导致每次下载完成后都需要手动另外设置下一个顺接的下载区间。
- 每次下载必须等待上一次下载完成才可以有效进行,而且等待下载时长较长且不固定(下载时间和数据量大小及网络状况有关,有时可能半小时、个把小时左右,也有可能几分钟)。
- 下载的打包zip数据里包含大量单个txt文件,但web页面却以当前点击下载按钮的时间对下载文件命名,无法做到见名知意,影响后续数据读取和分析。
- 人工下载,不能做到连续不间断得进行下载:有时会被其他工作耽误,下班后空闲的机器及整夜时间也无法充分高效利用……
据上,依赖人工守候及手动模式下载几个月的数据,变得低效繁琐且毫无意义,特别的会耽误获取数据的及时性。因此,开发一个自动下载小工具则有了意义。
二、技术方案
对下载页面进行了初步分析后,发现页面功能和元素构成比较简单:
- form表单+post请求方式。点击下载按钮后,请求发给自身,请求经过校验合法后,开始后台下载指定数据文件。过程中看不到任何构造的请求链接,看不到任何XHR请求。
- 数据处理加载没有JS脚本,只有错误提示时会弹出一个JS框。
- 页面主要元素为下拉框、复选框、文本框。
- 不存在frame切换情形。
由于POST请求且看不到XHR请求,无法直接构造请求链接,因此打算使用【Selenium+Chrome】进行操作。
三、开发笔记
3.1 用到的库
主要用到的库如下:
from selenium import webdriver
from selenium.webdriver.common.by import By # 查找元素
from selenium.common import exceptions # 异常捕获
from selenium.webdriver.support.ui import WebDriverWait # 设置元素加载显示等待时间
from selenium.webdriver.support import expected_conditions as EC # 等待事件发生
import datetime # 时间加减操作
import glob # 文件名模糊查找操作,可结合os.path使用
import os # 文件删除、重命名、是否存在等操作
3.2 datetime的时间加减计算
标准库datetime的功能很强大,支持以天、小时、分钟、秒为单位的加减乘除操作。这里由于每次下载需要自动计算并设置下载起止时间,因此用到了该库。
主要操作包括:
- 【strptime函数】:将日期字符串转换为datetime类型(然后才能进行时间操作)
dload_start_date_str = '2021-02-01 00:00:00'
dload_start_date = datetime.datetime.strptime(dload_start_date_str, '%Y-%m-%d %H:%M:%S')
-
【tiemdelta函数】:设置时间操作单位和数值(然后进行时间加减等操作)
load_end_date = dload_start_date + datetime.timedelta(hours=1) # 另外支持seconds等
- 日期类型转为指定格式的日期字符串
date_str = dload_start_date.strftime("%Y%m%d%H") # 与strptime作用相反
3.3 文件操作
这里主要利用glob库、os库实现文件的模糊查找、删除、文件重命名操作。
searched_files_list = glob.glob(search_dload_path_fuzname) # 包含路径完整文件名
searched_files_num = len(searched_files_list)
if searched_files_num > 1:
print('-- ERROR!查找到了多个文件!!')
print(searched_files_list)
break
if searched_files_num == 1:
dload_path_filename_raw = searched_files_list[0]
dload_filename_raw = dload_path_filename_raw[-19:] # 去除路径和后缀的文件名
# 重命名文件,方便查看使用
dload_filename_new = each_dload_start_end_date_str + '(' + dload_filename_raw[:-4] + ').zip'
dload_path_filename_new = DLOAD_PATH + dload_filename_new
if os.path.exists(dload_path_filename_new):
print('-- WARNING!存在同名文件,执行删除操作!')
os.remove(dload_path_filename_new) # 注:remove方法没有返回值!
os.rename(dload_path_filename_raw, dload_path_filename_new)
3.4 selenium印象深刻的操作
这次第一次接触到了selenium的部分功能。
- 处理js弹出框(警告类型,点击确定即可关闭)。
is_alert_pre = EC.alert_is_present()(browser) # 后面传参浏览器实例,返回True or False
if is_alert_pre:
alert_window = browser.switch_to.alert # switch_to.alert属性
# print(alert_window.text) # alert文本属性
time.sleep(1)
alert_window.accept() # 点击弹出框的“确定”按钮
-
下拉框选择和选项赋值
browser.find_element_by_id('dropProduct').find_element_by_xpath(
"//option[@value='" + project_name + "']").click() # 查找下拉框并点击赋值
3.5 异常处理
由于网页开发的比较单纯,会莫名其妙出现一些错误,另外也会偶尔出现组件查找、点击、加载方面的错误,因此使用了大量try catch except语句捕获异常并进行针对性处理,处理措施包括但不限于:
- 网页加载失败时重新加载。
- 页面元素查找、点击、值设置失败时,重新操作。
- 下载失败时,跳过当前下载继续下次下载。
- 循环间隔一定时间自动判断文件是否下载完成,超过最大限定下载时间后继续下次下载。
- 服务器莫名响应错误处理重新加载网页,保证下载不间断。
- ……
总之,以上的异常捕获和处理都是为了一个目的:无论发生何种类型错误,都能保证下载不间断以及继续,直到下载完所有有效数据。
四、总结
- 简单搜查了一下,form表单post方式提交请求时,链接构造应该也有一套规则的,但学校时学的那点web开发知识忘的差不多了,一时半会也没搞清楚如何构造链接。
- 涉及技术简单,未用到爬虫框架,但业务层面的处理所花费的时间精力远超技术开发的。说明有些项目做到最后,做得都是业务……
- 自动化极大的提高了下载时间,使原来需要花费一人约一个月的下载时间,使用单线程已经缩短到3~4天;如果采用多线程,则会更快!这就是工具的力量和效率!