NodeJS Кукольник Получите InnerText дочерних элементов из XPath - PullRequest
0 голосов
/ 26 марта 2020

У меня есть проект, чтобы очистить продукты, приобретенные определенными клиентами, от внутренней CRM. Эта CRM использует много динамически загружаемых плиток, поэтому не так много согласованных имен классов (у многих есть идентификатор, произвольно добавляемый при каждой загрузке страницы), и на странице с одинаковым именем класса также много разных отчетов / элементов, поэтому Я не могу запросить всю страницу для селектора элемента.

Я определил "родительский" элемент, который я хочу, через xpath. Затем я хочу детализировать и получить innerText только дочерних элементов, которые соответствуют селектору запросов (в большинстве потоков, которые я вижу, есть люди, которые делают селектор запросов на всей странице, это будет получать результаты из меню, которое я не хочу).

Я могу сделать это обычным Javascript в консоли браузера, я просто не могу понять, как это сделать в Node / Puppeteer. Вот что у меня пока есть:

//Getting xpath of the "box" that contains all of the product tiles that a customer has
const productsBox = await page.$x("/html/body/blah/blah/blah");

Вот где это ломается. Я не очень знаком с некоторыми синтаксисами или пониманием документации Puppeteer, но я пробовал несколько различных методов (я также не достаточно удобен с функциями, чтобы использовать формат =>. Документация Puppeteer имеет пример того, что я пытаюсь сделать, но я пытался с той же структурой, и он также ничего не возвращал):

//Tried using the elementHandle.$$eval approach on the zero index of my xpath results, 
//but doesn't return anything when I console.log(productsList)
    const productsList = await productsBox[0].$$eval('.title-heading', function parseAndText (products) {
      productsList=[];
      for (i=0; i<products.length; i++) {
        productsList.push(products[i].innerText.trim());
      }
      return productsList;
      }
    );

//Tried doing the page.$$eval approach with selector, passing in the zero index of my xpath
      const productsList = await page.$$eval('.title-heading', function parseAndText (products) {
      productsList=[];
      for (i=0; i<products.length; i++) {
        productsList.push(products[i].innerText.trim());
      }
      return productsList;
      }, productsBox[0]

//Tried the page.evaluate and then page.evaluateHandle approach on the zero index of my xpath, 
//doing the query selection inside the evaluation and then doing something with that.
    let productsList= await page.evaluateHandle(function parseAndText(productsBoxZero) {
      productsInnerList = productsBoxZero.querySelectorAll(".title-heading");
      productsList=[];
      for (i=0; i<productsInnerList.length; i++) {
        productsList.push(productsInnerList[i].innerText.trim());
        //Threw a console log here to see if it does anything, 
        //But nothing is logged
        console.log("Pushed product " + i + " into the product list");
      }
      return productsList;
    }, productsBox[0]);

С точки зрения вывода, я записал в консоль некоторые переменные и я получаю это:

productsBox is JSHandle@node
productsBox[0] is JSHandle@node
productList is

Для сравнения, я делал это параллельно через Javascript в консоли, чтобы убедиться, что я правильно прошагаю по логи c и получаю то, что ожидаю :

>productsBox=$x("/html/body/blah/blah/blah");
>productsInnerList=productsBox[0].querySelectorAll(".title-heading");
>productsInnerList.length;
//2, and this customer has 2 products
>productsList=[];
>for (i=0; i<productsInnerList.length; i++) {
    productsList.push(productsInnerList[i].innerText.trim());
};
>console.log(productsList)
>["Product 1", "Product 2"]

Спасибо за прочтение, и я ценю вашу помощь!

[Редактировать]

Для некоторых дополнительных исследований я попытался использовать page.evaluateHandle и попытался записать мои переменные до сих пор:

productsBox is JSHandle@node
productsBox[0] is JSHandle@node
productList is JSHandle@array

Что является прогрессом. Я пытался сделать: let productsText=await productsList.jsonValue();

Но когда я пытаюсь вывести, я ничего не получаю:

await console.log("productsText is " + productsText);

productsBox is JSHandle@node
productsBox[0] is JSHandle@node
productList is JSHandle@array
productsText is

Ответы [ 2 ]

0 голосов
/ 27 марта 2020

На основании ответа @ mbit я смог заставить его работать. Сначала я проверил на другом сайте, который был похож по структуре на мой. Скопировал код на мой оригинальный сайт, и он все еще не работал, только получил нулевой вывод. Оказывается, хотя у меня была страница ожидания. $ X (full / xpath) для родительского элемента, дочерние элементы, которые содержали innerText, все еще не были загружены. Итак, я сделал две вещи:

1) Добавил еще одну страницу ожидания. $ X (full / xpath) для первого элемента в списке, который был одной из моих целей 2) Реализовал подход page.evaluate, предоставленный mbit , 2a) Явно выписал функцию (все еще оборачивая голову вокруг структуры =>)

Окончательный код ниже (некоторые имена переменных были изменены в результате тестирования):

let productsTextList= await page.evaluate(function list(list) {
  const productsInnerList = list.querySelectorAll(".title-heading");
  productsTextList =[];
  for (n=0; n<productsInnerList.length; n++) {
      product=productsInnerList[n].innerText.trim();
      productsTextList.push(product);
  }
  return productsTextList;
}, productsBox[0]);

console.log(productsTextList);

Я выбрал Подход page.evaluate, потому что он более точно соответствует тому, что я делал в консоли браузера, поэтому его легко протестировать. Трюк с подходом elementHandle. $$, как упоминалось в mbit, использовал await element.getProperty('innerText') вместо .innerText. В ходе устранения неполадок и обучения я также наткнулся на этот поток на GitHub , в котором также говорится о том, как его извлечь (аналогично подходу mbit выше). Для тех, кто сталкивается с подобными проблемами, вы не одиноки!

0 голосов
/ 27 марта 2020

Я бы посоветовал внимательно прочитать документы, прежде чем пытаться выполнять каждую функцию. $$eval оценивает селектор, и передача элемента в этом случае не имеет смысла. evaluateHandle - для возврата элементов на странице, так как вы возвращаете массив текста, и он сериализуем, вам это не нужно. Все, что вам нужно, это передать элемент в page.evaluate или сделать все в контексте кукловода.

Чтобы увидеть на странице console.log, вам необходимо:

page.on('console', msg => console.log(msg.text()));
  1. Использование page.evaluate
let productsList= await page.evaluate((element) => {
    const productsInnerList = element.querySelectorAll(".title-heading");
    const productsList=[];
    for (const el of productsInnerList) {
        productsList.push(el.innerText.trim());
        console.log("Pushed product " + el.innerText.trim() + " into the product list");
    }
    return productsList;
}, productsBox[0]);
Использование elementHandle.$$
const productList = [];
const productsInnerList = await productsBox[0].$$('.title-heading');
for (const element of productsInnerList){
    const innerText = await (await element.getProperty('innerText')).jsonValue();
    productList.push(innerText);
}
...