NodeJs分页,递归承诺问题

Posted

技术标签:

【中文标题】NodeJs分页,递归承诺问题【英文标题】:NodeJs Pagination,recursive promise problem 【发布时间】:2021-06-30 07:33:56 【问题描述】:

我在 node.js 中使用cheerio 和 axios 抓取多个页面 我在使用 Promises 时遇到了困难,如果我点击最后一页,有人可以帮我返回 JSON 吗?谢谢!

const getWebsiteContent = async (url) => 
    await axios.get(url).then(res => 

        const $ = cheerio.load(res.data)

        pageNum = getTotalpages($);  // Get the pagination

        console.log(url);

        //Some scraping here
    )
    indexPage++; // Increment to the next page

    const nextPageLink = baseUrl + '&page=' + indexPage;      // get next page

    if (indexPage > pageNum) 
        var editedText = text.slice(0, text.length - 1);
        editedText += ']';
        editedText = JSON.parse(editedText); // I want to return this and use elsewhere
        return editedText;
    
    setTimeout(async () => 
        getWebsiteContent(nextPageLink); // Call itself
    , 1000);

var myJSON= await getWebsiteContent(baseUrl); // something like this

【问题讨论】:

我在一些my answers 中使用asyncUnfold。我写这些已经有一段时间了,但它非常适合这种问题。如果今晚晚些时候我有时间,我可以向您展示如何使用异步生成器:D 您要返回哪个 JSON? 1:谢谢,我等着! 2:在 if(indexPage>pageNum) 中,我连接 JSON 字符串的最后一位,将其解析为 JSON。我想返回该 JSON。 不要混用 async/await.then() 和传递回调。将setTimeout 包装在一个promise 中,并决定一种处理promise 的风格。 【参考方案1】:

我会写 getPages 作为异步生成器 -

async function* getPages (href, initPage = 0) 
  const res = await axios.get(setPage(href, initPage))
  const $ = cheerio.load(res.data)
  const pages = getTotalpages($)
  yield  page: initPage, dom: $ 
  for (let p = initPage; p < pages; p++) 
    await sleep(1000)
    const r = await axios.get(setPage(href, p))
    yield  page: p, dom: cheerio.load(r.data) 
  

这取决于帮助器setPage,它使用url module 操作href 页码,这比手动将字符串拼凑在一起要安全得多-

function setPage (href, page) 
  const u = new URL(href)
  u.searchParams.set("page", page)
  return u.toString()

还有另一个助手sleep,它可以防止setTimeout 与基于async 的代码混合。这让我们可以轻松地在页面之间暂停 -

async function sleep (ms) 
  return new Promise(r => setTimeout(r, ms))

最后我们编写scrape,它是getPages 的简单包装。这允许我们重用getPages 函数来根据需要抓取各种元素。使用这种方法的一个好处是调用者可以确定每个页面发生了什么。下面我们推送到result 数组,但作为另一个示例,我们可以使用fs 模块将每个页面写入磁盘。显然这由你决定 -

async function scrape (href) 
  const result = []
  for await (const page, dom of getPages(href)) 
    console.log("scraped page", page)  // some status message
    result.push(getSomeData(dom))      // get something from each page
  
  return result


scrape(myUrl).then(console.log, console.error)

【讨论】:

【参考方案2】:

您不应该将 then 与您的 async / await 代码一起使用。 分页应该是这样的:

let response = await axios.get(url)
let $ = cheerio.load(response.data)
// do some scraping
while(url = $('[rel=next]').attr('href'))
  response = await axios.get(url)
  $ = cheerio.load(response.data)
  // do more scraping

【讨论】:

不,这不能解决我的问题。我的输出是这样的: url/page1 undefined url/page2 url/page3 END 我的输出应该是这样的: url/page1 url/page2 url/page3 JSON END

以上是关于NodeJs分页,递归承诺问题的主要内容,如果未能解决你的问题,请参考以下文章

NodeJS 中的 Firebase javascript 承诺

NodeJS 不能等待 MySQL 数据库承诺,而是等待其他承诺

如何承诺此功能-nodejs [重复]

如何使用 Bluebird 承诺 NodeJS Express

节点递归承诺永远不会退出

nodejs 中的异步和 Q 承诺