【发布时间】:2022-01-05 07:40:20
【问题描述】:
我正在尝试构建一个网络爬虫来获取有关某些产品的信息并将它们存储在数据库中。我正在使用 Nightmare 获取 HTML 源代码(因为 javascript 代码必须在创建页面内容之前在服务器上运行)然后我使用 Cheerio 解析该源代码。完成解析后,我必须为产品下载一些图像。我有一个简单的下载功能,根据我尝试下载的图像在服务器上是否可用,我想返回一个字符串(或字符串数组),其中包含图像名称(我下载的)或我计算机上的默认图像名称。我尝试调用下载函数作为承诺,当我知道有多个图像要下载时,我尝试传递 Promise.all(),但无济于事。虽然我很肯定我的代码正在运行(图像按应有的方式下载,最终对象在几乎每个属性和值上看起来都很棒),但当我将对象打印到控制台时,图像属性字段仍然持有 [Promise] / [ Promise { } ] 我不太确定如何解决这个问题。我很肯定这些承诺会解决,但是当我将结果对象输出到控制台时它们并没有解决。这是一个问题,因为我必须传递该对象以存储在数据库中,而且我认为它们不会得到解决。
代码(减去确切链接)在下面:
const cheerio = require('cheerio')
const nightmare = require('nightmare')()
const download = require('image-downloader')
const settings = new function() {
this.baseURL = 'https://baseurl.whatever'
this.urlSearch = `${this.baseURL}/Product/Search?keyword=`
this.urlVariant = 'https://cdn.baseurl.whatever/Variant/'
this.urlProduct = 'https://cdn.baseurl.whatever/Product/'
this.imgPath = './img/'
}
var review_id = 0
function downloadImage(url, filepath, success, error) {
return download.image({ url, dest: filepath }).then(success, error)
}
const url = 'https://someurl.nevermind.meh/product?pid=50M3NUMB3R',
code = '50M3C0D3'
async function scrapeProduct(code) {
const product = await nightmare.goto(url)
.wait()
.evaluate(() => document.body.innerHTML)
.end()
.then(body => console.log(loadProduct(body, code)))
.catch(err => console.log(`There was an error: [${err}]`))
}
function loadProduct(body, code) {
$ = cheerio.load(body)
return {
title: $('li.LongName').text().trim(),
category: $('a#categoryTitleLink').text().trim(),
min_price: parseFloat($('span.MinPrice').text()),
max_price: parseFloat($('span.MaxPrice')?.text()) || parseFloat($('span.MinPrice').text()),
points: parseFloat($('div.AddtoCartUnderText span').text()),
variants: [...$('div.productDetailClassicRnd')].map(variant => {
const $field = $(variant).find('input'),
item_code = $field.attr('item_code')
if (item_code.split('-')[0] == code) return null
return {
code: item_code.split('-')[0],
title: $field.attr('item_name'),
image: downloadImage(
`${settings.urlVariant}${item_code.replace(' ', '%20')}`,
`${settings.imgPath}${item_code}`,
result => result.filename.split('/').reverse()[0],
_ => 'variant_default-VC.jpg'
)
}
}).filter(variant => variant !== null),
images: [...$('img#imgProduct')].map(image => {
const $image = $(image),
source = $image.attr('src')
return downloadImage(
source,
`${settings.imgPath}${source.split('/').reverse()[0]}`,
result => result.filename.split('/').reverse()[0],
_ => 'product_default.jpg'
)
}),
other_images: [...$('img.productDetailOtherIMG')].map(image => {
const $image = $(image),
source = $image.attr('src')
// Check if the other image is not a default one
if (/default_\d{1,2}/.test(source)) return null
return downloadImage(
source,
`${settings.imgPath}${source.split('/').reverse()[0]}`,
result => result.filename.split('/').reverse()[0],
_ => null
)
}).filter(other_image => other_image !== null),
how_to_use: $('span#HowToUse p')?.text().trim() || "",
technical_description: $('span#TechnicalDescription p')?.text().trim() || "",
product_description: $('span#ProductDescription p')?.text().trim() || "",
bought_with: [...$('a.redirectProductId')].map(item => $(item).attr('href').match(/=(\d+)$/)[1]),
rank: $('div.productAverageMainDiv').find('i.activeStar').length,
reviews_count: parseInt($('span#spnReviewCount').text()),
reviews: [...$('div.customerReviewsMainDiv')].map(review => {
const $review = $(review)
return {
id: ++review_id,
author: $review.find('div.customerName').text().trim(),
posted_at: $review.find('div.starIconsForReviews span').text().trim(),
rank: $review.find('span.productAverageMainDiv').find('i.activeStar').length,
message: $review.find('div.customerReviewDetail span').text().trim()
}
})
}
}
scrapeProduct(code)
我什至无法从生成的图像名称数组中过滤空值,因为一旦我达到过滤器功能,这些承诺就不会解决。不知何故,我的印象是
images: downloadImage(
URL,
filepath,
resolve() {},
reject() {}
)
将等到 downloadImage 函数将值返回给 image 属性,然后执行过滤器函数。另一方面,考虑到我猜想早在我的 downloadImage 函数有机会解决 promise 之前执行流向 filter 函数,我会将 .then() 链接到 downloadImage,但我不能,因为 downloadImage位于 map() 函数的返回值内 - 这是代码中的 .filter() 函数的返回值。
任何帮助将不胜感激!谢谢!
P.S.:我很确定我正在监督一些基本(逻辑)的事情,或者我没有正确理解,我为浪费你的时间而道歉,但我现在在这件事上苦苦挣扎了两天,我好像没啥想法了^_^
【问题讨论】:
-
这里有很多代码,因此很难正确理解您的问题可能出在哪里,但您的
other_images属性是null的数组或可能解析为null的承诺。你会想要other_images: Promise.all(/* current code */).then(arr => arr.filter(img => img !== null))。 -
您能告诉我们如何您尝试使用
Promise.all吗?
标签: javascript promise