天天看點

簡單的node爬蟲練手,循環中的異步轉同步簡單的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);
      

  

繼續閱讀