无法在 puppeteer 中使用 xpath 提取下一页 link
Can't extract next page link using xpath within puppeteer
我正在尝试找出一种方法,在 puppeteer 中使用 xpath 从 webpage 中抓取下一页 link。当我执行脚本时,我可以看到即使 xpath 正确,脚本也会得到乱码结果。我该如何解决?
const puppeteer = require("puppeteer");
const base = "https://www.timesbusinessdirectory.com";
let url = "https://www.timesbusinessdirectory.com/company-listings";
(async () => {
const browser = await puppeteer.launch({headless:false});
const [page] = await browser.pages();
await page.goto(url,{waitUntil: 'networkidle2'});
page.waitForSelector(".company-listing");
const nextPageLink = await page.$x("//a[@aria-label='Next'][./span[@aria-hidden='true'][contains(.,'Next')]]", item => item.getAttribute("href"));
url = base.concat(nextPageLink);
console.log("========================>",url)
await browser.close();
})();
当前输出:
https://www.timesbusinessdirectory.comJSHandle@node
预期输出:
https://www.timesbusinessdirectory.com/company-listings?page=2
首先,page.waitForSelector(".company-listing");
上缺少 await
。不等待这完全破坏了调用的意义,但它可能是它偶然起作用,因为非常严格的 waitUntil: "networkidle2"
覆盖了 select 或者你感兴趣,或者 xpath 是静态存在的(我懒得去检查了)。
一般来说,如果您在 page.goto
之后立即使用 waitForSelector
,waitUntil: "networkidle2"
只会减慢您的速度。只有在 waitForSelector
目标以外的页面上还有您需要的内容时才保留它,否则您将等待不相关的请求,这些请求会拉下与您的主要目标可能无关的图像、脚本和数据。如果它是一个加载缓慢的页面,那么增加 waitFor...
的超时是典型的下一步。
另一个注意事项是,在某些 CSS 目标上 waitForSelector
有点奇怪,然后立即尝试 select 一个 xpath。 waitForXPath
似乎更精确,然后在完全相同的 xpath 模式上调用 $x
两次。
接下来,让我们看看page.$x
的文档:
page.$x(expression)
expression <string>
Expression to evaluate.
returns: <Promise<Array<ElementHandle>>>
The method evaluates the XPath expression relative to the page document as its context node. If there are no such elements, the method resolves to an empty array.
Shortcut for page.mainFrame().$x(expression)
因此,与 evaluate
、$eval
和 $$eval
不同,$x
采用 1 个参数并解析为 elementHandle 数组。您的第二个参数回调不会像您想象的那样为您提供 href - 这仅适用于 eval-family 函数。
除了查阅文档,您还可以console.log
返回值来确认行为。您在 URL 中看到的 JSHandle@node
不是乱码,它是 JSHandle 对象的字符串化形式,提供了您可以对照文档进行交叉检查的信息。
解决方案是从函数返回的数组中获取第一个 elementHandle,然后 evaluate
使用您的原始回调在该句柄上:
const puppeteer = require("puppeteer");
const url = "https://www.timesbusinessdirectory.com/company-listings";
let browser;
(async () => {
browser = await puppeteer.launch({headless: true});
const [page] = await browser.pages();
await page.goto(url);
const xp = `//a[@aria-label='Next']
[./span[@aria-hidden='true'][contains(.,'Next')]]`;
await page.waitForXPath(xp);
const [nextPageLink] = await page.$x(xp);
const href = await nextPageLink.evaluate(el => el.getAttribute("href"));
console.log(href); // => /company-listings?page=2
})()
.catch(err => console.error(err))
.finally(() => browser?.close())
;
顺便说一句,还有 el => el.href
用于获取 href 属性。 .href
在这里包含基数 URL,因此您不需要连接。一般来说,behavior differs 除了提供绝对路径还是相对路径之外,所以最好了解这两个选项。
我正在尝试找出一种方法,在 puppeteer 中使用 xpath 从 webpage 中抓取下一页 link。当我执行脚本时,我可以看到即使 xpath 正确,脚本也会得到乱码结果。我该如何解决?
const puppeteer = require("puppeteer");
const base = "https://www.timesbusinessdirectory.com";
let url = "https://www.timesbusinessdirectory.com/company-listings";
(async () => {
const browser = await puppeteer.launch({headless:false});
const [page] = await browser.pages();
await page.goto(url,{waitUntil: 'networkidle2'});
page.waitForSelector(".company-listing");
const nextPageLink = await page.$x("//a[@aria-label='Next'][./span[@aria-hidden='true'][contains(.,'Next')]]", item => item.getAttribute("href"));
url = base.concat(nextPageLink);
console.log("========================>",url)
await browser.close();
})();
当前输出:
https://www.timesbusinessdirectory.comJSHandle@node
预期输出:
https://www.timesbusinessdirectory.com/company-listings?page=2
首先,page.waitForSelector(".company-listing");
上缺少 await
。不等待这完全破坏了调用的意义,但它可能是它偶然起作用,因为非常严格的 waitUntil: "networkidle2"
覆盖了 select 或者你感兴趣,或者 xpath 是静态存在的(我懒得去检查了)。
一般来说,如果您在 page.goto
之后立即使用 waitForSelector
,waitUntil: "networkidle2"
只会减慢您的速度。只有在 waitForSelector
目标以外的页面上还有您需要的内容时才保留它,否则您将等待不相关的请求,这些请求会拉下与您的主要目标可能无关的图像、脚本和数据。如果它是一个加载缓慢的页面,那么增加 waitFor...
的超时是典型的下一步。
另一个注意事项是,在某些 CSS 目标上 waitForSelector
有点奇怪,然后立即尝试 select 一个 xpath。 waitForXPath
似乎更精确,然后在完全相同的 xpath 模式上调用 $x
两次。
接下来,让我们看看page.$x
的文档:
page.$x(expression)
expression
<string>
Expression to evaluate.returns:
<Promise<Array<ElementHandle>>>
The method evaluates the XPath expression relative to the page document as its context node. If there are no such elements, the method resolves to an empty array.
Shortcut for
page.mainFrame().$x(expression)
因此,与 evaluate
、$eval
和 $$eval
不同,$x
采用 1 个参数并解析为 elementHandle 数组。您的第二个参数回调不会像您想象的那样为您提供 href - 这仅适用于 eval-family 函数。
除了查阅文档,您还可以console.log
返回值来确认行为。您在 URL 中看到的 JSHandle@node
不是乱码,它是 JSHandle 对象的字符串化形式,提供了您可以对照文档进行交叉检查的信息。
解决方案是从函数返回的数组中获取第一个 elementHandle,然后 evaluate
使用您的原始回调在该句柄上:
const puppeteer = require("puppeteer");
const url = "https://www.timesbusinessdirectory.com/company-listings";
let browser;
(async () => {
browser = await puppeteer.launch({headless: true});
const [page] = await browser.pages();
await page.goto(url);
const xp = `//a[@aria-label='Next']
[./span[@aria-hidden='true'][contains(.,'Next')]]`;
await page.waitForXPath(xp);
const [nextPageLink] = await page.$x(xp);
const href = await nextPageLink.evaluate(el => el.getAttribute("href"));
console.log(href); // => /company-listings?page=2
})()
.catch(err => console.error(err))
.finally(() => browser?.close())
;
顺便说一句,还有 el => el.href
用于获取 href 属性。 .href
在这里包含基数 URL,因此您不需要连接。一般来说,behavior differs 除了提供绝对路径还是相对路径之外,所以最好了解这两个选项。