用node做爬虫,循环模拟接口获取数据,async/await怎么做到同步操作,控制流程?

发布于 2022-09-05 03:47:45 字数 4384 浏览 10 评论 0

目前的思路如下:1.获取分类下所有的标签页面url,2.循环抓取页面中抓取当前标签页获取json的api地址,3.抓取当前标签的商品列表,4.抓取当前标签的分页加载中的商品。

但是现在做到的是,第二步开始,未等到2-4执行完毕再循环下一个,有尝试用async/await,但是未实现流程控制。此处求教。

var http = require('http');
var fs = require("fs");
var superagent = require('superagent');
var urls = [];
var pageIndex = 1;
var xlsxData = '';

getGoodsUrl(urls);

function getGoodsUrl(urls){
    superagent
        .post('http://bravetime.davdian.com/index.php?c=Index&a=getCatNavList')
        .type('text/html; charset=utf-8')
        .set('Accept','application/json, text/javascript, */*; q=0.01')
        .end(function(err, res) {
            if (err) {
                console.log('分类数据请求失败');
            } else {
                console.log('分类数据请求成功');
                var resData = res.text;
                var resData = JSON.parse(resData); 
                if(resData.data.length > 0){
                    resData.data.forEach(function(item){
                        var rowObj = [];
                        var title = item.title;
                        var category = item.content.category;
                         category.forEach(function(item){
                             var text = [];
                             text.push(title+ '--' + item.text);
                             text.push(item.link);
                            rowObj.push(text);
                         });
                         urls.push(rowObj);
                     });
                     loopUrls(urls);
                } else {
                    console.log('分类数据为空');
                }

                // saveInfo(xlsxData);
            }
        })
}

function loopUrls(urls){
    urls.forEach(function(item){
        var row = item;
        row.forEach(function(item){
            var tagTitie = item[0];
            var tegUrl = item[1];
            getApiUrl(tagTitie,tegUrl);
        });
    });
}

function getApiUrl(title,url){
    var realUrl = 'http://bravetime.davdian.com' + url;
    http.get(realUrl,function(res){
         var html = '';
         res.on('data',function(data){
             html += data;
         });
         res.on('end',function(){
             console.log('正在获取' + title + '页面数据');
             var reg = /goodsUrl = "(.+)"/;
             var apiUrl = reg.exec(html);
             getGoodsJson(apiUrl[1],pageIndex);
         });
     }).on('error',function(){
         console.log('获取html出错!!');
     });
}

function getGoodsJson(url,pageIndex){
    superagent
        .post('http://bravetime.davdian.com/' + url + 'page_size=10&rp=catergory_search&rl=list')
        .send({page:pageIndex})
        .type('application/x-www-form-urlencoded; charset=UTF-8')
        .set('Accept','application/json, text/javascript, */*; q=0.01')
        .end(function(err, res) {
            if (err) {
                console.log('第' + pageIndex + '页请求失败');
            } else {
                console.log('第' + pageIndex + '页请求成功');
                var resData = res.text;
                var resData = JSON.parse(resData); 
                if(resData.data.length > 0){
                    resData.data.forEach(function(item){
                         xlsxData = xlsxData + item.goods_name + '  ' + item.shop_price + '  ' + item.goods_number + '\r\n';
                     });
                     pageIndex = parseInt(pageIndex) + 1;
                     setTimeout(function(){
                         getGoodsJson(url,pageIndex);
                     },200);
                } else {
                    console.log('数据已加载完毕');
                    saveTxt(xlsxData);
                    pageIndex = 1;
                    return false;
                }

                // saveInfo(xlsxData);
            }
        })

}

function saveTxt(data){
    fs.writeFile("create.txt",data,function (err) {
        if (err) throw err ;
         console.log("File Saved !"); //文件被保存
    }) ;
}
function saveInfo(data){
     var buffer = xlsx.build([{name: "mySheetName", data: data}]);
     fs.writeFileSync("myFile.xlsx", buffer, 'binary');
     console.log('excel保存成功');
}

下面是结果图示及代码执行顺序:

图片描述

图片描述

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

︶葆Ⅱㄣ 2022-09-12 03:47:45

你这整个过程都是异步的,没看出一点同步的意思。我觉得你可能没有理解什么是异步。

Async/await 是建立在 Promise 基础上的,而 Superagent 本身是支持 Promise 的,你可以直接用 async/await。

async function() {
  try {
    const result = await superagent.get(url);
    console.log(result.headers);
    console.log(result.body);
  } catch (error) {
    console.error(error);
  }
}

http://visionmedia.github.io/...

http://www.ruanyifeng.com/blo...

然后你需要的就是把 http.get() 换成 superagent.get()

面如桃花 2022-09-12 03:47:45

可以使用 Node8 的 util.promisify,或者 Bluebird 等把 Node 回调形式的函数改成 Promise 风格的函数,然后就可以使用 async/await 来写代码。

代码本身还是异步调用,只是写法看起来像是同步的。所以在写的时候还是要注意流程结构,尤其是在写循环的时候。代的代码太长,所以我写个小例子来说明

async function remoteCall() {
    // do something
}

list = [];  // 假设是很多数据


async function process() {
    // 这种写法必须要一个 remoteCall 完成之后才进行另一个
    for (let i = 0; i < list.length; i++) {
        await remoteCall();
    }

    doAfter();
}

async function process2() {
    // 这种写法没法 await
    list.forEach(function(t) {
        remoteCall();
    });
}

async function process3() {
    // 这种写法 doAfter 一早就会执行
    list.forEach(async function(t) {
        await remoteCall();
    });

    // 它可能会在 remoteCall() 之前
    doAfter();
}

async function process4() {
    // 这种写法必须要全部 remoteCall 成功才能进行到 doAfter
    // remoteCall返回的 promise 如果 reject 会抛异常
    var promises = list.map(t => remoteCall());
    await Promise.all(promises);
    doAfter();
}
梦在深巷 2022-09-12 03:47:45

generator
async
promise

默嘫て 2022-09-12 03:47:45

一般他人的业务逻辑都没什么耐性能看下去。

如楼上所言,Async/await 是建立在 Promise 基础上,如果你调用的第三方库的API接口没有返回promise对象的话,想用Async/await 你只能自己每一步都新建一个promise对象,这样其实写起来也很麻烦,当然如果能返回promise对象的话就很方便。

以下不用promise 用node核心模块event写的,供你参考一下:

const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();

myEmitter.on('step1', (m) => {
    //第一步
    //业务逻辑处理得到结果result1
        
    //判断是否触发下一步,如有需要将这步的结果传给下一步
    myEmitter.emit('step2', result1);
   
});
myEmitter.on('step2', (result1) => {
    //第二步
    //业务逻辑处理得到结果result2
        
    //判断是否触发下一步,如有需要将这步的结果传给下一步
    myEmitter.emit('step3', result2);
});
myEmitter.on('step3', (result2) => {
    //以此类推
});
myEmitter.emit('step1', urls);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文