记录自己使用node superagent插件爬取数据历程

1,250 阅读2分钟

背景:在node koa框架下进行数据爬取

第一步:上来直接通过插件直接爬取

const options = {
  "Referer": win007 + "/cn/League/" + Season + "/" + LeagueId + ".html",//目标页面地址
  "Content-Type": "application/json",
  "User-Agent": userAgents[parseInt(Math.random() * userAgents.length)],//制造userAgents 信息,需自己封装方法
  "Accept": "application/javascript"
}
var result =  await superagent
              .get(url) //请求地址
              .set(options) // 设置请求头
              .proxy(proxyIp) //设置动态Ip
              .timeout({ response: 2000, deadline: 60000 }) //超时响应
              .buffer(true)

这样就实现一个简易的抓取但是在项目运行中大量文件抓取发现失败率一直很高,所以添加错误重试机制,

第二部添加重试机制

for(var i = 0; i < 4; i++){
  try{
    var result =  await superagent.get(url).set(options).proxy(proxyIp).timeout({ response: 2000, deadline: 60000 }).buffer(true)
    rs = {
      code: '0',
      data: result,
    }                            
    break;
  } catch(err) {
    utils.delay(1000)
    if(i == 3){
      rs = this.failRequest();
    }
  }
}  

通过for循环与try catch结合实现,避免多次回调,在for循环中发现请求成功则break退出循环,否则根据设置的最大循环次数进行重试,为避免同一时间节点大批量爬取页面在失败时,进行一定时间的延迟来模拟人的点击,也就是上面代码中的delay函数

    //延迟,在一定范围内进行延迟
    delay(ms) {
        var interval = Math.random() * ms ;
        return new Promise(resolve => setTimeout(resolve, interval))
    }

前两步完成代码如下,部分函数需自己封装

  async getTeamData(url, LeagueId, Season){    
    let rs = {};
    console.log(LeagueId,Season);
    const win007 = "http://zq.win007.com";
    const options = {
      "Referer": win007 + "/cn/League/" + Season + "/" + LeagueId + ".html",
      "Content-Type": "application/json",
      "User-Agent": userAgents[parseInt(Math.random() * userAgents.length)],
      "Accept": "application/javascript"
    }
    // HTTP, HTTPS, or SOCKS proxy to use
    const proxyIp = await utils.getProxyIp();
    for(var i = 0; i < 4; i++){
      try{
        var result =  await superagent.get(url).set(options).proxy(proxyIp).timeout({ response: 2000, deadline: 60000 }).buffer(true)
        rs = {
          code: '0',
          data: result,
        }                            
        break;
      } catch(err) {
        utils.delay(1000)
        if(i == 3){
          rs = this.failRequest();//生成失败时的返回值{code:-1, msg:'error'}
        }
      }
    }
    return rs
  }

第三步 对数据进行处理

接下来的就是对成功的数据进行处理,

  • 如果是爬取html页面则可以借助cheerio插件对数据进行格式化,这样$就是带有需要信息的对象,可以功过JQ的方法进行获取其中的信息
var $ = cheerio.load(sub_str);// sub_str为爬取的html数据
  • 如果爬取的是JS页面,则可以通过 eval() 方法获取变异后数据。

第四步 定时更新数据

这时就遇到一个比较坑的问题,在foreach循环中无法用await,具体原因大家自己百度就好,故而改用for循环,定时任务用到node-schedule 插件,

//定时任务
function scheduleCronstyle () {
  var schedule_option = {
    hour: 1,
    minute: 1,
    second: 1,
    dayOfWeek:[1, 3, 5, 6] 
  }
  //每周1,3,5,6的1点1时1分 执行一次:
  schedule.scheduleJob(schedule_option ,()=>{
    get_newest_data()
  }); 
}

第一次这样分享,可能逻辑不是特别清晰,欢迎大家留言,一起探讨