Selenium 完全指南

最后更新:2026-03-20 1. Selenium完全指南 · 安装与环境配置(/selenium-wan-quan-zhi-nan/#%E5%AE%89%E8%A3%85%E4%B8%8E%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE) 2. Selenium完全指南 · 启动浏览器(/selenium-wan-quan-zhi-nan/#%E5%90%AF%E5%8A%A8%E6%B5%8F%E8%A7%88%E5%99%A8) 3. Selenium完全指南 · 导航(/selenium-wan-quan-zhi-na

分享

最后更新:2026-03-20

官方文档:https://www.selenium.dev/documentation/
适用版本:Selenium 4.x(2026-05-08 核实)


目录

  1. Selenium完全指南 · 安装与环境配置
  2. Selenium完全指南 · 启动浏览器
  3. Selenium完全指南 · 导航
  4. Selenium完全指南 · 等待机制
  5. Selenium完全指南 · 元素定位
  6. Selenium完全指南 · 读取元素信息
  7. Selenium完全指南 · 鼠标与键盘操作
  8. Selenium完全指南 · 表单操作
  9. Selenium完全指南 · 窗口与标签页
  10. Selenium完全指南 · 框架与 iframe
  11. Selenium完全指南 · 弹窗处理
  12. Selenium完全指南 · JavaScript 执行
  13. Selenium完全指南 · 截图
  14. Selenium完全指南 · 最佳实践
  15. Selenium完全指南 · 常见陷阱与注意事项

安装与环境配置

安装

pip install selenium

Selenium 4.6+ 内置 Selenium Manager,会自动下载匹配当前浏览器版本的 WebDriver,通常不需要手动管理驱动。

如果需要手动管理驱动,也可以使用 webdriver-manager

pip install webdriver-manager

支持的浏览器

浏览器 驱动 自动管理(Selenium Manager)
Chrome / Chromium chromedriver 支持
Firefox geckodriver 支持
Edge msedgedriver 支持
Safari safaridriver(内置) macOS 自带,需开启开发者模式

版本要求

最低版本 说明
selenium 4.6+ 内置 Selenium Manager,推荐
Python 3.8+

启动浏览器

Chrome

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service

# 最简启动(Selenium Manager 自动管理驱动)
driver = webdriver.Chrome()

# 带选项启动
options = Options()
options.add_argument("--headless=new")       # 无头模式
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--window-size=1920,1080")
options.add_argument("--user-agent=...")     # 自定义 UA
options.add_experimental_option("excludeSwitches", ["enable-automation"])  # 去除自动化标记

driver = webdriver.Chrome(options=options)

# 手动指定驱动路径
service = Service(executable_path="/path/to/chromedriver")
driver = webdriver.Chrome(service=service, options=options)

Chrome Options 常用参数:

参数 说明
--headless=new 无头模式(Chrome 112+,新版)
--no-sandbox 禁用沙箱,Docker 环境必须加
--disable-dev-shm-usage 避免 /dev/shm 空间不足崩溃
--window-size=W,H 设置窗口大小
--disable-gpu 禁用 GPU,旧版无头模式需要
--disable-images 禁止加载图片,提升速度
--proxy-server=host:port 设置代理
--ignore-certificate-errors 忽略 SSL 证书错误
--disable-blink-features=AutomationControlled 隐藏自动化特征

Firefox

from selenium import webdriver
from selenium.webdriver.firefox.options import Options

options = Options()
options.add_argument("--headless")

driver = webdriver.Firefox(options=options)

Edge

from selenium import webdriver

driver = webdriver.Edge()

webdriver.Chrome() 参数表

参数 类型 默认值 说明
options Options None 浏览器选项对象
service Service None 驱动服务配置(路径、端口等)
keep_alive bool True 保持 HTTP 连接

关闭浏览器

driver.close()   # 关闭当前标签页
driver.quit()    # 关闭整个浏览器进程,释放驱动资源

推荐使用上下文管理器,自动调用 quit()

from selenium import webdriver

with webdriver.Chrome() as driver:
    driver.get("https://example.com")
    # 退出 with 块时自动 quit()

导航

driver.get(url)               # 打开 URL,等待页面加载完成
driver.back()                 # 后退
driver.forward()              # 前进
driver.refresh()              # 刷新

driver.get() 参数表:

参数 类型 默认值 说明
url str 必填 目标 URL,必须包含协议(https://)

获取当前页面信息:

driver.current_url    # 当前 URL(str)
driver.title          # 页面标题(str)
driver.page_source    # 页面 HTML 源码(str)

等待机制

Selenium 操作的最大痛点是时序问题。页面元素可能尚未加载完毕,直接操作会抛出异常。

隐式等待(ImplicitWait)

全局设置,对所有 find_element 生效,找不到元素时轮询等待,直到超时。

driver.implicitly_wait(10)  # 秒,全局生效
参数 类型 默认值 说明
time_to_wait float 0 最长等待秒数,0 表示不等待

缺点:与显式等待混用会产生不可预期的超时叠加,建议只用一种。

显式等待(WebDriverWait)

推荐方式。针对具体条件等待,超时抛出 TimeoutException

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, timeout=10)

# 等待元素可见
el = wait.until(EC.visibility_of_element_located((By.ID, "submit")))

# 等待元素可点击
el = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.submit")))

WebDriverWait() 参数表:

参数 类型 默认值 说明
driver WebDriver 必填 浏览器驱动实例
timeout float 必填 最长等待秒数
poll_frequency float 0.5 轮询间隔秒数
ignored_exceptions list None 等待期间忽略的异常类型列表

wait.until() / wait.until_not() 参数表:

参数 类型 说明
method callable 接受 driver 为参数的可调用对象,返回值为 False/None 时继续等待
message str 超时时的错误信息

常用 Expected Conditions:

条件 说明
presence_of_element_located(locator) 元素存在于 DOM(不要求可见)
visibility_of_element_located(locator) 元素可见(宽高>0,不被隐藏)
element_to_be_clickable(locator) 元素可见且可交互
invisibility_of_element_located(locator) 元素不可见或不在 DOM 中
text_to_be_present_in_element(locator, text) 元素文本包含指定字符串
title_contains(title) 页面标题包含指定字符串
title_is(title) 页面标题完全匹配
url_contains(url) URL 包含指定字符串
alert_is_present() 弹窗已出现
number_of_windows_to_be(num) 窗口数量达到指定值
frame_to_be_available_and_switch_to_it(locator) iframe 可用并自动切入
staleness_of(element) 元素已从 DOM 中移除(用于等待页面刷新)

强制等待(不推荐)

import time
time.sleep(2)  # 固定等待,不推荐用于生产代码

元素定位

find_element() / find_elements()

from selenium.webdriver.common.by import By

# 返回单个 WebElement,找不到抛 NoSuchElementException
element = driver.find_element(By.ID, "username")

# 返回 list[WebElement],找不到返回空列表
elements = driver.find_elements(By.CLASS_NAME, "item")

find_element() 参数表:

参数 类型 默认值 说明
by By 必填 定位策略,使用 By 类的常量
value str 必填 选择器值

By 定位策略:

策略 常量 示例值 说明
ID By.ID "username" 匹配 id 属性,最快
Name By.NAME "email" 匹配 name 属性
Class Name By.CLASS_NAME "btn-primary" 单个 class 名,不支持复合类
Tag Name By.TAG_NAME "input" 匹配标签名
CSS Selector By.CSS_SELECTOR "div.card > a" 标准 CSS 选择器,最灵活
XPath By.XPATH "//div[@class='nav']" 支持复杂逻辑,但较慢
Link Text By.LINK_TEXT "点击登录" 匹配 <a> 的完整文本
Partial Link Text By.PARTIAL_LINK_TEXT "登录" 匹配 <a> 的部分文本

CSS 选择器常用语法:

#id              /* ID */
.class           /* class */
div              /* 标签 */
div.card         /* 标签 + class */
input[type="text"]   /* 属性 */
div > p          /* 直接子元素 */
div p            /* 后代元素 */
p:nth-child(2)   /* 第2个子元素 */
p:first-child    /* 第1个子元素 */

XPath 常用语法:

//div[@id='main']              # 属性定位
//a[text()='登录']              # 文本定位
//a[contains(text(),'登录')]    # 文本包含
//input[@type='text']          # 属性过滤
//div[@class='nav']//a         # 后代
//ul/li[1]                     # 第1个 li
//li[last()]                   # 最后一个
//*[contains(@class,'btn')]    # class 包含(class 是空格分隔的,@class= 是全量匹配)
//input[@name='q']/..          # 父节点

在元素内部继续查找

WebElement 也有 find_element / find_elements 方法,作用域限定在该元素内:

container = driver.find_element(By.ID, "results")
items = container.find_elements(By.CLASS_NAME, "item")

读取元素信息

element = driver.find_element(By.ID, "title")

element.text                           # 可见文本内容(str),等价于 innerText
element.get_attribute("href")          # 获取 HTML 属性值
element.get_attribute("innerHTML")     # 内部 HTML
element.get_attribute("outerHTML")     # 包含自身的 HTML
element.get_attribute("value")         # 表单元素的当前值(用属性而非 text)
element.get_property("value")          # 获取 DOM property(JS 属性,实时值)
element.get_dom_attribute("class")     # 只读取 HTML 属性(不读 JS property)

element.is_displayed()                 # 是否可见(bool)
element.is_enabled()                   # 是否可交互(bool)
element.is_selected()                  # 是否被选中,适用于 checkbox/radio(bool)

element.tag_name                       # 标签名,如 "input"(str)
element.size                           # 元素大小,如 {"width": 100, "height": 50}
element.location                       # 相对页面左上角的坐标,如 {"x": 10, "y": 20}
element.rect                           # 合并 size 和 location,如 {"x":10,"y":20,"width":100,"height":50}

get_attribute() vs get_property() 区别:

方法 读取来源 动态更新 典型用途
get_attribute(name) HTML 属性 + DOM property 部分 hrefsrcclassvalue(初始值)
get_property(name) DOM property(JS) value(当前用户输入值)、checked
get_dom_attribute(name) 仅 HTML 属性 读原始 HTML 中的属性值

鼠标与键盘操作

基础点击

element.click()          # 左键点击

ActionChains(链式操作)

适用于复杂交互:悬停、双击、右键、拖拽、组合键等。

from selenium.webdriver.common.action_chains import ActionChains

actions = ActionChains(driver)

ActionChains 参数表:

参数 类型 默认值 说明
driver WebDriver 必填 浏览器驱动实例
duration int 250 动作持续时间(毫秒),用于模拟人类速度

常用鼠标动作:

方法 参数 说明
click(on_element=None) WebElement | None 左键点击,None 则点当前位置
double_click(on_element=None) WebElement | None 双击
context_click(on_element=None) WebElement | None 右键点击
move_to_element(to_element) WebElement 移动鼠标到元素中心(悬停)
move_to_element_with_offset(to_element, xoffset, yoffset) WebElement, int, int 移动到元素内偏移位置
move_by_offset(xoffset, yoffset) int, int 从当前位置偏移移动
click_and_hold(on_element=None) WebElement | None 按住鼠标左键
release(on_element=None) WebElement | None 释放鼠标左键
drag_and_drop(source, target) WebElement, WebElement 从 source 拖拽到 target
drag_and_drop_by_offset(source, xoffset, yoffset) WebElement, int, int 拖拽元素偏移指定距离
scroll_to_element(element) WebElement 滚动到元素(Selenium 4.2+)
scroll_by_amount(delta_x, delta_y) int, int 按像素滚动页面
perform() 执行所有已排队的动作
reset_actions() 清空已排队的动作

使用示例:

from selenium.webdriver.common.action_chains import ActionChains

actions = ActionChains(driver)

# 悬停
actions.move_to_element(menu).perform()

# 双击
actions.double_click(element).perform()

# 右键
actions.context_click(element).perform()

# 拖拽到另一个元素
actions.drag_and_drop(source, target).perform()

# 拖拽偏移
actions.drag_and_drop_by_offset(element, xoffset=200, yoffset=0).perform()

# 链式组合
actions.move_to_element(menu).pause(0.5).click(submenu).perform()

# 滚动页面
actions.scroll_by_amount(0, 500).perform()  # 向下滚动 500px

# 滚动到元素
actions.scroll_to_element(element).perform()

键盘操作

from selenium.webdriver.common.keys import Keys

element.send_keys("hello world")        # 输入文本
element.send_keys(Keys.ENTER)           # 按回车
element.send_keys(Keys.TAB)             # 按 Tab
element.send_keys(Keys.CONTROL, "a")    # Ctrl+A 全选
element.send_keys(Keys.CONTROL, "c")    # Ctrl+C 复制
element.send_keys(Keys.CONTROL, "v")    # Ctrl+V 粘贴
element.send_keys(Keys.BACKSPACE)       # 删除
element.send_keys(Keys.ESCAPE)          # Esc
element.send_keys(Keys.PAGE_DOWN)       # 向下翻页
element.send_keys(Keys.ARROW_DOWN)      # 向下箭头

常用 Keys 常量:

常量 说明
Keys.ENTER 回车
Keys.RETURN 回车(同上)
Keys.TAB Tab
Keys.ESCAPE Esc
Keys.SPACE 空格
Keys.BACKSPACE 退格
Keys.DELETE Delete
Keys.CONTROL Ctrl
Keys.ALT Alt
Keys.SHIFT Shift
Keys.COMMAND Command(Mac)
Keys.F5 F5(刷新)
Keys.PAGE_UP Page Up
Keys.PAGE_DOWN Page Down
Keys.HOME Home
Keys.END End
Keys.ARROW_UP/DOWN/LEFT/RIGHT 方向键

表单操作

# 输入框
input_el = driver.find_element(By.NAME, "username")
input_el.clear()               # 清空内容
input_el.send_keys("admin")    # 输入文本

# 提交表单(在表单内任意元素上调用)
input_el.submit()

# checkbox / radio
checkbox = driver.find_element(By.ID, "agree")
if not checkbox.is_selected():
    checkbox.click()

# 下拉框 <select>
from selenium.webdriver.support.ui import Select

select_el = driver.find_element(By.ID, "country")
sel = Select(select_el)

sel.select_by_value("CN")               # 按 value 属性选
sel.select_by_visible_text("中国")      # 按显示文本选
sel.select_by_index(2)                  # 按索引选(从0开始)
sel.deselect_all()                      # 取消所有选中(多选框)
sel.first_selected_option              # 当前选中的第一个 WebElement
sel.all_selected_options               # 所有选中项的列表
sel.options                            # 所有选项的列表

窗口与标签页

# 当前窗口句柄
current = driver.current_window_handle   # str

# 所有窗口句柄
all_handles = driver.window_handles      # list[str]

# 切换到指定窗口
driver.switch_to.window(all_handles[-1])

# 打开新标签页(Selenium 4+)
driver.switch_to.new_window("tab")    # 打开新标签页并切换
driver.switch_to.new_window("window") # 打开新窗口并切换

# 等待新窗口出现并切换
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 10)
wait.until(EC.number_of_windows_to_be(2))
driver.switch_to.window(driver.window_handles[-1])

# 关闭当前标签页后切回原始窗口
driver.close()
driver.switch_to.window(original_handle)

# 窗口大小与位置
driver.maximize_window()
driver.minimize_window()
driver.set_window_size(1920, 1080)
driver.set_window_position(0, 0)
driver.get_window_size()     # {"width": 1920, "height": 1080}
driver.get_window_position() # {"x": 0, "y": 0}

框架与 iframe

页面内嵌 iframe 时,必须先切换到 iframe 才能操作其中的元素。

# 按 WebElement 切换
iframe = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframe)

# 按 id 或 name 切换
driver.switch_to.frame("frame_id")

# 按索引切换(页面中第n个iframe,从0开始)
driver.switch_to.frame(0)

# 退出 iframe,回到主文档
driver.switch_to.default_content()

# 退出一层嵌套 iframe(回到父框架)
driver.switch_to.parent_frame()

弹窗处理

原生 JavaScript 弹窗(alertconfirmprompt)需要通过 switch_to.alert 处理。

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 等待弹窗出现
wait = WebDriverWait(driver, 10)
alert = wait.until(EC.alert_is_present())

alert.text          # 弹窗文本内容(str)
alert.accept()      # 点击确定(alert/confirm/prompt 均适用)
alert.dismiss()     # 点击取消(confirm/prompt)
alert.send_keys("input text")  # 向 prompt 输入内容,再 accept()

JavaScript 执行

# 同步执行,返回值即 JS return 的值
result = driver.execute_script("return document.title;")

# 传入 Python 对象作为参数(arguments[0] 对应第一个参数)
driver.execute_script("arguments[0].click();", element)
driver.execute_script("arguments[0].style.border='3px solid red';", element)

# 滚动到元素
driver.execute_script("arguments[0].scrollIntoView(true);", element)

# 滚动到底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

# 修改 input 值(绕过只读限制)
driver.execute_script("arguments[0].value='new value';", input_el)

# 异步执行(适用于 AJAX 回调)
driver.execute_async_script("""
    var callback = arguments[arguments.length - 1];
    setTimeout(function() { callback('done'); }, 1000);
""")

execute_script() 参数表:

参数 类型 默认值 说明
script str 必填 JavaScript 代码字符串
*args any 传给 JS 的参数,在 JS 中通过 arguments[n] 访问

截图

# 截整个页面(视口范围)
driver.save_screenshot("page.png")           # 保存到文件
png_bytes = driver.get_screenshot_as_png()   # 返回 bytes
base64_str = driver.get_screenshot_as_base64()  # 返回 base64 字符串

# 截单个元素
element.screenshot("element.png")           # 保存到文件
png_bytes = element.screenshot_as_png       # 返回 bytes
base64_str = element.screenshot_as_base64   # 返回 base64 字符串

最佳实践

始终使用显式等待,避免 time.sleep

# 不推荐
time.sleep(3)

# 推荐
wait = WebDriverWait(driver, 10)
el = wait.until(EC.element_to_be_clickable((By.ID, "submit")))
el.click()

用上下文管理器保证浏览器关闭

with webdriver.Chrome(options=options) as driver:
    driver.get("https://example.com")
    # 异常也会触发 quit()

优先使用 CSS Selector,XPath 作为备选

CSS Selector 执行比 XPath 快,语法更简洁。XPath 在需要向上查找父节点、基于文本定位时更适合。

提取复用 locator

# 将选择器定义为常量,方便维护
USERNAME_INPUT = (By.ID, "username")
LOGIN_BUTTON = (By.CSS_SELECTOR, "button[type='submit']")

driver.find_element(*USERNAME_INPUT).send_keys("admin")
wait.until(EC.element_to_be_clickable(LOGIN_BUTTON)).click()

使用 Page Object 模式组织代码

class LoginPage:
    URL = "https://example.com/login"
    USERNAME = (By.ID, "username")
    PASSWORD = (By.ID, "password")
    SUBMIT = (By.CSS_SELECTOR, "button[type='submit']")

    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)

    def open(self):
        self.driver.get(self.URL)
        return self

    def login(self, username, password):
        self.wait.until(EC.visibility_of_element_located(self.USERNAME)).send_keys(username)
        self.driver.find_element(*self.PASSWORD).send_keys(password)
        self.driver.find_element(*self.SUBMIT).click()
        return self

截图辅助调试

try:
    element = wait.until(EC.element_to_be_clickable((By.ID, "btn")))
    element.click()
except Exception as e:
    driver.save_screenshot("debug.png")
    raise

常见陷阱与注意事项

StaleElementReferenceException

页面刷新或 DOM 重新渲染后,之前持有的 WebElement 引用失效。解决方案:重新查找元素,或等待 staleness_of 后再查找。

# 错误做法:页面刷新后继续使用旧引用
el = driver.find_element(By.ID, "btn")
driver.refresh()
el.click()  # StaleElementReferenceException

# 正确做法:刷新后重新查找
driver.refresh()
wait.until(EC.staleness_of(el))  # 等待旧元素失效
el = wait.until(EC.element_to_be_clickable((By.ID, "btn")))
el.click()

ElementNotInteractableException

元素在 DOM 中存在但不可交互(被遮挡、display:none、opacity:0 等)。解决方案:等待 element_to_be_clickable,或用 JS 点击。

# JS 强制点击(绕过可见性限制,慎用)
driver.execute_script("arguments[0].click();", element)

class 属性包含空格时不能用 By.CLASS_NAME

By.CLASS_NAME 只接受单个 class 名。

# 错误:含空格会抛 InvalidSelectorException
driver.find_element(By.CLASS_NAME, "btn btn-primary")

# 正确:用 CSS Selector
driver.find_element(By.CSS_SELECTOR, ".btn.btn-primary")

XPath 中 @class= 是全量匹配

# 错误:class="btn btn-primary active" 不会被匹配到
//button[@class='btn']

# 正确:用 contains
//button[contains(@class,'btn')]

隐式等待与显式等待不要混用

两者同时存在时,超时时间会以不可预期的方式叠加,导致实际等待时间超出预期。只选择一种策略全局使用。

无头模式下 window-size 需要显式设置

无头模式下浏览器默认窗口极小,可能导致元素不可见或布局异常:

options.add_argument("--window-size=1920,1080")

反爬检测

部分网站检测 navigator.webdriver 属性:

options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
options.add_argument("--disable-blink-features=AutomationControlled")

# 注入脚本覆盖 webdriver 属性
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")

参见

Playwright完全指南
BeautifulSoup与lxml完全指南
httpx完全指南

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog