因此,我正在构建一个 express.js 应用程序,该应用程序使用 puppeteer 和 puppeteer-cluster 来访问一个乌克兰测试平台。我想做的事情是使用这个工具来解决测试。
所以,流程是这样的:
当我已经进入测试并回答了几个问题时,问题就出现了,并且在某些时候我收到了这样的错误:
Attempted to use detached Frame 'D78184744F4CA70F50307B66DDF53250'
。我的错误表明,获取以类“.v-test-questions-title”命名的元素时出现问题,而该元素确实必须存在。
async solveSingleAnswerQuestion() {
const pageElement = new PageElement(this.page)
const question = await pageElement.getContent('.v-test-questions-title') -> error says the problem is here
const variants = await pageElement.getContents(
'.v-test-questions-radio-block',
'.v-test-questions-checkbox-block'
)
console.log({ question, variants })
const answerIndex = await generateResponseIndex(
question || '',
variants
)
console.log({ answerIndex })
const variantsElements = await pageElement.getElements(
'.v-test-questions-radio-block',
'.v-test-questions-checkbox-block'
)
await variantsElements[answerIndex - 1].click()
const submitButton = await pageElement.getElement('.v-blue-button-test')
await submitButton.click()
await waitFor(5000)
}
下面是 PageElement 的代码(使所有代码更清晰的类)
import { cleanString, waitFor } from '@/utils'
import type { ElementHandle, Page } from 'puppeteer'
export class PageElement {
constructor(private readonly page: Page) {}
async loadAllElements() {
const pageElement = new PageElement(this.page)
const button = await pageElement.getElement('.vo-btn-blue')
if (button) {
await button.click()
await waitFor(5000)
await this.loadAllElements()
}
}
async getElementsByUniqueSelector(selector: string, fallback?: string) {
const elements = await this.getElements(selector, fallback)
const filteredElements = (
await Promise.all(elements.map(this.isElementWithSingleClass))
).filter((element) => element !== null)
return filteredElements
}
async getContentsByUniqueSelector(selector: string, fallback?: string) {
const elements = await this.getElementsByUniqueSelector(
selector,
fallback
)
const contents = await Promise.all(
elements.map((element) => element.evaluate((el) => el.textContent))
)
return this.cleanContents(contents)
}
async getContents(selector: string, fallback?: string) {
const elements = await this.getElements(selector, fallback)
const contents = await Promise.all(
elements.map((element) => element.evaluate((el) => el.textContent))
)
return this.cleanContents(contents)
}
async getElements(selector: string, fallback?: string) {
const elements = await this.waitForAndGetElements(selector)
if (elements.length > 0) {
return elements
}
if (fallback) {
return await this.waitForAndGetElements(fallback)
}
return []
}
async getContent(selector: string, fallback?: string) {
const element = await this.getElement(selector, fallback)
const content = await element?.evaluate((el) => el.textContent)
const cleanedContent = cleanString(content || '')
return cleanedContent
}
async getElement(selector: string, fallback?: string) {
const elements = await this.getElements(selector, fallback)
return elements[0]
}
private async waitForAndGetElements(selector: string) {
const el = await this.page
.waitForSelector(selector, {
visible: true,
timeout: 5000,
})
.catch(() => null)
const elContent = await el?.evaluate((el) => el.textContent)
console.log({ elContent })
return await this.page.$$(selector) -> error says that the problem is here. Puppeteer can't get the ".v-test-questions-title" element
}
private async isElementWithSingleClass(element: ElementHandle) {
const className = await element.getProperty('className')
const classList = (await className.jsonValue()).toString().split(' ')
return classList.length > 1 ? null : element
}
private async cleanContents(contents: (string | null)[]) {
return contents.map((content) => cleanString(content || ''))
}
}
还有我的集群初始化
import { Cluster } from 'puppeteer-cluster'
export const cluster = await Cluster.launch({
concurrency: Cluster.CONCURRENCY_CONTEXT,
maxConcurrency: 1000,
// monitor: true,
puppeteerOptions: {
headless: true,
dumpio: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
],
},
})
这是服务代码
async solveTestByTitle(testTitle: string) {
await this.enterCabinet()
const test = new Test(this.page)
const pageElement = new PageElement(this.page)
await test.startSolvingTestByTitle(testTitle)
const question = await pageElement.getContent('.v-test-questions-title')
console.log({ questionInService: question })
while (question) {
await test.solveSingleAnswerQuestion()
}
}
我一直在尝试很多事情,例如更改 puppeteer 选项、在容器中运行代码、更改浏览器、更改代码、在某些地方使用 page.waitForNavigation()、page.waitForNetworkIdle,但没有任何帮助,但有些方法只是更改了错误名称。
值得注意的是,当机器人回答问题时,网址不会改变。
我还没有找到任何解决方案。相反,我只是实现了重试逻辑。