news 2026/9/9 10:55:48

Selenium元素定位与交互实战:从入门到稳定落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Selenium元素定位与交互实战:从入门到稳定落地

1. 先把这件事想清楚:Selenium元素操作到底在解决什么问题

做自动化测试也好,写爬虫也好,接触Selenium的第一道坎几乎都是元素操作。原因很简单:所有后续动作——点击、输入、拖拽、断言——都建立在“你能找到那个元素”这个前提上。元素定位不稳,后面所有代码都是空中楼阁,今天能跑明天就挂,这在业界叫“脆弱的自动化”。

我见过不少同学学Selenium,上来就死记XPath语法,背完发现实际项目里照样抓瞎。问题不出在语法,出在没理解定位的本质:我们在模拟真实用户的眼睛。真实用户靠视觉找按钮,Selenium靠DOM结构里的特征找节点。所以定位策略的核心,就是找一组在目标页面上足够稳定、足够唯一的特征,这组特征还要能扛住页面改版。

这篇文章基于我在HoRain云环境里做Web自动化测试的实践经验,把从环境搭建、浏览器驱动选型、定位策略到高效交互的完整链路串起来讲。重点放在“为什么这么选”和“哪些坑我提前替你踩过了”,代码用Python写,因为目前测试圈和爬虫圈用Python+Selenium的组合最主流,遇到问题搜解决方案也最容易。

适合谁看:刚开始学Selenium的测试新人、想用Selenium做数据采集的爬虫开发者、以及写自动化脚本总遇到“元素找不到”报错的老手。看完你至少能解决80%的定位和交互问题。

2. 环境准备与浏览器驱动选型,这一步错了后面全白搭

2.1 Selenium安装其实没什么好纠结的

安装本身一句话就够:pip install selenium。但很多人装完就跑demo,跑完第一次能开浏览器,第二次就报错,绝大多数问题出在驱动上。Selenium本身只是个自动化协议客户端,真正去操控浏览器的是各个浏览器厂商提供的Driver,Chrome叫chromedriver,Firefox叫geckodriver,Edge叫msedgedriver。

这里有个容易忽略的点:Selenium 4.x和3.x的某些API有差异。比如定位元素,3.x的find_element_by_id写法在4.x里被标记为废弃,推荐换成find_element(By.ID, "xxx")这种写法。网上大量教程还是老写法,你照抄完发现自己的Selenium 4.10版本跑不起来,不是代码错了,是版本差异。建议装新版,然后统一用By这个枚举类,兼容性最好。

2.2 浏览器驱动到底怎么判断下载哪个,全网最乱的问题

“web自动化selenium浏览器驱动怎么判断下载哪个区别”这个搜索词天天有人查,我直接给结论。Chrome浏览器点右上角三个点,进“关于Chrome”,能看到类似“版本 126.0.6478.126”这样的信息。然后去chromedriver的下载列表里,找和你主版本号一致的最新版。主版本号就是第一个数字,126对126,不是非要精确到后面。

这里有个特例必须提醒:Chrome更新很勤快,有时Chromedriver还没跟上最新版,这时候你只需要找到最接近且小于你浏览器版本号的驱动,通常能正常工作。反过来,如果你用Selenium Manager(Selenium 4.6起内置),它会自动帮忙匹配驱动,但国内网络环境下有时下载慢或失败,所以手动下载驱动放到项目目录下,再用service = Service("chromedriver路径")指定,这种做法最可控。

Firefox和Edge同理,主版本号匹配即可。忘了自己装没装驱动,命令行敲chromedriver --version,能输出版本号就没问题。

2.3 快速验证环境可用的三行代码

环境装没装对,别急着写复杂脚本,先跑最小用例:

from selenium import webdriver driver = webdriver.Chrome() driver.get("https://www.example.com") print(driver.title) driver.quit()

能弹出浏览器、能打印出页面标题、能正常关闭,说明Selenium、驱动、浏览器三者已经打通。这一步跑不过,后面写再多都是白费。跑不过的时候按顺序排查:Python环境是否正常、selenium是否装进当前解释器、chromedriver是否在PATH里或路径是否正确、浏览器和驱动主版本号是否一致。我自己遇到过最傻的情况:系统里有两个Python环境,pip install装到A环境,脚本用B环境跑,折腾半小时才反应过来。

3. 元素定位八种武器,实际项目中怎么选才不翻车

3.1 ID定位:能用的优先用,没有就造一个

页面元素的id属性在HTML规范里要求唯一,所以find_element(By.ID, "login-btn")是性能最好、最稳定的定位方式。我写自动化脚本的第一原则:元素有id就用id,没有id再看其他。这不是因为我懒,而是id是前端工程师最常设置、最不容易重复的属性。

但现实是全国统一的前端框架下,很多团队写代码时id起得随意,比如btn1btn2这种,页面一改版就迁走。遇到这种情况,我不建议硬啃id,可以看一下元素附近有没有稳定的父节点,用相对定位去锁定。另外有些id是动态生成的,比如带时间戳或随机数的,每次刷新都变,这种id等于没有,别浪费时间。

3.2 XPath定位:不要只会复制,要会写“稳”的表达式

XPath是万金油,因为它能表达任意复杂的关系:兄弟节点、父节点、文本内容、属性组合。浏览器devtools里右键元素可以直接复制XPath,但复制出来的通常是绝对路径,又长又脆。我偏好手写相对XPath,围绕元素的稳定特征来写。

几个实用规则:

  • 属性定位://button[@class='submit-btn'],比绝对路径短得多。
  • 文本定位://span[contains(text(),'确认')],适合按钮文字经常变但关键字不变的场景。
  • 组合定位://div[@class='modal']//button[contains(@class,'primary')],先圈范围再精确定位,比单个长表达式更抗改版。
  • 避免使用下标://div[1]/div[2]/div[3]这种看着就脆,多一个节点整体失效。

XPath还有个容易踩的坑:属性值里的引号。如果属性值本身包含双引号,外层就用单引号,反过来一样。//button[contains(@title,'他说"确认"')]这种嵌套写法经常把人绕晕,我用的时候都先把属性值复制出来看一眼,确定引号类型再组装。

3.3 CSS Selector:性能好、写法短,建议和XPath双修

很多人只学XPath不学CSS,其实CSS Selector在Selenium里的表现非常优秀,语法也简单。#id是id选择器,.class是class选择器,[name='xxx']是属性选择器,div > p是父子关系。最关键的是:CSS Selector无法按文本内容定位,遇到“我想找文字是‘确认’的按钮”这种需求,CSS无能为力,必须用XPath,这是两者最大的能力分界线。

所以我的使用习惯是:能用CSS就用CSS,需要按文本定位或用复杂轴关系定位时切XPath。实践中这句话能解决大部分选择困难:

from selenium.webdriver.common.by import By # CSS方式 driver.find_element(By.CSS_SELECTOR, "button[data-action='submit']") # XPath方式 driver.find_element(By.XPATH, "//button[contains(text(),'提交')]")

3.4 其他四种定位方式,什么场景才值得用

除了ID、XPath、CSS,Selenium还提供By.NAMEBy.CLASS_NAMEBy.TAG_NAMEBy.LINK_TEXTBy.PARTIAL_LINK_TEXT

  • By.NAME:表单元素常见,inputselectname属性经常是后端接口的字段名,稳定性尚可,适合表单页。
  • By.CLASS_NAME:类名经常多个类叠加,比如class="btn btn-primary",用By.CLASS_NAME只匹配其中一个就行,但类名很容易被前端改样式时调换。
  • By.TAG_NAME:只适合批量找标签,比如获取页面上所有img标签,正常定位不用它。
  • By.LINK_TEXT:精确匹配链接文字;By.PARTIAL_LINK_TEXT是模糊匹配。只对<a>标签生效,适合翻页链接、导航菜单这类场景。

有个套路很好用:页面上一组结构相同的元素(比如多条数据列表),可以用find_elements配合索引或者遍历。这个方法我后面会专门讲。

3.5 八种定位方式对比速查

定位方式语法示例稳定性性能典型场景
IDBy.ID极高表单输入框、唯一按钮
NameBy.NAME传统表单页
Class NameBy.CLASS_NAME样式稳定的元素
Tag NameBy.TAG_NAME批量取标签
Link TextBy.LINK_TEXT导航链接、翻页
Partial LinkBy.PARTIAL_LINK_TEXT链接文字动态变化
XPathBy.XPATH中慢按文本、复杂关系
CSS SelectorBy.CSS_SELECTOR属性组合、层级结构

我的建议是:主修CSS和XPath两种,其他六种了解即可。定位能力不是背出来的,是用真实的页面反复调试练出来的。

4. 等待策略:精准定位的真正基石,比定位语法更重要

4.1 三种等待方式,很多人的失败都栽在这里

页面元素找不到,十个里有八个是时机问题:元素还没加载出来,代码就已经开始找了。Selenium提供三种等待,很多人混着装,实际上它们的天职完全不同。

  • 强制等待time.sleep(3):写死3秒,简单粗暴,但不管元素1秒就出来还是5秒才出来,都等3秒,慢且不可靠。
  • 隐式等待driver.implicitly_wait(10):设置一个全局的超时时间,每次find_element找不到元素时,会在这个时间内轮询等待。但它对“元素找到了但还没可点击、还没可见”这种状态毫无办法。
  • 显式等待WebDriverWait配合expected_conditions:针对某个元素、某种状态等待,既精准又高效,这才是自动化测试应该有的样子。

实际操作中,我不建议一上来就写显式等待,而是先判断页面用的是同步渲染还是异步渲染(Ajax)。同步页面加载完DOM就完整了,隐式等待基本够用;异步页面需要等接口返回后DOM才更新,必须显式等待。

4.2 显式等待的实战写法

from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By wait = WebDriverWait(driver, 10) # 等待元素可见并可点击 submit_btn = wait.until( EC.element_to_be_clickable((By.XPATH, "//button[contains(text(),'提交')]")) ) submit_btn.click()

这里有个关键点:element_to_be_clickablepresence_of_element_located更严格,前者要求元素在DOM里且可见且可交互。对于按钮类操作我一般用前者;对于只需要读取文本的元素,用后者就够了,效率更高。

另一个进阶技巧:显式等待虽然好,但不要给每个元素都单独写一个WebDriverWait,那会让代码啰嗦到没法看。我习惯在测试工具类里封装一个方法:

def wait_and_click(driver, locator, timeout=10): element = WebDriverWait(driver, timeout).until( EC.element_to_be_clickable(locator) ) element.click() return element

调用时传一个元组:wait_and_click(driver, (By.ID, "submit")),脚本清爽很多。

4.3 解决动态加载页面的定位难题

现代前端大量使用Vue、React这类框架,页面元素是异步渲染的,经常出现“元素存在但内容为空”“数据加载一半就返回了”的诡异情况。我处理这类页面的通用思路是三步:

  1. 先轮询等待数据接口对应的网络响应完成(用driver.execute_script监听performance或者简单点,等某个loading图标消失)。
  2. 再等目标元素出现在DOM中。
  3. 最后再等元素状态变成可交互。

其中第二步和第三步就是上面讲的显式等待。第一步很多人忽略,但它能帮你规避掉大量“元素找到了,值还是空的”这种bug。如果你用Selenium 4,可以试试WebDriverWaitpoll_frequency参数,默认0.5秒轮询一次,改成0.1秒能让定位更快,但会增加CPU开销,小项目无所谓,大项目要权衡。

4.4 等待策略的避坑清单

  • 隐式等待和显式等待不建议混用,混用的结果是你明明设了10秒超时,实际可能等20秒,因为Selenium的等待机制会叠加。
  • 不要对find_elements用隐式等待的预期,它可能返回一个空列表而不是抛异常,你拿空列表再去遍历就会触发其他错误。
  • time.sleep不是不能用,而是在等待某个动态元素的同时你又没有更好的等待条件时,短sleep(0.5~1秒)作为兜底方案完全合理,但绝对不能全篇都是sleep。

5. 高效交互实战:从点击输入到JS操作父页面元素

5.1 最常用的交互:点击、输入、键盘、清空

点击和输入是两个最基本的操作,但用起来有不少细节。

# 输入前先清空,避免残留文本 element = driver.find_element(By.ID, "username") element.clear() element.send_keys("test_user") # 键盘操作 from selenium.webdriver.common.keys import Keys element.send_keys(Keys.CONTROL, "a") # 全选 element.send_keys(Keys.BACKSPACE) # 删除 element.send_keys(Keys.ENTER) # 回车 # 点击 driver.find_element(By.XPATH, "//button[text()='登录']").click()

clear()的时机问题很多人忽略:如果输入框有默认值,直接send_keys会在默认值后面追加,导致数据不对。所以每次输入前先clear(),这是写自动化最容易被忽略的细节之一。

还有些前端的输入框是只读的,比如时间选择器,send_keys进不去。这时候套路是先用JS修改元素的readonly属性,再输入:

driver.execute_script("arguments[0].removeAttribute('readonly')", element) element.send_keys("2025-01-01")

5.2 下拉框、复选框、单选框的正确打开方式

原生<select>标签的下拉框,用Selenium提供的Select类最方便:

from selenium.webdriver.support.ui import Select select_elem = Select(driver.find_element(By.NAME, "city")) select_elem.select_by_visible_text("北京") # 按显示文本选 select_elem.select_by_value("beijing") # 按value值选 select_elem.select_by_index(2) # 按下标选,从0开始

但前端框架(比如Element UI、Ant Design)渲染的下拉框根本不是原生select,而是点击后出现一个浮层,里面是一堆lidiv。这种不能直接套Select类,处理方式是:先点击触发下拉,再用显式等待浮层出现,然后从浮层里定位目标项。

复选框的勾选,除了直接click(),还有一种情况:元素本身被遮挡,点击报“other element would receive the click”。这时候用JS强制点击:

driver.execute_script("arguments[0].click();", checkbox_element)

这个技巧适用面很广,后面讲常见问题还会提。

5.3 多表单切换与iframe里的父页面元素操作

嵌套页面是自动化里的隐形杀手。一个页面里嵌了iframe,你的find_element直接去找里面的元素,必然找不到,因为Selenium默认只在主文档里找。需要先切换进去,再操作,再切回来:

iframe = driver.find_element(By.TAG_NAME, "iframe") driver.switch_to.frame(iframe) # 在iframe里操作 driver.find_element(By.ID, "inner_input").send_keys("hello") # 操作完切回主文档 driver.switch_to.default_content()

关于搜索词里那个parent.layer.getframeindex 操作父页面元素,这其实是layui框架里父子页面交互的典型问题。场景是:父页面打开一个iframe弹窗,弹窗里要操作父页面的某个元素。parent.layer.getframeindex()拿的是当前iframe在layer体系里的索引索引,配合它去定位父页面元素。

如果你在Selenium里遇到这种父子页面嵌套,正确解法不是用parent这个JS对象(那是浏览器窗口层面的事),而是用Selenium自己的frame切换。先switch_to.parent_frame()返回上一层,再定位父页面的元素,然后switch_to.frame(iframe)回到子页面继续操作。

# 假设当前在iframe中,切回父层 driver.switch_to.parent_frame() parent_btn = driver.find_element(By.ID, "parent_btn") parent_btn.click() # 再切回iframe driver.switch_to.frame(driver.find_element(By.CSS_SELECTOR, "iframe[lay-id='myLayer']"))

5.4 弹出框、Alert、文件上传这些特殊交互

原生浏览器的alert/confirm弹窗,不是DOM元素,用switch_to.alert处理:

alert = driver.switch_to.alert print(alert.text) # 获取弹窗文本 alert.accept() # 点确定 # alert.dismiss() # 点取消

文件上传是另一个经典难关。如果页面上有<input type="file">,不需要真的去操作系统文件对话框,直接把文件路径发给输入框就行:

upload_input = driver.find_element(By.CSS_SELECTOR, "input[type='file']") upload_input.send_keys("/path/to/myfile.pdf")

但如果上传组件是自定义的,点击后调用底层文件选择窗口,Selenium就无能为力了。这时候我一般用pyautogui或者autoit这类操作系统层面的工具去配合输入文件路径,或者直接调用前端暴露的上传接口。

5.5 JS操作元素:定位之外的另一条腿

Selenium的execute_script能执行任意JavaScript,这在很多场景里比正规API还好用。搜索词里提到的“js数组操作元素”,在自动化里通常指用JS批量处理一组元素的场景。比如我想拿到页面上某列表的所有文本:

texts = driver.execute_script( "return Array.from(document.querySelectorAll('.item-list span')).map(el => el.textContent)" )

这个写法用Array.from把NodeList转成数组,再map出文本,一次性拿到所有数据,比find_elements然后逐个取文本效率高不少。

还可以用JS做很多Selenium原生做不到的事:

  • 拿到元素的任意属性:arguments[0].getAttribute('data-id')
  • 改变元素样式做调试标记:arguments[0].style.border='2px solid red'
  • 强制触发事件:arguments[0].dispatchEvent(new Event('change'))
  • 直接操作Vue/React组件的内部状态(虽然不建议,但应急时很好用)

一个真实的例子:某后台管理系统用Vue写了一个自定义日期组件,点击后浮层渲染到body下面,Selenium怎么都定位不到。后来我用execute_script找到浮层里的输入框,直接赋值并触发input事件,问题秒解。JS操作元素这条腿一定要学会,因为它能绕过很多前端框架的坑。

5.6 用ActionsChains模拟鼠标键盘组合操作

鼠标悬停、拖动、双击、右键这些操作,用ActionsChains

from selenium.webdriver.common.action_chains import ActionChains actions = ActionChains(driver) # 悬停到菜单上,再点击子菜单 menu = driver.find_element(By.ID, "nav-menu") child = driver.find_element(By.LINK_TEXT, "子菜单") actions.move_to_element(menu).click(child).perform()

这里有个坑:ActionChains必须用perform()才会真正执行,很多人写完忘掉,然后一脸懵地找bug。还有个细节:执行完后最好sleep一下再走下一步,因为浏览器执行完操作到界面反应有一个微小的时间差,不等待容易出偶发失败。

拖拽用的drag_and_drop(source, target),在实现“把文件拖到上传区域”这类场景里经常遇到,注意目标区域一定要可见,否则拖拽无效。

5.7 element.click()被拦截时的高效兜底方案

正常交互里最常见的神秘报错:

selenium.common.exceptions.ElementClickInterceptedException: Element <button> is not clickable at point (x, y). Other element would receive the click

翻译成人话:元素找到了,也可见,但页面上有个东西盖住了它。盖住它的通常是一个fixed定位的弹层、loading遮罩、或者广告浮层。兜底方案有几种:

  1. 等遮罩消失再点(显式等待它消失)。
  2. 用JS强制点击,无视遮挡(:hover有时需要先hover上去)。
  3. ActionChains先点击别处关闭浮层,再点目标元素。

我推荐优先用方案1,因为强制点击容易掩盖真实问题——比如按钮其实是因为某个校验没通过暂时不可用,你强制点了反而让后续依赖状态错乱。方案2适合页面有广告弹层这种与业务无关的干扰物,在爬虫场景里特别常用。

6. 常见问题与排查技巧实录

6.1 定位不到元素怎么排查,按这个顺序来

我把“元素找不到”的排查思路整理成一套固定流程,遇到问题照着走,比自己瞎试快得多:

排查步骤操作常见结论
1确认页面是否加载完成网络慢导致元素没出现
2在DevTools中手动确认元素真实存在可能xpath/css写错
3检查元素是否在iframe内需要切换frame
4检查元素是否为动态id/动态class需要换定位策略
5检查是否被shadow DOM包裹需要穿透shadow root
6看是否处于新打开的标签页/窗口需要切换window handle

第6条容易被忽略:driver.get打开的页面在标签页A,点击后浏览器新开标签页B,你还在A里面找B的元素,当然找不到。解法:

handles = driver.window_handles driver.switch_to.window(handles[-1]) # 切到最新标签页

6.2 元素存在但get_attribute取不到值的几种情况

取文本或属性是断言功能是否正确的关键步骤。常见莫名其妙的情况是:element.text返回空字符串,但页面上明明有字。

原因通常是两组:一是元素本身用textContent渲染,而Selenium的.text属性只拿可见文本,需要改用get_attribute('textContent');二是数据是异步加载的,定位到的元素是空壳,文本还没填充进来,这时候加一个文本非空的等待条件:

WebDriverWait(driver, 10).until( lambda d: d.find_element(By.ID, "result").text.strip() != "" )

6.3 跑批脚本时偶发失败的经典场景与对策

自动化脚本最大的痛点不是写不出来,而是“这次跑通过了,下次跑就挂”。偶发失败往往来自三大元凶:网络抖动、元素加载时序、浏览器内存膨胀。

对策思路也明确:

  • 网络抖动:关键步骤加重试机制,失败重试1~2次。
  • 加载时序:统一用显式等待,不信任sleep。
  • 浏览器内存膨胀:跑一段时间后driver.quit()重启浏览器,长任务切分批次。

这些问题的共性在于它们都不是代码逻辑错误,而是环境对抗问题。我的经验是:自动化脚本里预留容错逻辑,比追求一次跑对更重要,毕竟真实环境的不可控因素太多了。

6.4 Hook性能优化与并发执行建议

脚本稳定性解决之后,迟早会碰到速度问题。单线程跑几百个用例,体验很差。Selenium本身是阻塞式的,同时操作同一个浏览器的同一个页面没有意义,要做到并行,一般用concurrent.futures或者pytest-xdist开多个进程,每个进程起一个独立的driver。

这里有个注意点:多进程并行时,浏览器数量等于进程数,对机器内存要求很高。我一般限制在4~8个并行进程,再多反而会因为资源竞争导致失败率上升。另外,每个driver用完必须quit(),否则浏览器后台僵死占用资源,这是我踩过最深的坑。

还有一个小技巧:如果只是爬取数据而不需要渲染视觉效果,可以开启headless无头模式,速度能提升不少。但要注意,无头模式下某些前端特性可能不生效(比如UA标识不同导致服务端返回不同页面),上线前一定要先验证一遍。

6.5 可视化调试:把Selenium变成“直播”

搜索词里有“selenium爬虫可视化”,这个点确实很实用。我调试定位问题时的常规做法是:写脚本时给关键元素加高亮边框,然后截图留档。这样定位准不准一眼就能看出来。

def highlight(driver, element): driver.execute_script( "arguments[0].style.border='3px solid red'", element ) # 使用 ele = driver.find_element(By.ID, "username") highlight(driver, ele) driver.save_screenshot("debug.png")

再加上driver.get_screenshot_as_png(),可以在断言失败时自动截图,方便回溯。对于复杂页面,我还会配合driver.page_source输出HTML快照,用于后续离线分析。这套组合拳在调试动态页面时简直救命。

7. 从入门到落地:一份可以抄作业的完整示例流程

说了这么多,最后给一套能直接跑的完整示例。场景:用Selenium打开一个带登录和列表页面的后台系统,输入账号密码,登录后定位列表数据,并把数据提取出来。

import time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 初始化 driver = webdriver.Chrome() driver.implicitly_wait(5) try: driver.get("https://your-target-site.com/login") # 登录表单 username = driver.find_element(By.ID, "username") username.clear() username.send_keys("admin") password = driver.find_element(By.ID, "password") password.clear() password.send_keys("123456") password.send_keys(Keys.ENTER) # 等待登录跳转后,列表加载完成 wait = WebDriverWait(driver, 15) wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".data-row"))) # 提取所有数据行文本 rows = driver.find_elements(By.CSS_SELECTOR, ".data-row") for i, row in enumerate(rows[:5]): print(f"第{i+1}行: {row.text}") # 分页点击示例:等下一页按钮可点击再点 next_btn = wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, "a.next-page")) ) next_btn.click() finally: driver.quit()

这套流程里用了隐式等待做兜底、显式等待做核心依赖,两者分得清清楚楚。实际项目页面结构可能更复杂,但只要定位思路和等待策略到位,换成你的真实选择器就能直接复用。

再补充一个我常用的封装思路:把所有定位器(locator)放在一个配置文件里,脚本用变量引用。页面改版时只需要改配置,不用动业务代码。这在维护大型自动化项目时能省下大量时间。

最后再分享一个写自动化脚本的心法:别追求一次写好,先跑通再优化。第一版写得丑一点没关系,能跑起来你就能看到真实页面、真实时序,然后针对性地改。我见过太多人对着代码空想半天,都不愿意先跑一次,结果浪费的时间比写代码多十倍。Selenium这个东西,纸上谈兵永远学不会,打开浏览器、跑起来、把错误一个个解决掉,才是最快的成长路径。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 10:55:46

【单片机课设毕设项目】基于 STM32 或 51 单片机的厨房险情自动处置与蓝牙传输系统设计 基于 STM32 或 51 单片机的可手动干预燃气火情智能防护系统实现(017607)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华
网站建设 2026/9/9 10:55:18

企业流程架构演进:从裸用Activiti到统一流程平台

1. 从裸用 Activiti 到建设流程平台&#xff1a;一次架构演进复盘先说背景。我过去几年带团队做过不少企业内部系统的流程模块&#xff0c;早期项目图省事&#xff0c;直接在业务代码里嵌 Activiti&#xff0c;部署一个流程就塞一个流程定义&#xff0c;所有审批逻辑都往引擎里…

作者头像 李华
网站建设 2026/9/9 10:54:30

单片机毕设项目:基于 STM32 的停车费用播报与车位状态监控系统设计 基于 STM32 的按键 IC 卡管理模拟停车场系统设计(016507)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华
网站建设 2026/9/9 10:52:26

金额存储选型:Long与BigDecimal的精度、性能与场景全解析

1. 从一次线上故障说起&#xff1a;金额到底该怎么存大概两年前&#xff0c;我接手过一个电商结算系统的重构任务&#xff0c;代码里有个订单金额字段用的是double。当时看到这个字段的第一反应就是头疼&#xff0c;因为我知道这个系统每隔几个月就会出一次对不上账的问题&…

作者头像 李华
网站建设 2026/9/9 10:51:16

2026年AI编程生产力工具实战指南:告别伪插件,构建真工作流闭环

1. 别被“Claude Code”四个字带偏了&#xff1a;先搞清你真正需要什么生产力最近刷技术社区、开发者群、甚至前端面试备考群&#xff0c;总能看到类似标题的帖子&#xff1a;“Claude Code 插件安装失败&#xff1f;”“Claude Code Ollama 配置踩坑实录”“求一份2026最新Cl…

作者头像 李华