当前位置: 首页 > news >正文

Selenium自动化测试从入门到精通:四阶段学习路线与实战指南

1. 从零到一:为什么你需要一份Selenium学习计划?

如果你是一名测试工程师、开发人员,或者对自动化感兴趣的技术爱好者,最近一定频繁听到“Selenium”和“Web自动化测试”这两个词。它们就像技术圈里的“网红”,热度居高不下。但很多朋友在接触时,往往陷入一个误区:打开搜索引擎,输入“Selenium教程”,然后被海量的、零散的、甚至版本过时的文章淹没,东学一点定位,西看一点等待,折腾半天,浏览器没跑起来,信心先被消耗了一半。这正是我写这份学习计划的初衷——它不是一个简单的命令列表,而是一份为你量身定制的、有节奏、有重点的“作战地图”。

我干了十多年测试和开发,从最早的QTP(UFT)到Selenium 2.0,再到现在的Selenium 4,亲眼看着这个生态从简陋走向强大,也亲手用它在无数个项目里搭建起可靠的自动化防线。我的体会是,学Selenium,或者说学任何一项能直接产生价值的技术,最忌讳的就是“漫无目的”。你需要清楚地知道:第一,它到底是什么,能解决你工作中的哪些具体痛点?第二,学习的路径是什么,先学什么后学什么效率最高?第三,有哪些坑是前辈们已经踩过的,你可以轻松绕开?

Selenium本质上是一个用于Web应用程序自动化测试的工具集合。但它绝不仅仅是“测试”专属。前端开发可以用它来做表单重复提交、页面交互回归验证;数据工程师可以用它来抓取动态渲染的网页数据(虽然这不是最佳实践,但在某些场景下很有效);甚至运维同学也能用它来编写简单的网站监控脚本。它的核心价值在于“模拟真人操作浏览器”,让程序代替你去点击、输入、跳转、验证。想象一下,每天上班第一件事,不是手动点开几十个页面检查功能是否正常,而是喝杯咖啡的功夫,脚本已经跑完并生成了详细的测试报告——这种效率的提升是实实在在的。

所以,这份学习计划适合谁?它适合所有希望将重复、繁琐的Web操作自动化的人,无论你是想踏入自动化测试领域的新手,还是想提升团队效率的技术负责人。接下来,我会把这套经过多年实践验证的学习体系拆解给你看,从环境搭建这个“拦路虎”开始,到核心操作的“肌肉记忆”,再到高级应用的“战术组合”,最后是持续集成的“工业化部署”。我们一步一步来。

2. 学习路线图设计:四阶段构建你的自动化能力金字塔

盲目学习等于浪费时间。一份好的学习计划,必须像建造金字塔一样,先打牢地基,再构筑主体,最后完成封顶。我将Selenium的学习划分为四个循序渐进的阶段,每个阶段都聚焦于解决一类核心问题,并产出可见的成果,让你始终保持学习动力和清晰的方向感。

2.1 第一阶段:环境搭建与“Hello World”(第1周)

这个阶段的目标只有一个:让你的第一个Selenium脚本成功运行起来,在浏览器上看到效果。这是信心建立的关键一步,但也是新手放弃率最高的“魔鬼第一步”。问题通常不出在Selenium本身,而出在它依赖的浏览器驱动上。

核心任务与实操要点:

  1. 语言与工具选型:Selenium支持多种语言(Java, Python, C#, JavaScript等)。对于初学者,我强烈推荐Python。原因很简单:语法简洁,库丰富,社区活跃,能让你快速聚焦于Selenium本身,而不是陷入复杂的语言特性中。IDE可以选择PyCharm社区版(免费且功能强大)或VS Code。
  2. 安装Selenium库:这是最简单的一步。打开命令行(CMD或Terminal),输入pip install selenium即可。建议使用清华或阿里云的镜像源来加速:pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple
  3. 下载与配置浏览器驱动——最大的坑!Selenium需要通过一个名为“WebDriver”的组件来与真实浏览器通信。你必须下载与你的浏览器版本完全匹配的驱动。
    • Chrome驱动(chromedriver):去淘宝镜像站(https://npm.taobao.org/mirrors/chromedriver/)或官方仓库下载。查看你的Chrome版本(浏览器地址栏输入chrome://version/),下载对应版本号的chromedriver。
    • 重点:驱动放置位置。有三种方法,推荐第一种给新手:
      • 方法一(推荐):将下载的chromedriver.exe(Windows) 或chromedriver(Mac/Linux) 文件,直接放在你Python脚本的同一个目录下。
      • 方法二:将其放在某个文件夹(如C:\WebDriver\),并将该文件夹路径添加到系统的环境变量PATH中。
      • 方法三:在代码中指定绝对路径。
  4. 编写并运行“Hello World”脚本:创建一个demo.py文件,输入以下代码:
from selenium import webdriver from selenium.webdriver.common.by import By import time # 1. 创建浏览器驱动对象,这将自动打开一个Chrome浏览器窗口 driver = webdriver.Chrome() # 如果驱动不在PATH或当前目录,需指定路径:webdriver.Chrome(executable_path=r‘你的路径\chromedriver.exe’) # 2. 访问百度首页 driver.get(“https://www.baidu.com“) print(“当前页面标题是:”, driver.title) # 3. 找到搜索框,输入“Selenium” search_box = driver.find_element(By.ID, “kw”) # 通过ID定位元素 search_box.send_keys(“Selenium“) # 输入文本 # 4. 找到“百度一下”按钮并点击 search_button = driver.find_element(By.ID, “su”) search_button.click() # 5. 等待2秒,看看搜索结果 time.sleep(2) # 6. 关闭浏览器 driver.quit()

运行这个脚本,你会看到一个Chrome浏览器自动打开,访问百度,输入关键词并搜索,然后关闭。恭喜你,万里长征第一步成功了!

注意事项:很多新手在这里卡住,报错‘chromedriver’ executable needs to be in PATH。请严格按照上述步骤3检查驱动版本和位置。另一个常见错误是浏览器自动更新后驱动版本不匹配,只需重新下载对应版本的驱动即可。

2.2 第二阶段:核心操作与元素定位(第2-3周)

当浏览器能听话地打开和关闭后,我们就要学习如何“指挥”它了。这个阶段的目标是熟练掌握对网页元素的定位、操作和断言,这是自动化脚本的“手和眼睛”。

核心任务拆解:

  1. 八种元素定位大法:这是Selenium的基石。By类提供了多种定位方式,你必须像熟悉自己手掌一样熟悉它们。

    • ID、NAME:最优先使用,通常唯一且稳定。find_element(By.ID, “idValue”)
    • CLASS_NAME、TAG_NAME:常用于定位一组元素。注意CLASS可能有空格(复合类名),需用.替换空格或使用CSS选择器。
    • LINK_TEXT、PARTIAL_LINK_TEXT:专门用于定位超链接文本。
    • CSS_SELECTOR:功能强大,语法灵活,是定位复杂元素的首选。例如find_element(By.CSS_SELECTOR, “#loginForm .username”)
    • XPATH:终极武器,几乎可以定位任何元素,但表达式可能复杂且易受页面结构变化影响。慎用绝对路径(以/开头),多用相对路径和属性结合。

    我个人的经验是:优先使用ID和NAME,复杂场景用CSS_SELECTOR,万不得已再用XPATH。在浏览器开发者工具(F12)中,可以直接在元素上右键“Copy” -> “Copy selector”或“Copy XPath”,但这只是参考,生产脚本中需要优化。

  2. 常用元素操作

    • 点击click()
    • 输入/清空send_keys(“text”)/clear()
    • 获取文本/属性text属性 /get_attribute(“attributeName”)
    • 表单提交submit()(适用于表单内的元素)
  3. 浏览器操作

    • 导航:back(),forward(),refresh()
    • 窗口:maximize_window(),set_window_size(width, height),switch_to.window(handle)(多窗口切换)
    • 弹框处理:switch_to.alert接受、驳回或输入文本。

这个阶段,最好的练习就是找一个你熟悉的网站(如一个电商网站的登录、搜索商品、加入购物车流程),尝试用脚本完整地模拟操作一遍。你会遇到各种定位难题,这正是进步的过程。

2.3 第三阶段:等待、框架与高级技巧(第4-5周)

脚本能跑起来不代表稳定。这个阶段我们要解决自动化脚本中最令人头疼的问题:稳定性可维护性。核心是理解“等待”机制,并开始引入初步的框架思想。

核心内容解析:

  1. 三种等待机制——稳定性的灵魂

    • 强制等待time.sleep(seconds)。简单粗暴,但效率低下,不推荐在正式脚本中使用,仅用于临时调试。
    • 隐式等待driver.implicitly_wait(seconds)。为整个driver会话设置一个全局的等待时间,在查找任何元素时,如果元素没有立即出现,会轮询等待设定的时间。它是一把“双刃剑”,设置过长会拖慢脚本整体速度,且无法处理某些特定条件(如元素可点击)。
    • 显式等待这是你必须掌握的核心技能!它允许你为某个特定条件设置等待,条件满足则继续,超时则报错。它更智能、更高效。
    from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待一个ID为‘myElement’的元素出现,最多等10秒 element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, “myElement”)) ) # 等待元素可被点击 button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, “.submit-btn”)) ) button.click()

    显式等待的常见条件(EC)presence_of_element_located(元素存在),visibility_of_element_located(元素可见),element_to_be_clickable(元素可点击),title_contains(标题包含)等。我的黄金法则是:对于任何后续操作依赖的元素(尤其是点击、输入),都使用显式等待来确保其状态就绪。

  2. 引入单元测试框架:是时候告别在单个.py文件里写一堆脚本了。引入unittestpytest(更强大,推荐)。它们能帮你组织测试用例、生成测试报告、进行前置后置操作(setup/teardown)。

    import unittest from selenium import webdriver class TestBaiduSearch(unittest.TestCase): def setUp(self): self.driver = webdriver.Chrome() self.driver.implicitly_wait(5) # 可设置一个较短的全局隐式等待作为兜底 def test_search_selenium(self): driver = self.driver driver.get(“https://www.baidu.com“) driver.find_element(By.ID, “kw”).send_keys(“Selenium“) driver.find_element(By.ID, “su”).click() # 使用显式等待确认搜索结果出现 WebDriverWait(driver, 10).until( EC.title_contains(“Selenium“) ) self.assertIn(“Selenium“, driver.title) def tearDown(self): self.driver.quit() if __name__ == ‘__main__’: unittest.main()
  3. 高级技巧初探

    • 执行JavaScriptdriver.execute_script(“js code”),用于处理Selenium原生API难以实现的操作,如滚动页面、修改元素属性等。
    • 文件上传:对于<input type=“file”>元素,直接使用send_keys(“文件绝对路径”),而不是尝试去点击“浏览”按钮。
    • Cookie处理get_cookies(),add_cookie(),可用于登录状态的保持。

2.4 第四阶段:项目实战与持续集成(第6周及以后)

理论知识学得再多,不如一个实战项目来得深刻。这个阶段,你需要找一个真实的业务场景,搭建一个结构清晰、易于维护的小型自动化项目,并尝试将其集成到开发流程中。

项目实战设计思路:

  1. 选择目标系统:最好是你工作中正在开发或维护的系统,或者找一个有登录、表单、数据列表等典型功能的公开网站进行练习。

  2. 设计项目结构:一个良好的结构是后期维护的保障。推荐如下目录结构:

    your_project/ ├── config/ # 配置文件(如数据库、URL、账号密码) │ └── config.py ├── page_objects/ # **页面对象模型(Page Object Model, POM)核心!** │ ├── login_page.py │ ├── home_page.py │ └── ... ├── test_cases/ # 测试用例 │ ├── test_login.py │ └── ... ├── test_data/ # 测试数据(JSON, Excel) ├── reports/ # 测试报告输出目录 ├── logs/ # 运行日志 ├── utils/ # 工具类(如读取文件、发送邮件、数据库连接) │ └── common.py ├── conftest.py # pytest的共享配置(夹具等) └── requirements.txt # 项目依赖
  3. 实现POM设计模式:这是提升脚本可维护性的最关键实践。将每个页面封装成一个类,页面的元素定位和基本操作作为这个类的方法。测试用例中只包含业务逻辑,不直接包含元素定位。这样,当页面UI发生变化时,你只需要修改对应的Page类,而不需要修改大量的测试用例。

    # page_objects/login_page.py from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class LoginPage: def __init__(self, driver): self.driver = driver self.wait = WebDriverWait(driver, 10) # 定位器 USERNAME_INPUT = (By.ID, “username”) PASSWORD_INPUT = (By.ID, “password”) LOGIN_BUTTON = (By.CSS_SELECTOR, “button[type=‘submit’]”) ERROR_MSG = (By.CLASS_NAME, “alert-error”) # 页面操作方法 def enter_username(self, username): elem = self.wait.until(EC.visibility_of_element_located(self.USERNAME_INPUT)) elem.clear() elem.send_keys(username) def enter_password(self, password): elem = self.wait.until(EC.visibility_of_element_located(self.PASSWORD_INPUT)) elem.clear() elem.send_keys(password) def click_login(self): elem = self.wait.until(EC.element_to_be_clickable(self.LOGIN_BUTTON)) elem.click() def get_error_message(self): try: elem = self.wait.until(EC.visibility_of_element_located(self.ERROR_MSG)) return elem.text except: return None # 业务组合方法 def login(self, username, password): self.enter_username(username) self.enter_password(password) self.click_login()
    # test_cases/test_login.py import pytest from page_objects.login_page import LoginPage class TestLogin: def test_login_success(self, driver): # driver通过pytest fixture注入 login_page = LoginPage(driver) driver.get(“https://example.com/login“) login_page.login(“valid_user”, “valid_pass”) # 断言登录成功,例如跳转到首页 assert “Dashboard” in driver.title def test_login_failure(self, driver): login_page = LoginPage(driver) driver.get(“https://example.com/login“) login_page.login(“invalid_user”, “invalid_pass”) error_msg = login_page.get_error_message() assert error_msg is not None assert “用户名或密码错误” in error_msg
  4. 生成测试报告:使用pytest-htmlAllure等插件生成美观详细的HTML报告,直观展示测试结果。

  5. 集成到CI/CD(持续集成/持续部署):这是自动化的最终归宿。将你的测试项目放到Git仓库,然后在Jenkins、GitLab CI、GitHub Actions等工具中配置任务,实现代码提交后自动触发测试、生成报告并通知结果。这一步让自动化从“可运行”变成了“有价值的生产力工具”。

3. 避坑指南与高频问题实录

学技术,踩坑是必经之路。但有些坑,你可以不用亲自踩。下面是我和同事们多年积累下来的“血泪经验”,希望能帮你节省大量调试时间。

3.1 环境与驱动问题

  • 问题WebDriverException: Message: ‘chromedriver’ executable needs to be in PATH

  • 排查:99%的原因是驱动未正确配置。检查:1) 下载的chromedriver版本号是否与Chrome浏览器版本完全匹配(主版本号一致即可)。2) chromedriver可执行文件是否放在了系统PATH包含的目录,或代码中指定的路径。

  • 解决:使用代码指定路径是最稳妥的方式:webdriver.Chrome(executable_path=‘/absolute/path/to/chromedriver’)。或者,将驱动所在目录加入系统环境变量PATH。

  • 问题:浏览器闪退,或脚本执行完浏览器不关闭。

  • 排查:代码逻辑问题或驱动冲突。

  • 解决:确保在脚本最后调用了driver.quit()而不是driver.close()quit()会关闭所有窗口并终止驱动进程,而close()只关闭当前标签页。在try…except…finally结构中,将quit()放在finally块中是良好实践。

3.2 元素定位与交互问题

  • 问题NoSuchElementExceptionElementNotInteractableException

  • 排查:这是最常见的问题。原因可能包括:1) 定位表达式写错了。2) 元素在iframe/frame内。3) 元素尚未加载出来(需要加等待)。4) 元素被遮挡(如弹窗、浮动广告)。5) 元素在屏幕外,需要滚动。

  • 解决

    1. 优先使用显式等待,而不是sleep或仅靠隐式等待。
    2. 检查页面是否有iframe,如果有,需要使用driver.switch_to.frame(frame_reference)切换到对应iframe后才能定位其中的元素,操作完记得switch_to.default_content()切回来。
    3. 使用driver.execute_script(“arguments[0].scrollIntoView(true);”, element)将元素滚动到视图中。
    4. 对于复杂或动态生成的元素,在开发者工具中使用$x(‘your_xpath’)$$(‘your_css’)进行实时验证。
  • 问题StaleElementReferenceException(元素过时引用)

  • 排查:你之前找到的元素,由于页面刷新、AJAX更新或DOM重新渲染,已经“失效”了。

  • 解决不要缓存可能变化的元素。对于这类动态元素,最好是每次使用时重新查找。或者,在POM中,将定位器(Locator)和元素查找分开,每次操作前通过定位器重新获取元素。

3.3 等待与同步问题

  • 问题:脚本有时成功有时失败,像“玄学”。

  • 排查:几乎可以断定是等待策略不当。隐式等待和显式等待混用不当,或等待时间设置不合理。

  • 解决

    • 明确分工:设置一个较短的全局隐式等待(如5秒)作为“安全网”,用于那些不重要的元素查找。对于所有关键操作(点击、输入、获取状态),一律使用显式等待,并选择最合适的条件(如element_to_be_clickable)。
    • 避免“死等”:不要滥用time.sleep(固定时长)。这不仅慢,而且不可靠。页面加载快的时候它在空等,加载慢的时候它又不够等。
    • 自定义等待条件:当内置条件不满足时,可以自定义等待函数,例如等待某个元素的特定文本出现。
    def wait_for_text_in_element(driver, locator, text, timeout=10): “”“自定义等待:直到元素包含特定文本”“” def _predicate(drv): try: element_text = drv.find_element(*locator).text return text in element_text except StaleElementReferenceException: return False WebDriverWait(driver, timeout).until(_predicate, f“元素 {locator} 的文本未在 {timeout} 秒内包含 ‘{text}’”)

3.4 浏览器与配置问题

  • 问题:如何让脚本在后台无界面运行?

  • 解决:使用无头(Headless)模式。这在CI/CD环境中非常有用,可以节省资源。

    from selenium.webdriver.chrome.options import Options chrome_options = Options() chrome_options.add_argument(“--headless”) # 启用无头模式 chrome_options.add_argument(“--disable-gpu”) # 某些系统需要 chrome_options.add_argument(“--no-sandbox”) # Linux环境下可能需要 chrome_options.add_argument(“--window-size=1920,1080”) # 设置窗口大小 driver = webdriver.Chrome(options=chrome_options)
  • 问题:如何绕过网站检测Selenium?

  • 排查:一些高级反爬或安全措施会检测浏览器是否由自动化工具控制。

  • 解决(此方法仅用于合法测试目的):

    • 使用chrome_options.add_experimental_option(“excludeSwitches”, [“enable-automation”])chrome_options.add_argument(“--disable-blink-features=AutomationControlled”)来隐藏“Chrome正受到自动测试软件控制”的提示。
    • 但这并非万能,更高级的检测需要更复杂的手段。对于测试而言,通常与开发团队沟通,在测试环境关闭相关检测是更可行的方案。

4. 进阶思考:Selenium与Playwright等新工具的对比

在学习Selenium的过程中,你很可能也会听到“Playwright”、“Cypress”这些名字。它们都是优秀的浏览器自动化工具。了解它们的差异,能帮助你更好地定位Selenium的用武之地,并在未来技术选型时做出明智决策。

Selenium的优势与定位:

  • 历史悠久,生态成熟:社区庞大,资料丰富,几乎所有你能想到的问题都能找到答案。
  • 多语言支持:对团队技术栈兼容性好。
  • 真正的跨浏览器:支持Chrome、Firefox、Safari、Edge等主流浏览器,标准统一。
  • W3C标准:Selenium WebDriver现在是W3C推荐标准,具有很好的规范性和长期支持预期。

Selenium的劣势:

  • 环境配置相对繁琐:需要单独下载和匹配浏览器驱动。
  • API有时不够直观:例如等待机制需要额外学习。
  • 执行速度:相较于一些新工具,在复杂场景下可能稍慢。

Playwright(微软出品)的特点:

  • 开箱即用:自动下载浏览器驱动,无需手动管理。
  • 强大的自动化能力:原生支持网络拦截、移动端模拟、文件下载等,对现代Web应用(单页应用SPA)支持更好。
  • 自动等待:内置智能等待,大多数情况下无需手动编写等待代码。
  • 多浏览器支持:同样支持Chromium、Firefox、WebKit。

如何选择?

  • 如果你是初学者,或团队已有成熟的Selenium资产和技能栈坚定不移地学习Selenium。它是行业的基石,理解了Selenium,再学其他工具会易如反掌。它的就业市场需求也依然是最旺盛的。
  • 如果你启动一个全新的项目,且追求更现代的API和更少的配置麻烦:可以认真评估Playwright。它在开发体验和某些高级功能上确实有优势。
  • 如果你的测试以Chromium系浏览器为主,且需要极致的开发体验和快速执行:也可以看看Cypress(但其架构和浏览器支持与Selenium/Playwright有根本不同)。

我的建议是:先精通Selenium。它教给你的不仅仅是工具的使用,更是对Web自动化底层原理(驱动通信、等待、DOM操作)的深刻理解。这份理解是通用的,是你未来快速掌握任何新自动化框架的资本。当你用Selenium能稳健地完成各种复杂场景的自动化后,再去了解Playwright,你会发现你只是在学习一套更便捷的“语法糖”,核心思想早已掌握。

学习计划的价值在于提供路径和减少迷茫,但真正的成长源于动手实践。不要试图一次性学完所有东西再开始写代码。最好的方式是“边学边做,以战养兵”。从今天起,就打开你的IDE,从让浏览器弹出第一个“Hello World”开始,每周按计划推进,用项目去串联知识点。遇到问题,善用搜索引擎和官方文档(Selenium官方文档:https://www.selenium.dev/documentation/)。坚持下去,两个月后,你回头看,会发现你已经拥有了将重复性Web操作彻底自动化的能力,这将成为你职业生涯中一项极具价值的生产力工具。

http://www.gsyq.cn/news/1569671.html

相关文章:

  • 基于MC68HC908EY16的红外遥控LIN机器人:输入捕获与总线通信实战
  • 2026上海防水补漏上门施工哪家强?正规商家资质+报价+口碑+售后四维实测对比 - 防水资讯
  • FanControl智能散热配置:打造个性化风扇控制方案
  • 什么是全景运维地图?全景运维地图包括哪些关键技术?
  • 基于BFU768F的5-6GHz低噪声放大器设计:实现1.4dB噪声系数与快速开关
  • Java Web自动化测试入门:Selenium环境搭建与Page Object模式实战
  • 从MPC5674F到MPC5676R:嵌入式系统单核到双核迁移实战指南
  • 程序员量化交易实战 06:先把数据库表结构讲清楚
  • uClinux在ColdFire无MMU平台的移植与调试实战指南
  • 8大主流网盘直链下载助手:免费解锁高速下载的终极解决方案
  • 从EA LPC1788到Keil MCB1700的emWin BSP移植实战指南
  • 英雄联盟玩家的3个秘密武器:如何用本地自动化工具提升游戏体验
  • QQ音乐解析终极指南:轻松获取海量音乐资源的完整解决方案
  • 半导体量检测工艺及设备
  • 3D合成与不变技能:实现机器人视点泛化的核心技术
  • Expect SSH自动化脚本编写原理与生产实践指南
  • 俄艾斯国际俄罗斯EAC认证,提升国货欧亚市场核心竞争力 - 品牌速递
  • 2026长沙防水补漏上门施工哪家强?正规商家资质+报价+口碑+售后四维实测对比 - 防水资讯
  • 2026高速贴标机故障率口碑综合测评:飞彬贴标机适配各行业深度分析 - 万事通达
  • i.MX53xA UART与USB接口硬件设计:电气特性解析与工程实践
  • B站会员购抢票自动化:如何用biliTickerBuy告别手动抢票的烦恼?
  • Memory is Reconstructed, Not Retrieved: Graph Memory for LLM Agents
  • 5种方法快速掌握跨平台资源下载工具:从技术原理到实战应用
  • Linux /dev/null 原理与实战:标准流重定向与静默化工程
  • 武汉市洪山区水电维修|维小达|电路|水管|马桶|暖气|管道疏通一站式全屋水电维保服务 - 维小达科技
  • 开源漏洞扫描工具实战:从工具使用到漏洞原理的逆向学习指南
  • 2026沈阳防水补漏上门施工哪家强?正规商家资质+报价+口碑+售后四维实测对比 - 防水资讯
  • CF2144E1 思路分享(dp)
  • 3分钟掌握Adobe-GenP:终极Adobe软件激活完整指南
  • 一篇讲清亲情账号、家庭共济与医保钱包