Selenium无input标签文件上传及保存样式重置问题
Selenium 自动化操作 trovo.live 修改用户头像问题方案
问题梳理
- 点击页面「Upload」按钮会直接唤起系统文件选择弹窗,通过
document.getElementsByTagName("input")全量遍历DOM,无法找到对应file类型的<input>元素,无法使用常规element.SendKeys()方式传入本地文件路径完成上传。 - 站点逻辑分析:手动选择文件后,站点会自动将选中图片转换为data URI格式,赋值给头像预览
<img>标签的src属性,同时设置style属性中的transform: matrix()参数,完成图片裁剪、缩放、定位调整全流程。 - 手动模拟逻辑失效:自行将目标图片转为data URI赋值给预览img的src属性,同时手动计算matrix参数设置对应transform样式后,点击保存按钮站点会重置手动设置的CSS样式,最终保存的头像定位错误;通过Upload按钮手动上传时设置的matrix参数可被正常识别保存。
现有约束
- HTML静态源码中不存在承载文件上传功能的
<input>标签 - 无法使用
System.Windows.Forms.SendKeys.SendWait()方法向系统弹窗传入文件路径 - 引入AutoItX等第三方库处理弹窗存在项目兼容问题,优先要求不依赖第三方库实现
- 已定位到站点实现上传功能的JS源码,但不清楚如何直接调用内部图片缩放、上传相关方法
对应解决方案
1. 介入保存按钮执行逻辑,强制传入自定义matrix参数
保存时手动设置的CSS被重置,核心原因是站点仅认自身上传流程中写入内存的裁剪状态参数,不会读取DOM上手动修改的style值。
通过Selenium的execute_script能力即可实现参数注入:
- 先在浏览器控制台打断点调试,找到保存逻辑中读取裁剪参数的内部状态变量,通常挂载在window全局对象或组件实例上,例如
window.__avatarCropper.config这类路径 - 点击保存按钮前,通过JS直接修改对应内部状态变量,把提前计算好的6个matrix参数赋值进去,再触发原保存逻辑,站点不会识别到参数被篡改,也不会重置样式
- 核心代码示例:
// 实际变量路径需通过断点调试确认,此处为示例 window.__trovoAvatarModule.cropConfig.matrix = [scaleX, skewY, skewX, scaleY, translateX, translateY]; // 触发原生保存逻辑 document.querySelector('.avatar-save-submit').click();
2. 绕过按钮点击,直接模拟站点上传函数逻辑
既然已经定位到站点上传相关的JS源码,完全不需要触发文件选择弹窗,直接构造参数调用内部上传方法即可:
- 在源码中定位到文件选择完成后的回调函数,这类方法通常接收
File/Blob对象作为入参,负责后续的图片解析、裁剪初始化、预览渲染流程 - 通过Selenium执行JS,先将目标图片转为对应格式的
Blob对象(可先读取本地图片为base64,再转换为Blob,注意MIME类型需和图片实际格式匹配) - 将构造好的Blob对象作为入参,直接调用定位到的内部上传回调方法,后续站点会自动走完原生的裁剪、预览、保存流程,不需要手动修改DOM样式
- 如果目标方法在闭包内未暴露到window全局,可通过劫持事件监听、断点拦截的方式拿到方法引用,挂载到window上后再调用。
3. 无第三方依赖,通过Selenium原生能力处理文件选择弹窗
找不到file类型input通常是两种原因:一是input是点击Upload按钮时动态创建的,触发文件选择后立刻从DOM移除,常规遍历时机不对抓不到;二是input被放置在shadow DOM内部,普通DOM遍历方法无法穿透查找,两种情况都可以用Selenium原生能力解决:
- 针对动态创建input的场景:点击Upload按钮前,先通过JS重写
document.createElement方法,监听type为file的input创建时机,将元素缓存到window自定义变量上,同时拦截input的click事件阻止系统弹窗唤起。点击Upload按钮后,直接从缓存变量拿到file input元素,用Selenium原生的sendKeys传入本地文件绝对路径即可,不需要处理系统弹窗。
核心拦截代码示例:window._cachedFileInput = null; const originCreate = document.createElement.bind(document); const typeProp = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'type'); document.createElement = function(tag) { const el = originCreate(tag); if (tag.toLowerCase() === 'input') { typeProp.set.call(el, 'file'); Object.defineProperty(el, 'type', { set: function(val) { if (val === 'file') window._cachedFileInput = el; return typeProp.set.call(this, val); }, get: () => typeProp.get.call(el) }); el.addEventListener('click', e => { e.stopImmediatePropagation(); e.preventDefault(); }, true); } return el; } - 针对shadow DOM场景:递归遍历页面所有元素的
shadowRoot节点,在每个shadowRoot内部查找type为file的input元素,找到后直接调用sendKeys传入文件路径即可,无需唤起系统弹窗。
内容的提问来源于stack exchange,提问作者SPIRINN
相关产品推荐
相关产品推荐

