Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/34.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 迭代中的StaleElementReferenceError_Javascript_Node.js_Selenium_Selenium Webdriver - Fatal编程技术网

Javascript 迭代中的StaleElementReferenceError

Javascript 迭代中的StaleElementReferenceError,javascript,node.js,selenium,selenium-webdriver,Javascript,Node.js,Selenium,Selenium Webdriver,我的应用程序从数据库中获取ID列表。我用光标迭代这些内容&对于每个ID,我使用Selenium将其插入URL,以获取页面上的特定项目。这是对关键字进行搜索&获取与该搜索最相关的项目。数据库中大约有1000个结果。在随机迭代中,1个驱动程序操作将抛出一个StaleElementReferenceError,其完整消息为: 陈旧元素引用:元素未附加到页面文档\n(会话信息:chrome=77.0.3865.75) 查看这些数据,我可以发现造成这种情况的两个常见原因是: 该元素已被完全删除 元素不再

我的应用程序从数据库中获取ID列表。我用光标迭代这些内容&对于每个ID,我使用Selenium将其插入URL,以获取页面上的特定项目。这是对关键字进行搜索&获取与该搜索最相关的项目。数据库中大约有1000个结果。在随机迭代中,1个驱动程序操作将抛出一个
StaleElementReferenceError
,其完整消息为:

陈旧元素引用:元素未附加到页面文档\n(会话信息:chrome=77.0.3865.75)

查看这些数据,我可以发现造成这种情况的两个常见原因是:

  • 该元素已被完全删除
  • 元素不再附加到DOM
前者是最常见的原因

index.js

const { MongoClient, ObjectID } = require('mongodb')
const fs = require('fs')
const path = require('path')
const { Builder, Capabilities, until, By } = require('selenium-webdriver')
const chrome = require('selenium-webdriver/chrome')

require('dotenv').config()

async function init() {
    try {
        const chromeOpts = new chrome.Options()
        const ids = fs.readFileSync(path.resolve(__dirname, '..', 'data', 'primary_ids.json'), 'utf8')
        const client = await MongoClient.connect(process.env.DB_URL || 'mongodb://localhost:27017/test', {
            useNewUrlParser: true
        })
        const db = client.db(process.env.DB_NAME || 'test')
        const productCursor = db.collection('product').find(
            {
                accountId: ObjectID(process.env.ACCOUNT_ID),
                primaryId: {
                    $in: JSON.parse(ids)
                }
            },
            {
                _id: 1,
                primaryId: 1
            }
        )
        const resultsSelector = 'body #wrapper div.src-routes-search-style__container--2g429 div.src-routes-search-style__products--3rsz9'
        const mostRelevantSelector = `${resultsSelector}
            > div:nth-child(2)
            > div.src-routes-search-product-item-raw-style__product--3vH_O:nth-child(1)`
        const titleContainerSelector = `${mostRelevantSelector}
            > div.src-routes-search-product-item-raw-style__mainPart--1HEWx
            > div.src-routes-search-product-item-raw-style__containerText--3NefD
            > div.src-routes-search-product-item-raw-style__description--3swql
            > div.src-routes-search-product-item-raw-style__titleContainer--tazkH`
        const productImageSelector = `${mostRelevantSelector}
            > div.src-routes-search-product-item-raw-style__mainPart--1HEWx
            > div.src-routes-search-product-item-raw-style__containerImages--1PfdF
            > a.src-routes-search-product-item-raw-style__productImage--1Y42Y
            > img`
        const linkSelector = `${titleContainerSelector} > a`
        const primaryIdSelector = `${titleContainerSelector} > p`

        chromeOpts.setChromeBinaryPath('/usr/local/bin')

        const driver = await new Builder()
                .withCapabilities(Capabilities.chrome())
                .forBrowser('chrome')
                .build()

        let newProds = {}
        let product
        let i = 0

        while (await productCursor.hasNext()) {
            i += 1
            product = await productCursor.next()

            let searchablePrimaryId = product.primaryId
            let link
            let primaryId
            let pId
            let href
            let img
            let imgSrc

            if (product.primaryId.includes('#')) {
                searchablePrimaryId = product.primaryId.substr(0, product.primaryId.indexOf('#'))
            }
            if (searchablePrimaryId.includes('-')) {
                searchablePrimaryId = searchablePrimaryId.substr(0, searchablePrimaryId.indexOf('-'))
            }
            await driver.get(`https://icecat.biz/en/search?keyword=${encodeURIComponent(searchablePrimaryId.toLowerCase())}`)
            link = await driver.wait(until.elementLocated(By.css(linkSelector)), 10000) // wait 10 seconds
            img = await driver.wait(until.elementLocated(By.css(productImageSelector)), 10000)
            imgSrc = await img.getAttribute('src')
            primaryId = await driver.wait(until.elementLocated(By.css(primaryIdSelector)), 10000)
            pId = await primaryId.getText()
            href = await link.getAttribute('href')
            const iceCatId = href.substr(href.lastIndexOf('-') + 1, href.length)
            const _iceCatId = iceCatId.substr(0, iceCatId.indexOf('.html'))
            const idFound = (searchablePrimaryId.toUpperCase() === pId.toUpperCase()) && !imgSrc.includes('logo-fullicecat')

            newProds[product._id.toString()] = {
                primaryId: product.primaryId,
                iceCatId:  idFound ? _iceCatId : 'N/A'
            }
        }
        const foundProducts = Object.values(newProds).filter(prod => prod.iceCatId !== 'N/A')
        console.log(`\nFound ${foundProducts.length}/${JSON.parse(ids).length}`)
        fs.writeFileSync(path.resolve(__dirname, '..', 'data', 'new_products.json'), JSON.stringify(newProds, null, 4), 'utf8')

        driver.quit()
    } catch(err) {
        throw err
    }
}

init()
    .then(res => {
        console.log(res)
    })
    .catch(err => {
        console.error(err)
    })
为了进行调试,我在每个驱动程序操作周围放置了一个
try…catch
,以查看哪个特定操作失败了,但它不起作用,因为它从来都不是一个失败的一致操作。例如,有时如果是
elementLocated
行中的一行或其他行,那么它就是
getAttribute
操作


如果在该场景中是后者,这就是为什么我对抛出此错误感到困惑的原因,因为selenium肯定在页面上找到了元素(即
链接
),但无法在元素上执行
getAttribute('src')
?这就是为什么我对我所犯的错误感到困惑。我想我一定是在设置selenium来处理迭代时出错了。迭代次数从不高于110

在您的情况下,第二个原因是元素不再连接到DOM。如果找到了
WebElement
,并且随后刷新了DOM,则即使DOM没有更改,该元素也会变得过时,同一定位器将返回新的
WebElement

通常,
driver.get()
会一直阻止,直到页面完全加载,但是此站点正在运行JavaScript来加载搜索结果。您可以通过在开发人员工具控制台中运行
document.readyState
来测试它,您将在搜索结果仍在加载时看到
“complete”
结果

在找到结果之前,页面有一个微调器,希望它足以等待它出现并在抓取页面之前变得陈旧

await driver.get(`https://icecat.biz/en/search?keyword=${encodeURIComponent(searchablePrimaryId.toLowerCase())}`)

let spinner = driver.wait(until.elementIsVisible(By.className('src-routes-search-style__loader---acti')))
driver.wait(until.stalenessOf(spinner))

link = await driver.wait(until.elementLocated(By.css(linkSelector)), 10000)

您不必等待Ajax请求完成。该网站在您结束时检索并刷新dom,并且每隔几秒钟不断调用索引,因此dom可能会不断更新。您可能可以保存AJAX请求、获取结果、处理并再次启用AJAX。

您是否可以尝试从img Src=wait img.getAttribute('Src')中删除“wait”。因为wait-for-img已经在它的前一行中处理过了。

在您找到元素和对其执行操作之间,dom仍然通过客户端脚本进行更新时,可能会发生这种情况。捕获陈旧元素异常可能会有所帮助。如果捕获到该特定异常,请重新运行find元素/do something函数。(WebDriverWait只会抛出timeout或stale元素。)您可以在这里找到一个例子(java):它看起来更像是一个注释,而不是答案。这是一个很好的解决方案,让我解决了我的问题!我唯一更改的是
elementIsVisible
需要一个WebElement,而不是一个选择器,因此更改为
elementLocated
。我还给了
驱动程序.wait()
一分钟的超时时间。这在第一次时似乎没有必要,但在我取下它时坏了。我现在有:
let spinner=wait driver.wait(直到.elementLocated(By.css(spinnerSelector))
wait driver.wait(直到.stalenessOf(spinner),60000)//1分钟