当前位置: 首页 > news >正文

简单的node爬虫练手,循环中的异步转同步

简单的node爬虫练手,循环中的异步转同步

 

转载:https://blog.csdn.net/qq_24504525/article/details/77856989

 

看到网上一些基于node做的爬虫项目,自己也想写一下练手,正好同事需要各省市的信息

一、开发环境搭建

  1.  node 安装最新版 后面会用到async、await 
  2.  webstrom编辑器
  3.  新建reptitle文件夹 --> npm init (初始化工程)


二、爬取页面分析

 

  1. 入口 ,获取该页面所有的省市,记录下省市名称,及html地址
  2. 查询省市下面的市区
  3. 依次爬取,略。。

 

三、关键代码

1. 代码分析 
  •  cheerio包用于解析页面中的html,用法同jquery
  •  fs 生成文件
  •  http 发起get请求页面
  • async 用于解决异步
  • iconv、bufferhelper 用于解析中文乱码
2.  因为http发起的请求是异步,循环中的异步函数不能按照想要的既定顺序执行,所以我用es6、7中的promise async await 将异步函数转化成同步
3. 代码
 
let http = require("http");
let cheerio = require("cheerio"); 
let fs = require("fs");
let async = require("async");
let iconv = require('iconv-lite');
let BufferHelper = require('bufferhelper');
let initUrl = "http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/index.html";
let url = "http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/";//初始url
let dataSource = [];
/**
 * @method 生成文件
 * @param fileName
 * @param text
 */
const createTxt =(fileName,text) =>{
  return new Promise((resolve,reject)=>{
    fs.appendFile("spider/data/"+fileName+".txt",text,"utf-8",function(err){
      if(err){
        console.log(err)
      }else{
        resolve(true)
      }
    })
  })
};
/**
 * @method promise封装http请求
 * @returns {Promise.<void>}
 */
const httpGet = (url) =>{
  return new Promise((resolve,reject)=>{
    http.get(url,(res)=>{
      let buffer = new BufferHelper();
      res.on("data",(data)=>{
        buffer.concat(data);
      });
      res.on("end",()=>{
        let buf = buffer.toBuffer();
        let html = iconv.decode(buf,'GBK');
        let $ = cheerio.load(html); //采用cheerio解析页面
        resolve($);
      })
    })
  })
};
/**
 * @method 获取所有省
 * @param initUrl
 * @returns {Promise.<void>}
 */
async function getProvince(initUrl) {
  console.time("计时器");
  let subUrlArray = [];
  await httpGet(initUrl).then(($)=>{
    let provincetds = $(".provincetr td");
    provincetds.each((i)=> {
      let subUrl = provincetds.eq(i).find("a").attr("href");
      let name = provincetds.eq(i).find("a").text();
      dataSource.push({
        province: name,
        cityArray: []
      });
      //将函数参数放入队列
      subUrlArray.push({
        url: url,
        subUrl: subUrl,
        j: i
      });
    })
  });
  //await的上下文async
 // console.log(subUrlArray)
  for(let i=0;i<subUrlArray.length;i++){
    console.log("进入"+dataSource[i].province);
    await startRequest(url,subUrlArray[i].subUrl,i);
    //根据省生成文件
    let fileName = dataSource[i].province;
    let text = JSON.stringify(dataSource[i].cityArray);
    await createTxt(fileName,text);
  }
 
  console.timeEnd("计时器");
}
/**
 * @method 根据省查询该省下面的所有市
 * @param url
 * @param subUrl
 * @param i
 * @returns {Promise}
 */
async function startRequest(url,subUrl,i){
  let subUrlArray = [];
  await httpGet(url+subUrl).then(($)=>{
    let citytr = $(".citytr");
    //保存省市和地址
    citytr.each(function(index){
      let cityNum = $(this).find("td").eq(0).find("a").text();
      let name = $(this).find("td").eq(1).find("a").text();
      let cityUrl = $(this).find("td").eq(0).find("a").attr("href");
      dataSource[i]["cityArray"].push({
        city : name,
        cityNum : cityNum,
        countryArray : []
      });
      //将函数参数放入队列
      let countryUrl = url.replace(/.html/,"");
      subUrlArray.push({
        countryUrl: countryUrl,
        subUrl: cityUrl,
      });
    });
  });
  for(let j=0;j<subUrlArray.length;j++){
    let url = subUrlArray[j].countryUrl;
    let subUrl = subUrlArray[j].subUrl;
    await startCounty(url,subUrl,i,j);
  }
}
/**
 * @method 查询市区下面的区县
 * @param url
 * @param subUrl
 * @param proIndex
 * @param cityIndex
 * @returns {Promise.<void>}
 */
async function startCounty(url,subUrl,proIndex,cityIndex){
  let subUrlArray = [];
  //console.log("进入区县",url+subUrl)
  await httpGet(url+subUrl).then(($)=>{
    let countytr = $(".countytr");
    //保存区县和地址
 
    countytr.each(function(i){
      //console.log("区县",i)
      let countyNum = $(this).find("td").eq(0).find("a").text();
      let name = $(this).find("td").eq(1).find("a").text();
      let areaurl = $(this).find("td").eq(0).find("a").attr("href");
      dataSource[proIndex]["cityArray"][cityIndex]["countryArray"].push({
        county : name,
        countyNum : countyNum,
        areaArray : [],
      });
      let newUrl = subUrl.split(/\//)[0]+"/"+areaurl;
      if(areaurl){
        subUrlArray.push({
          newUrl : newUrl,
          index : i
        });
      }
    });
  });
  for(let i=0;i<subUrlArray.length;i++){
    let data = subUrlArray[i];
    await getTree(url,data.newUrl,proIndex,cityIndex,data.index);
  }
}
/**
 * @method 根据区县爬取街道
 * @param url
 * @param subUrl
 * @param proIndex
 * @param cityIndex
 * @param countyIndex
 * @returns {Promise.<void>}
 */
async function getTree(url,subUrl,proIndex,cityIndex,countyIndex){
  let subUrlArray = [];
  //console.log("街道",url+subUrl)
  await httpGet(url+subUrl).then(($)=>{
    let towntr = $(".towntr");
    //console.log("towntr",towntr.length)
    //保存区县和地址
    towntr.each(function(i){
      let  countyNum = $(this).find("td").eq(0).find("a").text();
      let name = $(this).find("td").eq(1).find("a").text();
      let newurl = $(this).find("td").eq(0).find("a").attr("href");
      dataSource[proIndex]["cityArray"][cityIndex]["countryArray"][countyIndex]["areaArray"].push({
        area : name,
        areaNum : countyNum,
        jwhArray : [],
      });
      let reUrl = subUrl.split(/\//)[0]+"/"+subUrl.split(/\//)[1]+"/"+newurl;
      if(newurl){
        subUrlArray.push({
          reUrl : reUrl,
          index : i
        })
      }
    });
  });
  for(let i=0;i<subUrlArray.length;i++){
    let data = subUrlArray[i];
    await getJwh(url,data.reUrl,proIndex,cityIndex,countyIndex,data.index);
  }
}
/**
 * @method 根据街道爬取办事处
 * @param url
 * @param subUrl
 * @param proIndex
 * @param cityIndex
 * @param countyIndex
 * @param areaIndex
 * @returns {Promise.<void>}
 */
async function getJwh(url,subUrl,proIndex,cityIndex,countyIndex,areaIndex){
  let subUrlArray = [];
  //console.log("getJwh",url+subUrl);
  await httpGet(url+subUrl).then(($)=>{
    let villagetr = $(".villagetr");
    //console.log(villagetr.length);
    villagetr.each(function(i){
      let  countyNum = $(this).find("td").eq(0).text();
      let name = $(this).find("td").eq(2).text();
      dataSource[proIndex]["cityArray"][cityIndex]["countryArray"][countyIndex]["areaArray"][areaIndex]["jwhArray"].push({
        jwh : name,
        jwhNum : countyNum,
      });
      //console.log("name",name)
    });
  })
}
 
getProvince(initUrl);

  

 

相关文章:

  • 活跃银行×××之二:创新的恶意软件Osiris
  • “数据中心运维管理VIP学习群”问题汇总(一)
  • Oracle 数据文件及其管理
  • Oh!MongoDB日志从文本穿越成了图片?咋整!
  • AtCoder Grant Contest 10.F 博弈
  • Centos7安装Openresty和orange
  • 在ubuntu18实践
  • 非常全面的vim配置文件
  • EXCHANGE DAG环境如何排除ISCSI使用的网络适配器
  • PHP 的 SAPI 是个什么东西
  • 爬虫框架Scrapy入门——爬取acg12某页面
  • Seafile网盘专业版部署(debian8、ubuntu系统)
  • 七分钟理解 Java 的反射 API
  • 缓存与数据库的数据一致性方案介绍
  • Qt之QPropertyAnimationQEasingCurve
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • CentOS 7 防火墙操作
  • css选择器
  • JavaScript新鲜事·第5期
  • PHP的类修饰符与访问修饰符
  • 基于webpack 的 vue 多页架构
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 前端之React实战:创建跨平台的项目架构
  • 强力优化Rancher k8s中国区的使用体验
  • 区块链分支循环
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 用mpvue开发微信小程序
  • 容器镜像
  • (7)STL算法之交换赋值
  • (LeetCode C++)盛最多水的容器
  • (Redis使用系列) SpirngBoot中关于Redis的值的各种方式的存储与取出 三
  • (TOJ2804)Even? Odd?
  • (zt)最盛行的警世狂言(爆笑)
  • (二)学习JVM —— 垃圾回收机制
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (一)UDP基本编程步骤
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (转)EXC_BREAKPOINT僵尸错误
  • *上位机的定义
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器
  • .NET 设计模式—适配器模式(Adapter Pattern)
  • .NET成年了,然后呢?
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • .NET使用存储过程实现对数据库的增删改查
  • .net通用权限框架B/S (三)--MODEL层(2)
  • .net下简单快捷的数值高低位切换
  • @RequestMapping-占位符映射
  • [ C++ ] template 模板进阶 (特化,分离编译)
  • [ 英语 ] 马斯克抱水槽“入主”推特总部中那句 Let that sink in 到底是什么梗?
  • [ 云计算 | AWS ] AI 编程助手新势力 Amazon CodeWhisperer:优势功能及实用技巧
  • [1525]字符统计2 (哈希)SDUT
  • [bzoj1901]: Zju2112 Dynamic Rankings