我正在尝试使用 Python 和 Playwright 自动抓取具有“无限滚动”功能的网站。
问题是 Playwright 到目前为止还不包含滚动功能,更不用说无限自动滚动功能了。
根据我在网上发现的内容和我的个人测试,我可以使用
page.evaluate()
函数和一些 Javascript 代码自动执行无限或有限滚动。
例如,这有效:
for i in range(20):
page.evaluate('var div = document.getElementsByClassName("comment-container")[0];div.scrollTop = div.scrollHeight')
page.wait_for_timeout(500)
这种方法的问题在于,它要么通过指定滚动数量,要么通过告诉它通过
while True
循环永远继续下去来工作。
我需要找到一种方法来告诉它继续滚动,直到加载最终内容。
这是我目前正在尝试的Javascript
page.evaluate()
:
var intervalID = setInterval(function() {
var scrollingElement = (document.scrollingElement || document.body);
scrollingElement.scrollTop = scrollingElement.scrollHeight;
console.log('fail')
}, 1000);
var anotherID = setInterval(function() {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
clearInterval(intervalID);
}}, 1000)
这在我的 Firefox 浏览器或 Playwright Firefox 浏览器中都不起作用。它立即返回并且不会间隔执行代码。
如果有人能告诉我如何使用 Playwright 创建一个自动滚动功能,该功能将在到达动态加载网页的底部时检测并停止。
所以我找到了一个可行的解决方案。
我所做的是将 Javascript 与 python Playwright 代码结合起来。
我用 200 毫秒的计时器启动 setInterval,用
page.evaluate()
在页面上向下滚动,然后用 python 循环跟踪它,每秒检查页面的总高度(包括滚动)是否已更改。如果发生变化,则继续滚动,如果没有变化,则滚动结束。page.evaluate(
"""
var intervalID = setInterval(function () {
var scrollingElement = (document.scrollingElement || document.body);
scrollingElement.scrollTop = scrollingElement.scrollHeight;
}, 200);
"""
)
prev_height = None
while True:
curr_height = page.evaluate('(window.innerHeight + window.scrollY)')
if not prev_height:
prev_height = curr_height
time.sleep(1)
elif prev_height == curr_height:
page.evaluate('clearInterval(intervalID)')
break
else:
prev_height = curr_height
time.sleep(1)
使用新的 mouse.wheel(x, y) 功能查看下面的答案,了解使用 playwright 滚动的最新方式。将我的答案与他的答案结合起来,以减少使用 JS 的需要。
mouse.wheel(x, y)
。在下面的代码中,我们将尝试滚动浏览具有“无限滚动”的 youtube.com:
from playwright.sync_api import Playwright, sync_playwright
import time
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
context = browser.new_context()
# Open new page
page = context.new_page()
page.goto('https://www.youtube.com/')
# page.mouse.wheel(horizontally, vertically(positive is
# scrolling down, negative is scrolling up)
for i in range(5): #make the range as long as needed
page.mouse.wheel(0, 15000)
time.sleep(2)
time.sleep(15)
# ---------------------
context.close()
browser.close()
with sync_playwright() as playwright:
run(playwright)
其他解决方案对我来说有点冗长和“杀伤力”,而这对我有用。
这是一个两眼线,让我有些偏头痛才过来:)
注意:您必须放入您自己的选择器。这只是一个例子...
while page.locator("span",has_text="End of results").is_visible() is False:
page.mouse.wheel(0,100)
#page.keyboard.down(PageDown) also works
从字面上看,只需继续滚动,直到出现某种唯一的选择器。在这种情况下,当您滚动到底部时,会弹出带有字符串“结果结束”(针对我的用例的上下文)的跨度标记。
我相信你可以翻译这个逻辑以供你自己使用..
剧作家有
page.keyboard.down('End')
命令,它会滚动到页面末尾。
所以我遇到了类似的问题,但它是一个具有滚动的特定元素,而不是页面本身,我发现如果您单击有问题的元素并将焦点应用于它,page.mouse.wheel将滚动该特定元素。 (在我的例子中,我的元素是 tbody)
async scrollIntoView (locator : Locator) {
let i = 0;
while(await locator.isHidden()) {
await this.page.locator('your locator goes here').click();
await this.page.mouse.wheel(0, 300);
i++;
if (await locator.isVisible()) { return; }
else if (i >= 5) { return; }
}
}
您可以使用
i
删除增量防护,我只是将其放在那里以避免任何无限循环。
我创建了这个自动滚动功能来滚动到 Shopee (shopee.sg) 的页面底部。
async function autoScroll(page) {
const maxScrolls = 100;
const scrollDelay = 4000;
let previousHeight = 0;
let scrollAttempts = 0;
while (scrollAttempts < maxScrolls) {
await page.keyboard.down('End');
await page.waitForTimeout(scrollDelay);
const currentHeight = await page.evaluate(() => document.body.scrollHeight);
if (currentHeight === previousHeight) {
break;
}
previousHeight = currentHeight;
scrollAttempts++;
}
}
此代码对我有用:
prev_page_height = page.evaluate("$(document).height()")
print(f'Initial page height {prev_page_height}')
# scroll down
while True:
page.evaluate("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2) # giving some time to load the page
cur_page_height = page.evaluate("$(document).height()")
print(f'Current page height {cur_page_height}')
if cur_page_height > prev_page_height:
prev_page_height = cur_page_height
elif cur_page_height == prev_page_height:
break
这个话题很老,但对我来说很新。我一直在使用剧作家滚轮,但对我来说,它需要鼠标来控制/聚焦。
因此,如果我碰巧正在打字(通常是这样)并且它滚动,我美丽的文字就会消失,再也不会被看到。
我将继续尝试上面发布的 js 解决方案,看看这是否能让我解决鼠标/焦点问题。