因为无限滚动页面的后续数据由JavaScript动态加载,requests仅获取初始HTML且不执行JS,故无法拿到滚动后内容;需用Selenium或Playwright模拟滚动并显式等待新元素出现。

为什么直接请求HTML拿不到无限滚动后的内容
因为无限滚动页面的后续数据通常由 JavaScript 动态拉取,初始 HTML 里只有首屏内容。requests 或 urllib 获取到的源码不执行 JS,自然没有滚动后加载的列表项。这不是反爬强度问题,是渲染机制差异——得让浏览器环境跑起来。
用 Selenium 模拟滚动时,execute_script 怎么写才稳定
别只用 window.scrollTo(0, document.body.scrollHeight) 一次滚到底:有些页面监听的是“接近底部”而非“到底”,且滚动太快可能触发防抖或漏加载。更稳妥的做法是分段滚动 + 等待新内容出现:
for i in range(5):
driver.execute_script("window.scrollBy(0, 800)")
time.sleep(1.2) # 给 JS 加载和渲染留出时间
# 检查是否新增了元素(比如 class="item" 的数量变多了)
items = driver.find_elements(By.CLASS_NAME, "item")
if len(items) >= expected_count:
break
-
scrollBy比scrollTo更可控,避免反复触底重试 -
time.sleep不要低于 0.8s,部分页面接口有节流(throttle) - 用
find_elements而非page_source判断是否加载完成,后者可能包含未渲染的占位节点
用 Playwright 替代 Selenium 有什么实际好处
Playwright 对滚动加载的适配更原生,尤其在等待动态内容上语义清晰、不易卡死:
page.goto(url)
page.evaluate("window.scrollBy(0, 5000)")
page.wait_for_function("document.querySelectorAll('.item').length > 20")
-
wait_for_function是关键:它会在浏览器上下文中轮询执行 JS 表达式,直到返回真值,比盲等sleep可靠得多 - 默认启用无头模式且启动快,
chromium/firefox/webkit三端行为一致,不用为 Safari 兼容性额外调试 - 对 Shadow DOM 和 iframe 内滚动也支持更好,Selenium 在这类结构里常找不到目标元素
滚动加载后怎么提取数据才不重复、不遗漏
很多页面滚动时会复用 DOM 节点(如虚拟列表),直接用 find_elements 取全部再遍历,容易拿到旧节点或空文本。正确做法是每次滚动后只抓“新增部分”:
- 记录上一轮末尾元素的
data-id或text特征,下一轮从该位置之后开始取 - 用
driver.find_elements(By.XPATH, "//div[@class='item'][position() > last_position]")这类带位置过滤的 XPath(需配合动态计算) - 更稳的方式:每轮滚动后,用
page.query_selector_all(".item")(Playwright)或driver.find_elements获取当前全部,再用 Python 去重(比如按item.get_attribute("data-key")构建集合)
滚动不是万能解法——有些站点用 IntersectionObserver API 监听可见性,纯 scrollTo 可能不触发;还有些在滚动后延时 300ms 才发请求,这个时间差必须覆盖到,否则就差那几条数据。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











