Очистка динамических страниц с помощью node.js и безголового браузера
Я пытаюсь удалить данные со страницы, которая загружается динамически. Для этого я использую безголовый браузер кукловод
Puppeteer можно рассматривать как headlessBrowserClient в коде.
Основная задача - аккуратно закрыть браузер, как только будут получены необходимые данные. Но если вы закроете его раньше, чем будет завершено выполнение defineCustomCode, то при этом произойдет потеря прогресса
defineCustomCode - это функция, которую можно вызывать так, как будто мы запускаем ее в инструментах Chrome Dev.
Чтобы контролировать сетевые запросы и асинхронный поток API кукловода - я использую асинхронный генератор, который инкапсулирует всю логику, описанную выше.
Проблема в том, что я чувствую, что код пахнет, но я не вижу лучшего решения.
Идеи?
module.exports = function buildClient (headlessBrowserClient) {
const getPageContent = async (pageUrl, evaluateCustomCode) => {
const request = sendRequest(pageUrl)
const { value: page } = await request.next()
if (page) {
const pageContent = await page.evaluate(evaluateCustomCode)
request.next()
return pageContent
}
}
async function * sendRequest (url) {
const browser = await headlessBrowserClient.launch()
const page = await browser.newPage()
const state = {
req: { url },
}
try {
await page.goto(url)
yield page
} catch (error) {
throw new APIError(error, state)
} finally {
yield browser.close()
}
}
return {
getPageContent,
}
}
1 ответ
Ты можешь использовать waitForFunction
или же waitFor
а также evaluate
с Promise.all
, Независимо от того, насколько динамичен веб-сайт, в конце вы ждете, чтобы что-то стало правдой, и закройте браузер, когда это произойдет.
Поскольку у меня нет доступа к вашему динамическому URL, я собираюсь использовать некоторые случайные переменные и задержки в качестве примера. Он разрешится, как только переменная вернет истину.
await page.waitForFunction((()=>!!someVariableThatShouldBeTrue);
Если ваша динамическая страница фактически создает селектор где-то после того, как вы оцениваете код? В таком случае,
await page.waitFor('someSelector')
Теперь вернемся к вашему customCode, позвольте мне немного его переименовать,
await page.evaluate(customCode)
Где customCode - это то, что будет устанавливать переменную someVariableThatShouldBeTrue
где-то правда. Честно говоря, это может быть что угодно, запрос, строка или что угодно. Возможности безграничны.
Вы можете поместить обещание внутри страницы. Оцените, что недавно хром очень хорошо их поддерживает. Таким образом, следующее также будет работать, разрешите после загрузки функции / данных. Убедитесь, что customCode является асинхронной функцией или возвращает обещание.
const pageContent = await page.evaluate(CustomCode);
Хорошо, теперь у нас есть все необходимые части. Я немного изменил код, чтобы он не пах мне:D,
module.exports = function buildClient(headlessBrowserClient) {
return {
getPageContent: async (url, CustomCode) => {
const state = {
req: { url },
};
// so that we can call them on "finally" block
let browser, page;
try {
// launch browser
browser = await headlessBrowserClient.launch()
page = await browser.newPage()
await page.goto(url)
// evaluate and wait for something to happen
// first element returns the pageContent, but whole will resolve if both ends truthy
const [pageContent] = await Promise.all([
await page.evaluate(CustomCode),
await page.waitForFunction((() => !!someVariableThatShouldBeTrue))
])
// Or, You realize you can put a promise inside page.evaluate, recent chromium supports them very well
// const pageContent = await page.evaluate(CustomCode)
return pageContent;
} catch (error) {
throw new APIError(error, state)
} finally {
// NOTE: Maybe we can move them on a different function
await page.close()
await browser.close()
}
}
}
}
Вы можете изменить и настроить его по своему желанию. Я не тестировал окончательный код (так как у меня нет APIError,valuCustomCode и т. Д.), Но он должен работать.
У него нет всех тех генераторов и тому подобного. Обещания, вот как вы можете иметь дело с динамическими страницами: D.
PS: IMO, такие вопросы больше подходят для обзора кода.