The Elusive NodeList
Я застрял на этом 3 недели . Я испробовал буквально сотни попыток заставить это работать. Я просто не могу понять, что происходит не так. У меня закончились возможные решения, чтобы попробовать.
При этом ...
Краткое описание приложения
Я недавно согласился на подработку по разработке приложения, которое будет уведомлять клиента всякий раз, когда новый Jeep Wrangler публикуется на Facebook Marketplace. Это звучало довольно просто, и я согласился на эту работу. Я оценил завершение максимум за три дня. Это было в прошлом месяце. Ниже приведена блок-схема работы приложения:
Технология
После некоторого исследования, я решил go с NodeJs и Puppeteer для разработки приложения.
Шаг 1: Скрип
Мне пришлось ознакомиться со страницей, содержащей необходимую информацию. Это страница результатов поиска:
https://www.facebook.com/marketplace/tampa/search/?query=jeep%20wrangler&sortBy=creation_time_descend&exact=false
Как вы увидите, результаты перечислены в формате сетки. Поэтому мне нужно взять контейнер со списком элементов ...
<!-- This is the DIV that is the container nodelist -->
<div class="bq4bzpyk j83agx80 btwxx1t3 lhclo0ds jifvfom9 muag1w35 dlv3wnog enqfppq2 rl04r1d5">
Я нашел это, проверив консоль:
Я использовал тот же метод, чтобы найти элементы, содержащие заголовок , цена и изображение . Эта информация является дочерним узлом содержащего выше элемента.
Шаг 2: Код
После большого количества проб и ошибок я смог получить минимальную рабочую версию, которая будет парсить в поисках «iPhone X» в качестве поискового запроса. Отлично работает! Но когда я попытался адаптировать этот код, просто изменив ключевое слово поиска на «Jeep Wrangler», это не удалось. Вот суть рабочего кода:
https://gist.github.com/johnsdeveloper/1a7d02554dbfd682ee274b2ef0696f00
Мой код
Взяв этот рабочий код, я придумал мой исходный код ниже. Это не работает. Я получаю эту ошибку каждый раз:
(узел: 4928) UnhandledPromiseRejectionWarning: TypeError: Невозможно прочитать свойство length для undefined в removeDupes (C: \ wamp64 \ www\puppeteer \ index. js: 56: 32) в initScraper (C: \ wamp64 \ www\puppeteer \ index. js: 86: 27) (узел: 4928) UnhandledPromiseRejectionWarning: необработанное отклонение обещания.
Похоже, новые результаты листинга возвращаются пустыми, что означает, что «очистка» не сработала.
Вот мой полный код:
const puppeteer = require('puppeteer');
const jsonfile = require("jsonfile");
var fileName = "./public/data/newjeeps.json";
var changed = false;
var browser;
const getItems = async searchTerm => {
//{headless: false, defaultViewport: null} --> put this in launch() method below as parameter for developtment purposes --> opens up browser window
//const browser = await puppeteer.launch({headless: false, defaultViewport: null});
browser = await puppeteer.launch({
headless: true,
args: ["--no-sandbox"]
});
const page = await browser.newPage();
await page.goto(`https://www.facebook.com/marketplace/tampa/search/?query=jeep%20wrangler&sortBy=creation_time_descend&exact=false`);
await autoScroll(page);
const itemList = await page.waitForSelector('#mount_0_0 > div > div > div.rq0escxv.l9j0dhe7.du4w35lb > div > div > div.j83agx80.cbu4d94t.d6urw2fd.dp1hu0rb.l9j0dhe7.du4w35lb > div > div.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.cbu4d94t.d2edcug0.rj1gh0hx.buofh1pr.g5gj957u.hpfvmrgz.dp1hu0rb > div')
.then(() => page.evaluate(() => {
const itemArray = [];
const itemNodeList = document.querySelectorAll('#mount_0_0 > div > div > div.rq0escxv.l9j0dhe7.du4w35lb > div > div > div.j83agx80.cbu4d94t.d6urw2fd.dp1hu0rb.l9j0dhe7.du4w35lb > div > div.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.cbu4d94t.d2edcug0.rj1gh0hx.buofh1pr.g5gj957u.hpfvmrgz.dp1hu0rb > div > div > div > div > div > div > span > div > a');
itemNodeList.forEach(item => {
console.log(item);
});
return itemArray;
}))
.catch(() => console.log("Selector error."));
return itemList;
}
/**
* Remove any duplicates from JSON files
*
* @param {*} existingResults
* @param {*} newResults
* @returns array Returns new array of unique listings
*/
const removeDupes = async function (existingResults, newResults) {
var existingTitle;
var newTitle;
var newResults;
/* Loop through EXISTING (marketplacebot.json) */
for (var i = 0; i < existingResults.length; i++) {
/* Existing Title & Price TODO*/
existingTitle = existingResults[i].itemTitle;
/* Loop through NEW data */
for (y = 0; y < newResults.length; y++) {
/* New Title */
newTitle = newResults[y].itemTitle;
/* Do we have a match? */
if (existingTitle == newTitle) {
console.log("match");
// Remove from new results
newResults.splice(y, 1);
// Change detected?
changed = true;
}
}
}
return newResults;
}
const initScraper = async() => {
// Get currently listed items on Marketplace
const items = await getItems('Jeep Wrangler');
//items.sort(function(a, b){return a.itemPrice - b.itemPrice});
// Get existing JSON from file
const existing = await jsonfile.readFile(fileName);
// Compare the two, save only the differences
const results = await removeDupes(existing,items);
//console.log(results);
// Now save the differences back to the JSON files, the
// web page will pick up and display.
var success = await jsonfile.writeFile(fileName, results);
// If there were any differences, notify me
if(changed == true){
const page2 = await browser.newPage();
await page2.goto('http://sendmail.com/mail.php');
}
}
initScraper();
// This takes care of the auto scrolling problem
async function autoScroll(page) {
await page.evaluate(async () => {
await new Promise(resolve => {
var totalHeight = 0;
var distance = 100;
var timer = setInterval(() => {
var scrollHeight = document.body.scrollHeight;
window.scrollBy(0, distance);
totalHeight += distance;
if (totalHeight >= scrollHeight || scrollHeight>7000) {
clearInterval(timer);
resolve();
}
}, 100);
});
});
}