恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Playwright动态网页爬虫实战:滚动加载与点击翻页的通用处理套路
首页
资讯中心
/
Playwright动态网页爬虫实战:滚动加载与点击翻页的通用处理套路
Playwright动态网页爬虫实战:滚动加载与点击翻页的通用处理套路
发布时间:2026/10/21 9:08:33
先给结论动态页面本质上就两种列表加载方式一种是滚动到底触发加载一种是点按钮触发下一页。这两种形态占掉日常所见动态列表的九成以上。你只要把这两套模板练熟往后碰到任何需要交互才能翻页的网站基本都是往这两个方向上套区别只是选择器不同、等待条件不同而已。这篇笔记我按自己的实战习惯来写不是帮你把 Playwright 文档再抄一遍而是把处理动态列表时真正用得上的套路捋出来怎么判断页面加载完了、怎么避免重复提取、怎么判断已经翻到最后一页、什么时候该放弃 Playwright 去直接抓接口这些才是动态爬虫真正花时间的地方。1. 动态列表的根本认知页面不是死的1.1 为什么 requests 拿不到你要的数据很多新手会先遇到一个诡异的现象用 requests 请求一个列表页返回的 HTML 里什么都有唯独没有列表数据。这不是你写错了而是页面本身就是空壳子——数据是页面加载完成后由 JavaScript 再向后端发请求拿到响应后渲染到 DOM 里的。服务端返回的 HTML 只有一个框架和一堆 script 标签。在爬虫的语境里这套机制叫动态渲染。网站这么做的原因很多有的是为了前后端分离方便维护有的是为了用户体验更好也有的是为了增加自动化抓取的难度。但你要记住一个本质结论只要是动态页面你看到的列表数据一定不在最初的响应里必须在浏览器里等一等、动一动、数据才出现。这就解释了为什么要上 Playwright。requests 相当于你站在店门口往里喊问老板菜单上有什么老板没理你因为菜单是店员JavaScript在店铺里用投影仪DOM 渲染放出来的。Playwright 不一样它是直接把一个真人无头浏览器派进店里坐下菜单放出来他就看得见点哪桌的菜也清楚。1.2 动态列表的两种典型形态我接触过的动态列表九成以上跑不出这两种形态滚动加载也叫无限滚动。页面先渲染第一批数据你滚到接近底部时页面检测到滚动事件自动向接口请求下一批插入到列表末尾于是你继续滚、它继续加载永远没有尽头直到所有数据加载完或者后端起限制。点击翻页页面渲染当前页数据底部有按钮——可能是一排数字分页1、2、3、4也可能是一个“加载更多”“下一页”按钮。必须点击按钮浏览器才会请求并渲染新的数据。注意点击翻页里有个变种就是按钮不换内容也不关掉而是每次点击都在底部追加一批数据这种看起来和滚动加载很像但触发方式是点击。这里的关键区别是触发方式不同。滚动加载靠监听 scroll 事件触发网络请求点击翻页靠用户主动点击或键盘操作触发。你的爬虫代码必须模拟对应的动作页面才肯继续喂数据。理解了这一点后面所有代码逻辑就顺理成章了滚动加载的套路是“模拟滚动→等待渲染→提取数据→判断是否到底”点击翻页的套路是“点击按钮→等待渲染→提取数据→判断是否到最后一页”。通用套路的核心就是把这四个步骤用循环串起来。2. 准备工作Playwright 环境与最小可用模板2.1 安装与启动先说环境。Playwright 目前的稳定用法仍然是同步 API 配合 with 语句代码结构清晰调试也直观。安装就一句话pip install playwright装完还需要装浏览器内核playwright install chromium如果你的运行环境是服务器可能还需要手动装一些系统依赖库官方命令是playwright install-deps建议在 Linux 服务器上部署前提前跑一遍。我第一次在纯净容器里跑就吃了这个亏浏览器起不来报错信息里全是缺 so 文件。启动浏览器的结构非常固定以后所有脚本基本都长这样from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessFalse) page browser.new_page() page.goto(https://目标站点列表页) # ... 你的操作逻辑 browser.close()这里headlessFalse是带界面运行调试阶段强烈建议打开看真实效果。确认逻辑没问题后再改成headlessTrue在服务器上无头跑。带界面跑还有一个好处你能直观看到滚动加载时页面的变化节奏有利于后面调整等待策略。2.2 三个必须提前掌握的核心操作写动态列表爬虫之前先把这三个操作刻进脑子里后面全在用1. 等待元素出现page.wait_for_selector(css选择器或text选择器, timeout10000)这个方法会阻塞直到元素出现如果超时会抛异常。它的意义在于动态加载是需要时间的你提取数据前必须确保目标元素已经在 DOM 里了。10 秒只是常用默认值实际跑的时候我会根据网络状况调整到 15 秒或 5 秒。2. 执行 JS 滚动page.evaluate(window.scrollTo(0, document.body.scrollHeight))这一行把页面滚到底部。对无限滚动页面来说这个动作会触发加载逻辑。有些网站监听的是某个容器元素的 scroll 事件而不是 window此时把document.body替换成对应容器的选择器即可page.evaluate(document.querySelector(.列表容器选择器).scrollTo(0, 100000))3. 提取数据items page.locator(列表项选择器).all() for item in items: title item.locator(标题选择器).inner_text()Playwright 的 locator 体系比 Selenium 更严密支持 CSS、text、xpath 等。实际项目里 CSS 选择器最常用text选择器在处理“下一页”这种文字按钮时特别好用。2.3 一个最小示例先跑通再谈套路我不建议直接上完整项目先写个 20 行的最小示例验证环境没问题from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessFalse) page browser.new_page() page.goto(https://example.com/list) # 找一个真实动态列表页 page.wait_for_selector(.list-item) count page.locator(.list-item).count() print(f当前页面加载了 {count} 条数据) browser.close()这段代码的核心作用有两点一是验证 Playwright 环境没问题二是验证页面上列表项的选择器是否正确。选择器对了真实项目里至少省掉一半的排查时间。3. 滚动加载无限列表的通用处理套路3.1 套路的核心循环滚、等到位、再提取、看终止滚动加载页面的处理本质上是一个四步循环滚动到底部触发加载等待新数据渲染完成提取这批新增数据或全量重提判断是否已经滚到没有新数据的程度如果你对循环还不太敏感我拆开解释一遍。第一步的滚动动作必须真实发生页面才会向接口发请求第二步的等待最关键很多人漏数据就是因为滚完立刻提取这时候 DOM 还没来得及更新第三步的提取有两种策略后面细说第四步是滚动加载的难点页面没有明确的“页数”概念你必须通过某种信号判断“加载完了”。下面给一个通用模板适配多数无限滚动列表from playwright.sync_api import sync_playwright import time def scroll_and_extract(url, item_selector, max_scrolls50): 通用滚动加载提取 item_selector: 列表项选择器 max_scrolls: 最大滚动次数防止死循环 results [] with sync_playwright() as p: browser p.chromium.launch(headlessFalse) page browser.new_page() # 可选拦截图片加速加载 page.route(**/*.png, lambda route: route.abort()) page.route(**/*.jpg, lambda route: route.abort()) page.goto(url, timeout60000) page.wait_for_selector(item_selector, timeout10000) last_height 0 scroll_count 0 while scroll_count max_scrolls: # 1. 滚动到底部 page.evaluate(window.scrollTo(0, document.body.scrollHeight)) scroll_count 1 # 2. 等待页面高度变化或新元素出现 try: page.wait_for_function( () document.body.scrollHeight arguments[0], last_height, timeout5000 ) except Exception: # 高度不再变化认为到底了 break # 3. 提取当前所有项 items page.locator(item_selector).all() titles [item.inner_text() for item in items] results titles # 全量覆盖简单粗暴 # 4. 更新高度进入下一轮 last_height page.evaluate(document.body.scrollHeight) time.sleep(1) browser.close() return results这段代码就是整套滚动加载的核心骨架。接下来我把几个细节展开讲。3.2 滚动距离与速度不是随意选的滚动加载不是滚得越深越好。很多页面每次滚动只加载一批数据你一步滚到底部看到的效果是页面疯狂“跳动”——因为滚动事件触发得太快请求可能只发出去一部分。我在实际项目里踩过这个坑一次性滚到底结果只拿到前两三批数据后面的数据页面根本没来得及请求。所以实际写的时候我会把滚动改成渐进式def scroll_gradually(page, step800, delay0.5): 每次滚动固定步长给页面缓冲时间 current 0 max_height page.evaluate(document.body.scrollHeight) while current max_height: current step page.evaluate(fwindow.scrollTo(0, {current})) time.sleep(delay) max_height page.evaluate(document.body.scrollHeight)步长选 800 是因为在很多屏幕尺寸下800 像素大约是视口高度的 80%不会触发浏览器的“快滚到底”加速行为也不会因为步子太小导致循环次数太多。delay 选 0.5 秒是给网络请求留时间。注意这俩参数不是定死的页面加载快就用大步长、短延迟加载慢就反过来。另一种做法是滚动一步、等一批数据出现、再滚下一步这种最稳但速度慢。我通常用在要求高完整性的场景比如爬全量商品信息。3.3 终止条件的本质判断滚动加载最大的坑是“不知道什么时候结束”。前面模板里我用的是比较页面高度——当高度不再变化认为到底。这个办法对大多数页面有效但有几个例外页面底部有推荐区块、页脚滚动不会再加载新数据但高度仍然存在。此时你会停不下来一直循环直到 max_scrolls 超限。部分页面的滚动容器不是 document.body而是某个内嵌 div此时document.body.scrollHeight永远是同一个值判断失效。针对第一种情况我会检查最后两次滚动之间列表项的个数是否还在增加。如果连续滚动多次数量不再变化就认为到底prev_count 0 stable_rounds 0 while scroll_count max_scrolls: page.evaluate(window.scrollTo(0, document.body.scrollHeight)) time.sleep(1) current_count page.locator(item_selector).count() if current_count prev_count: stable_rounds 1 if stable_rounds 3: break else: stable_rounds 0 prev_count current_count连续三次数量没变化基本可以断定到底了。这个阈值我调过很多次三次是比较可靠的平衡点——网络慢的时候偶尔一两次没加载出来三次还没变化那大概率是真没了。针对第二种内嵌容器的情况改动也只是把滚动目标和高度判断都换成容器元素page.evaluate(document.querySelector(.feed-list).scrollTo(0, 1000000))3.4 数据提取的两种策略滚动加载场景下数据提取有两个流派各有适用场景全量覆盖式每次滚动后重新提取页面所有列表项直接覆盖结果列表。优点是逻辑简单不会重复因为每次都是全量缺点是如果单轮提取耗时较长滚动后页面可能又插入了新数据最后会有轻微遗漏。适合列表项数量不夸张几百条内的场景。增量追加式每次都重新提取然后按某个唯一标识比如标题、链接去重后追加到结果。优点是速度快适合上万条数据的场景缺点是去重逻辑要写好否则容易重复。实际操作中我多数用增量追加因为滚动加载的列表动辄几十甚至上百次请求全量覆盖的时间成本太高。示例seen set() all_data [] while ...: # 滚动、等待 items page.locator(item_selector).all() for item in items: title item.locator(.title).inner_text() if title in seen: continue seen.add(title) all_data.append(title)这里用标题作为唯一标识前提是标题不会重复。如果列表有重复标题的可能改用详情页链接或者数据 ID 更稳妥。4. 点击翻页分页与“加载更多”的通用套路4.1 分页按钮的套路点、等、提、判点击翻页比滚动加载的终止条件更直观因为页面给了你“最后一页”的信号。但操作步骤本身仍然要写对。通用的分页翻页模板长这样def paginate_click(url, item_selector, next_btn_selector, max_pages10): 通用点击翻页提取 next_btn_selector: 下一页按钮的选择器 all_data [] with sync_playwright() as p: browser p.chromium.launch(headlessFalse) page browser.new_page() page.goto(url, timeout60000) page.wait_for_selector(item_selector, timeout10000) for page_num in range(1, max_pages 1): print(f正在提取第 {page_num} 页) # 1. 提取当前页数据 items page.locator(item_selector).all() for item in items: all_data.append(item.inner_text()) # 2. 判断下一页按钮是否存在且可点击 next_btn page.locator(next_btn_selector) if next_btn.count() 0 or not next_btn.is_enabled(): print(没有下一页按钮了结束) break # 3. 点击下一页 next_btn.click() # 4. 等待页面刷新等待旧元素消失或新元素出现 try: page.wait_for_selector(item_selector, timeout10000) except Exception: print(f第 {page_num} 页点击后没有加载出新元素) break # 可选等待 URL 变化或页码变化 time.sleep(0.5) browser.close() return all_data这段代码有几个地方需要特别注意我逐个说一下。第一提取数据和点击操作的顺序不能反。必须先提取当前页数据再点击下一页否则你会漏掉当前页。这个顺序听起来是废话但我见过有人把点击写在提取前面的脚本一跑就翻到第二页才开始提取第一页数据直接丢了。第二点击后不能立刻提取必须等待页面刷新。这里有一个判断刷新完成的技巧——等待当前页的某个标志性元素消失或者等待页码数字变化。可以结合wait_for_selector的state参数# 等待旧元素进入 detached 状态从 DOM 中移除 try: page.wait_for_selector(.pagination .current, statedetached, timeout10000) except Exception: pass # 如果当前页码元素没有消失可能页面是局部刷新有的站点翻页是整页跳转整个 DOM 都会变化此时上面的代码用来等待旧 DOM 卸载它消失说明新页面已经在渲染了。有的站点是局部刷新只换列表区域分页按钮不变这段时间等待条件要换成“列表区域内容变化”才行。第三页码不是数字而是“加载更多”按钮时点击后按钮不会消失数据会继续追加。这种情况的终止条件不能靠按钮消失判断要靠数据数量是否变化。4.2 “加载更多”按钮披着点击外衣的滚动加载很多站点没有数字分页只有一个“加载更多”按钮点击一次加载一批新数据按钮还在原地。这种形态我习惯把它当作滚动加载来处理只不过触发动作从滚动变为点击。模板如下def click_load_more(url, item_selector, load_more_selector, max_clicks50): all_data [] with sync_playwright() as p: browser p.chromium.launch(headlessFalse) page browser.new_page() page.goto(url, timeout60000) page.wait_for_selector(item_selector, timeout10000) prev_count 0 stable_rounds 0 for i in range(max_clicks): # 提取当前数据 items page.locator(item_selector).all() current_count len(items) # 判断是否还在变 if current_count prev_count: stable_rounds 1 if stable_rounds 2: print(加载更多已到头) break else: stable_rounds 0 prev_count current_count # 汇总数据 for item in items: all_data.append(item.inner_text()) # 点击加载更多 btn page.locator(load_more_selector) if btn.count() 0 or not btn.is_visible(): print(加载更多按钮不存在或不可见) break btn.click() # 等待新数据渲染 time.sleep(1) browser.close() return all_data注意这里用“连续两轮数量不变”作为终止条件因为按钮点击后数据是追加的只能在数量上做文章。有些页面加载更多到最后一页后按钮会被移除这时判断btn.count() 0会直接跳出如果按钮还在但点击无效就是数量不变的情况。4.3 点击翻页时最容易踩的坑点击翻页里最容易翻车的场景有三个我单独拎出来坑一按钮明明定位到了click 却没有任何反应。原因通常是按钮被另一个元素遮挡或者是按钮绑定的事件在某个初始化完成后才生效。解决方法有两个一是改用btn.click(forceTrue)强制点击二是用 JavaScript 直接触发page.evaluate(document.querySelector(按钮选择器).click())前者适合遮挡不严重的情况后者属于绕过式触发能解决大部分“假点击”。坑二点击后页面会打开新标签页。有些页面的“下一页”是target_blank外链直接点击会开新标签Playwright 默认不会自动切换过去。处理方式是监听新页面事件with page.expect_popup() as popup_info: next_btn.click() new_page popup_info.value new_page.wait_for_load_state() # 然后在 new_page 上操作不过说实话遇到这种页面我通常先看看 URL 规律——很多外链下一页其实就是个分页 URL直接拼参数用 requests 反而更简单。坑三点击翻页时登录态丢了。某些站点的分页接口需要登录凭证页面跳转时 cookie 或 token 校验不过去。排查方式是观察点击翻页后的响应状态用 Playwright 的 network 事件page.on(response, lambda resp: print(resp.url, resp.status))如果点击后看到某个接口返回 401 或 403大概率就是登录态问题。解决方式是把登录流程前置先登录成功后再进列表页而不是直接访问列表页。5. 机制与选型什么时候该用 Playwright什么时候该绕开5.1 两种加载方式的对比速查用表格给你整理一个对比方便以后遇到页面时直接判断用哪套模板对比维度滚动加载点击翻页分页按钮点击翻页加载更多触发方式滚动到特定位置点击数字/下一页按钮点击加载更多按钮终止条件滚动高度不再变化/列表项数量连续不变下一页按钮消失/禁用按钮消失/数量连续不变提取速度较慢受滚动节奏限制较快翻页间隔可控较慢取决于加载速度稳定性中容易滚过头高分页逻辑清晰中按钮状态判断需要小心常用等待策略wait_for_function 高度变化等待旧元素 detached等待列表项数量增加数据完整性高理论上可滚到底高页数明确高直到按钮消失我个人的经验是遇到滚动加载先看接口遇到点击翻页优先用 Playwright 硬跑。原因很简单滚动加载往往背后是一个连续翻页的接口接口参数通常就 page 或 offset 两个数字构造起来很容易而点击翻页虽然也有接口但不少后端校验了请求头、Referer、加密参数直接用 requests 反而不如 Playwright 模拟点击稳定。这不是绝对的但作为快速选型的参考很有效。5.2 什么时候应该放弃 Playwright 转抓接口Playwright 不是银弹在两种场景下我会主动放弃它第一个场景是接口结构极其简单时。打开开发者工具滚动列表如果发现每一次滚动都是同一个接口URL 参数里就一个 page 数字或者一个 offset 偏移量且响应是干净的 JSON直接用 requests 循环请求接口速度至少是 Playwright 的十倍还不用处理浏览器资源。这时候硬用 Playwright 属于给自己加戏。判断方式很简单滚动页面看 Network 面板如果请求 URL 规律清晰、没有特殊加密参数先用 requests 试试。能通就直接抓接口不折腾。第二个场景是页面极其复杂滚动加载还嵌套了多级 iframe、Shadow DOM。这种页面用 Playwright 写选择器会非常痛苦一步步穿透层级不仅代码冗长而且页面一改版就崩。相比之下直接分析接口往往更省事。不过注意如果接口响应是加密文本或者需要特殊签名那就还是老老实实回归 Playwright至少它不跟你玩加密那一套。5.3 Playwright 与 Selenium 的选型差异既然你已经学到 Playwright 了顺便把和 Selenium 的差异说清楚。两者都能做动态页面爬取但体验差距很明显安装体验Playwright 一条命令装完能跑自带浏览器内核Selenium 需要额外下载对应版本驱动还得手动配路径版本一不匹配就报错。API 设计Playwright 的 locator 体系明显更现代支持自动等待Selenium 的 find_element 系列写完经常要手动 sleep 等渲染。调试能力Playwright 自带 codegen 录制功能和 trace 回放定位选择器时可以直接录制操作生成代码这在 Selenium 里没有对应级别的工具。稳定性Playwright 的 auto-waiting 机制很大程度上避免了元素未出现的竞态问题。不过 Selenium 在一点上仍有优势老项目生态成熟网上资料多部分浏览器自动化场景如既有系统兼容只能用它。新项目我基本只用 Playwright。6. 常见问题与排查技巧实录最后集中整理我在实际项目中踩过的坑和排查方法按出现频率排序遇到问题直接来对号入座。6.1 元素定位到了点击却没有反应现象代码执行到btn.click()不报错程序也不终止但页面就是没有任何变化。排查步骤在带界面模式下看按钮是否真的被点击了有没有高亮或涟漪效果。检查按钮是否被遮罩层遮挡用btn.is_visible()验证。查看 Network 面板确认点击后是否发出了请求。解决方案先试forceTrue再试 JS 点击。如果 JS 点击也没有反应有两种可能——按钮绑定的监听事件还没完成初始化等待时间加长或者按钮不是真的翻页按钮而是某个弹出层的触发点。常见于页面里其实有两个“下一页”按钮一个隐藏在弹窗里。定位时选择器写得要具体把父容器也带上。6.2 wait_for_selector 超时页面一直卡在加载状态现象wait_for_selector抛 TimeoutError但页面肉眼可见已经渲染了部分数据。原因选择器写得和实际 DOM 结构不匹配或者目标元素渲染完成后又被页面 JavaScript 篡改了比如先渲染数据再套一个壳子。解决方案换选择器优先用稳定的 class 名或 data 属性。不要用那些带数字后缀的动态 class很多框架生成的类名每次刷新都会变。另外可以先用page.content()打印当前 HTML肉眼确认目标元素真实存在后再据此改选择器。6.3 提取到的数据重复现象循环跑完后结果里有大量一模一样的条目。原因增量追加时去重逻辑失效或者滚动/点击发生的速度太快页面没及时加载新数据导致同一批数据被提取了多次。解决方案先用标题加链接一起作为唯一键如果是纯文本列表没有链接用“序号 标题”组合去重。另外检查一下循环里的等待时间太短的话加长到 1 秒以上。6.4 无头模式下页面加载速度异常慢现象headlessTrue 时原本带界面跑得好好的页面无头模式下一片空白或者等了很久才渲染。原因部分网站检测到无头浏览器的指纹特征会返回降级页面。这不是 Playwright 的问题是目标站点识别了 navigator.webdriver 之类的特征。解决方案加启动参数规避常见做法是去掉部分 webdriver 特征browser p.chromium.launch( headlessTrue, args[ --disable-blink-featuresAutomationControlled, --disable-dev-shm-usage, ] )加--disable-dev-shm-usage能解决一部分容器环境下渲染内存不足的问题。如果仍然不行退一步用 headlessFalse 在后台跑。6.5 页面弹窗、广告盖住了按钮现象页面正常加载但弹了一个新用户引导的遮罩层click()报错说元素不可点击因为它被遮挡了。解决方案先关弹窗再点按钮弹窗关闭按钮通常可以强杀try: page.locator(.close-btn).click(timeout3000) except Exception: pass如果弹窗没有显式关闭按钮可以直接按 ESC 键部分弹窗支持键盘关闭。再不行就用page.evaluate把遮罩层的 display 设为 none。page.evaluate(document.querySelector(.mask).style.display none)6.6 翻页后页码没变实际跳到了别的页现象点击 “下一页”按钮后URL 变了但页码数字没变或者提取到的数据是上一页的。原因页面的翻页是按表单提交实现的点击按钮后其实发了一次 POST 请求页面 URL 不会变DOM 局部刷新。此时不能用“等待旧元素消失”来判断刷新完毕因为分页区域可能根本没变化。解决方案改用“等待列表区域第一个条目变化”的方式。实现可以用wait_for_functionpage.wait_for_function( (old_title) { const first document.querySelector(.list-item .title); return first first.innerText ! old_title; }, first_title_at_current_page, timeout10000 )实践中这个判断往往比等待“某个元素消失”更可靠因为局部刷新的页面里很多元素是复用的不会消失。6.7 网络请求被目标站点的频率限制拦截现象滚动加载跑到中途某一次滚动后页面不再加载新数据重启浏览器发现前几十次都很正常后面无论如何都不出数据。原因触发频率限制目标站点对单位时间内的列表接口请求数做了限制短时间内高频滚动就会被封。解决方案控制滚动速度每两次滚动之间增加随机间隔。时间用 0.8 到 1.5 秒之间的随机浮点数避免请求节奏规律化也让行为更接近真人浏览。另外可以在请求中加入 Cookie 保持会话统一而不是每次重新开启浏览器否则身份特征变化更容易触发风控。实际项目中我的取舍习惯这篇写到这里已经很长了最后再说一点工作流层面的个人习惯。我接到一个动态列表需求时不是上来就写 Playwright 脚本而是先花五分钟干三件事打开开发者工具看 Network 里有没有直出的 JSON 接口观察页面是滚到底加载还是点按钮才加载确认有没有明显的分页参数规律。这三件事决定了整个方案是走接口直取还是走浏览器自动化。如果接口直取可行我绝不会为了“显得高级”而硬套 Playwright如果接口加密严重Playwright 就是最省心的兜底手段。这两种思维要并存而不是一味迷信某个工具。另外Playwright 脚本写完后我建议你花点时间把循环里的 sleep 优化成 wait_for 系列方法让等待时间自适应页面加载速度。这样做的好处有两个——脚本更稳不至于因为某次网络慢而误判终止速度也更优页面快的时候不等多余时间页面慢的时候不提前抢跑。动态页面难的不是技术本身而是对“页面什么时候算加载完”的判断。这个感觉只能靠多看多跑积累没有捷径。你把滚动加载和点击翻页这两套模板多跑几个真实网站自然就摸到规律了。