现场打脸:如何使用Selenium批量上传文件?
摄影:产品经理
霸王餐里面的牛蛙
我们知道,Selenium里面,当我们获得一个 element 对象的时候,如果它是一个输入框,那么我们可以使用.send_keys()
方法,模拟键盘按键,发送特定的字符串到输入框中,例如:
input_box = driver.find_element_by_xpath('//input[@class="xxx"]')
input_box.send_keys('账号 xxx')
但如果要说.send_keys()
可以上传文件,你可能会非常吃惊吧。今天有个读者在问我的时候,我也非常吃惊,觉得这怎么可能:
结果我到 Selenium 的文档里面一看,发现send_keys()
竟然真的可以上传文件: 8.5. How to upload files into file inputs ?[1]
为了验证这个说法,我们使用 Flask 手写一个支持上传功能的简陋网站。网站代码如下:
网站运行效果如下图所示:
点击“选择文件”按钮,在弹出的对话框里面选中一个文件,然后点击“Upload”按钮,就会把文件上传到代码里面的uploads
文件夹中,如下图所示:
现在我们在 Selenium 里面进行测试:
from selenium.webdriver import Chrome
driver = Chrome('./chromedriver')
driver.get('http://127.0.0.1:5000')
file_input = driver.find_element_by_xpath('//input[@type="file"]')
file_input.send_keys('/Users/kingname/test_send_keys/target/x.txt')
submit = driver.find_element_by_xpath('//input[@type="submit"]')
submit.click()
经过测试,发现确实可以正常上传文件。如下图所示:
这样一来,既然 .send_keys()
能够正常工作,那么就可以反向推测出,浏览器上传文件的原理,选择文件的对话框实际上提供给浏览器的仅仅是一个文件路径。当我们点击了上传按钮以后,浏览器会根据这个路径去读硬盘,找到这个文件然后上传。由于文件路径本质上就是一个字符串,所以用.send_keys()
本质上就是直接替代了选择文件对话框生成的文件路径,直接把这个路径上传给了文件输入表单。
那么如何一次性上传多个文件呢?
只要网站支持同时上传多个文件,那么我们可以把多个文件的路径拼接到一个长字符串中,路径与路径之间使用换行符n
来进行分割。
假设在文件夹/Users/kingname/test_send_keys/target
里面有多个文件,如下图所示:
我们需要一次性全部上传。那么,可以使用换行符把每一个文件的路径拼接起来:
代码可以写为:
import os
from selenium.webdriver import Chrome
folder = '/Users/kingname/test_send_keys/target'
file_name_list = os.listdir(folder)
path_list = [os.path.join(folder, x) for x in file_name_list]
path_split_by_newline = 'n'.join(path_list)
driver = Chrome('./chromedriver')
driver.get('http://127.0.0.1:5000')
file_input = driver.find_element_by_xpath('//input[@type="file"]')
file_input.send_keys(path_split_by_newline)
submit = driver.find_element_by_xpath('//input[@type="submit"]')
submit.click()
运行效果如下图所示:
成功上传多个文件。
参考资料
[1]
8.5. How to upload files into file inputs ?: https://selenium-python.readthedocs.io/faq.html?highlight=send_keys#how-to-upload-files-into-file-inputs
- 洛谷P1919 【模板】A*B Problem升级版(FFT快速傅里叶)
- win10下vagrant+centos7 rails虚拟开发机配置流程
- 再免费多看一章--k-means++
- 面向对象先导课感想
- 【LATEX】个人版latex论文模板
- 【前端】wangEditor(富文本编辑器) 简易使用示例
- 【前端】ACE Editor(代码编辑器) 简易使用示例
- LOJ#6277. 数列分块入门 1
- 【Ruby on Rails】Model中关于保存之前的原值和修改状态
- 一个很逗的东西——Jd
- SQL Server 数据库清除日志的方法
- 算法模板——平衡树Treap 2
- DTS,DTC, DTB你都认识吗?
- LOJ#6278. 数列分块入门 2
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- leetcode之最长回文串
- Discourse CentOS 8 全新安装手册
- 3分钟短文:Laravel路子真野啊!路由昵称前缀中间件
- CSS中重要的BFC概念
- Redis哨兵集群中哨兵挂了,主从库还能切换吗?
- 你的 Redis 为什么变慢了?
- 解决Maven依赖冲突的好帮手,这款IDEA插件了解一下?
- Python爬虫实现HTTP网络请求多种实现方式
- 在tensorflow以及keras安装目录查询操作(windows下)
- Python调用OpenCV实现图像平滑代码实例
- php微信公众号开发之音乐信息
- Laravel关联模型中过滤结果为空的结果集(has和with区别)
- php微信公众号开发之二级菜单
- django中的ajax组件教程详解
- php微信公众号开发之校园图书馆