无限滚动的页面,HTML 里只有第一屏的数据,其余要滚动才会加载。爬虫怎么滚、什么时候停、遇到视口很高时为什么会卡住?这篇用一个本机的无限滚动页面和 CDP 逐个试。
打开后条目数 10,1.5 秒后条目数 10,服务器收到 /api/items 请求 1 次(页面一打开,空列表里的哨兵就在视口内,IntersectionObserver 第一次回调就加载了第 1 页;加载完后哨兵被推到视口外,不滚动就不会再触发);页面总高度 1020 px
页面打开时只有第一页数据,哨兵被推到视口外之后不再触发。HTML 解析或 requests 只能拿到这一页(见第 54 篇)。
滚了 11 次(含最后确认不再增加的 3 次),条目数 40,/api/items 请求 4 次,用时 1397 ms,页面显示结束标记:False
匀速滚动加“连续几次没增长就停”很容易提前停下:我的例子里每页 1000 px,滚 3 次 300 px 还没碰到哨兵,就在 40 条停了(100 条里只抓到 40 条)。
跳了 11 次,条目数 100,/api/items 请求 10 次,用时 1741 ms,结束标记:True
每次滚到页面底部更可靠,哨兵一定会进入视口:11 次滚动拿到全部 100 条,10 次请求,最后出现结束标记。
朴素:只响应"进入视口":不滚动等 1.5 秒后条目数 10;再滚动 2 次后 10;/api/items 请求共 1 次 修正:加载完成后重新 observe:不滚动等 1.5 秒后条目数 40;再滚动 8 次后 100;/api/items 请求共 10 次
IntersectionObserver 在元素的可见性发生变化时才回调。如果加载完一页后哨兵仍在视口内(视口很高),就不会再有回调,页面卡住——这是页面自己的 bug,但也会让爬虫拿不到数据。页面如果每次加载后重新 observe 就不会卡。
用结束标记判断:956 ms 到底;用"条目数 1.0 秒内不再增加"判断:2017 ms(比结束标记多出约 1060 ms;这是两次独立加载的差,其中包含那 1.0 秒静默期);两者都得到 100 条
能读到页面的结束标记(例子里哨兵变成 END)就用它。只能靠“条目数在静默期内不再增长”判断时,静默期要比一次加载的最长耗时长,否则会和第二节一样提前停下。