nodejs簡(jiǎn)易爬蟲支持代理ip

nodejs 爬蟲支持代理IP 原創(chuàng)雕蟲小技

歡迎一起交流學(xué)習(xí)遍尺,廢話不說直接上代碼

const request = require("request")
const iconv = require('iconv-lite')
const cheerio = require("cheerio")
const _ = require("lodash")
const crypto = require('crypto')
class spider {

  constructor(config) {
    /**
     * @param {boolen} debug
     * @param {boolen} proxy
     * @param {string} proxy_ip
     * @param {array} temp_proxy
     * @param {array} headers
     */
    this.config = config
    this.doc_type = config.doc_type || 'html'
    this.headers = {
      'Host': this.config.Host
    }
    this.result_obj = {
      id: Number,
      title: String,
      link: String,
      desc: String,
      from: String
    }
    this.result_ext = {}
    this.debug = false
    this.proxy = false
    this.proxy_ip = null
    this.temp_proxy = []
    this.headers = []
    this.result_list = []
  }

  /**
   * @param {string} url
   * @param {string} method
   * @return {string} 
   */
  async so(url = "baidu.com", method = 'get') {
    let options = {
      method: method,
      url: url,
      timeout: 8000,
      headers: this.headers
    }
    console.log('開始爬取', url)
    if (this.temp_proxy.length && this.proxy) {
      if (!this.proxy_ip) {
        this.say('找出可用的代理ip')
        this.proxy_ip = await this.checkIp()
        console.log('獲取到了才到下一步')
      }
      if (!this.proxy_ip) {
        console.log('還沒有的話尊勿。估計(jì)是不行了')

        return false
      }
      options.proxy = this.proxy_ip
      return new Promise((resolve, reject) => {

        request(options, (error, response, body) => {
          try {

            if (error) throw error;

            if (/meta.*charset=gb2312/.test(body)) {
              body = iconv.decode(body, 'gbk');
            }
            if (this.proxy_ip) {
              this.say('這個(gè)IP果然牛逼!!!!', this.proxy_ip)
            }
            this.say('爬取完成了诡渴,丟出去html給下一個(gè)兄弟處理\n')

            if (this.doc_type == 'json') {
              resolve(this.handle(body[this.res_data]))
            } else {
              resolve(this.handle(body))
            }


          } catch (e) {
            this.say(options.proxy + '爬取失敗了寺晌,因?yàn)檫@原因代理ip換一下吧' + options.proxy, e.response)
            this.tempProxy = []
            this.proxyIp = null
            resolve(false)
          }

        });
      }).catch(e => {
        this.say(e);
        return false
      })
    } else {
      return new Promise((resolve, reject) => {

        request(options, (error, response, body) => {
          try {

            if (error) throw error;

            if (/meta.*charset=gb2312/.test(body)) {
              body = iconv.decode(body, 'gbk');
            }
            let result
            this.say('爬取完成了司倚,丟出去html給下一個(gè)兄弟處理\n')
            if (this.doc_type == 'json') {
              result = JSON.parse(body)
              // console.log(result)
              return resolve(this.handle(result[this.res_data]))
            } else {
              console.log(this.config.doc_type)
              resolve(this.handle(body))
            }

          } catch (e) {
            this.say('爬取失敗了究流,因?yàn)檫@原因', e.response)
            return reject(e);
          }

        });
      }).catch(e => {
        console.log(e)
      })
    }
  }
  /**
   * 獲取代理IP
   * @returns 
   */
  get_proxy_list() {
    let api_url = 'http://www.66ip.cn/mo.php?sxb=&tqsl=100&port=&export=&ktip=&sxa=&submit=%CC%E1++%C8%A1&textarea=http%3A%2F%2Fwww.66ip.cn%2F%3Fsxb%3D%26tqsl%3D100%26ports%255B%255D2%3D%26ktip%3D%26sxa%3D%26radio%3Dradio%26submit%3D%25CC%25E1%2B%2B%25C8%25A1';
    return new Promise((resolve, reject) => {
      let options = {
        method: 'GET',
        url: api_url,
        gzip: true,
        encoding: null,
        headers: {
          'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
          'Accept-Encoding': 'gzip, deflate',
          'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4',
          'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',
          'referer': 'http://www.66ip.cn/'
        }
      }

      request(options, (error, response, body) => {
        try {
          if (error) throw error;
          if (/meta.*charset=gb2312/.test(body)) {
            body = iconv.decode(body, 'gbk');
          }
          let ret = body.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,4}/g);
          resolve(ret);
        } catch (e) {
          return reject(e);
        }
      })
    })
  }
  /**
   * 設(shè)置代理ip
   */
  async set_proxy() {
    //   this.say(this.tempProxy)
    this.proxy = true
    this.say('好吧請(qǐng)稍等予跌,代理IP稍后奉上....')
    if (!this.tempProxy.length) {
      this.temp_proxy = await this.get_proxy_list()
      this.say('這次一用力獲取到了' + this.temp_proxy.length + '個(gè)IP,請(qǐng)測(cè)試温亲!')
    } else {
      this.say('原來的代理IP還有慢慢慢用')
      //其實(shí)該換新的了
      this.temp_proxy = []
    }
  }
  /**
   * 處理數(shù)據(jù)
   * @returns
   */
  handle(data) {
    if (this.doc_type == 'json') {
      let item = {}
      for (let i in data) {
        item = {}
        for (let it in this.result_obj) {
          //如果是數(shù)讀取到最后一個(gè)字符串
          if (_.isArray(this.result_obj[it])) {
            if (this.result_obj[it].length == 2) {
              item[it] = data[i][this.result_obj[it][0]][this.result_obj[it][1]]
            }
            if (this.result_obj[it].length == 3) {
              item[it] = data[i][this.result_obj[it][0]][this.result_obj[it][1]][this.result_obj[it][2]]
            }
          } else {
            item[it] = data[i][this.result_obj[it]]
          }

        }
        let new_item = Object.assign(item, this.result_ext)
        new_item['key'] = this.set_key(new_item.title)
        this.result_list[i] = new_item
      }
      if (this.debug) {
        this.say(data[0])
        this.say(this.result_list[0])
      }
    }

    if (this.doc_type == 'html') {
      let $ = cheerio.load(data)
      this.say('這是一個(gè)html文檔棚壁,具體是否可用要問一下下面的兄弟才知道')
      return $
    }
    return this.result_list
  }

  async test_proxy(proxy_ip) {
    if (proxyIp == undefined) {
      this.say('ip 已經(jīng)沒了,重新獲取')
      this.temp_proxy = []
      await this.set_proxy()
    }
    //測(cè)試這個(gè)代理IP 可用就設(shè)置
    return new Promise((resolve, reject) => {
      let target_options = {
        method: 'GET',
        url: 'http://ip.chinaz.com/getip.aspx',
        timeout: 8000
      };

      //這里修改一下栈虚,變成你要訪問的目標(biāo)網(wǎng)站
      this.say(`開始測(cè)試這個(gè)IP ${proxyIp}`);

      target_options.proxy = 'http://' + proxy_ip;
      request(target_options, (error, response, body) => {
        try {
          if (error) throw error;
          body = body.toString();
          // this.say(body);

          if (body.length < 100) {
            this.say(`兄弟這個(gè)可以拿去用==>> ${proxy_ip}`);
            resolve('http://' + proxy_ip)
          } else {
            this.say(`這個(gè)IP無效==>> ${proxy_ip}`);
            resolve(false)
          }

        } catch (e) {
          return reject(false);
        }

      });

    }).catch(e => {
      return e
    })
  }

  say(lang, lang1 = '') {
    if (this.debug) {
      console.log(lang, lang1)
    }
  }
  async check_ip() {
    //從第一個(gè)開始找找到合適的返回
    if (this.proxy) {
      for (let i in this.temp_proxy) {
        console.log(this.temp_proxy[i])
        let true_ip = await this.testProxy(this.temp_proxy[i])
        if (true_ip) {
          //記住這個(gè)索引
          return true_ip
        }
      }
    }
    return false
  }
  set_key(str) {
    if (!str) {
      return 'no str'
    }
    let rand = Math.ceil(Math.random() * 100)
    const md5 = crypto.createHash('md5')
    let password = md5.update(str + rand).digest('hex')
    return password
  }
}

/**
 * api 例子
 */

const api_spider = new spider({
  Host: 'juejin.com',
  doc_type: 'json'
})
api_spider.res_data = ['d']
api_spider.result_obj = {
  id: 'objectId',
  title: 'title',
  link: 'originalUrl',
  desc: 'content',
  like: 'collectionCount',
  comments: 'commentsCount',
  createdAt: 'createdAt'
}
api_spider.result_ext = {
  from: '掘金'
}

api_spider.so('https://search-merger-ms.juejin.im/v1/search?query=node&page=0&raw_result=false&src=web')
  .then((data) => {
    console.log(data[0])
  })



/**
 * html 例子
 */

const so = async function () {

  let start = new spider({
    Host: 'zzk.cnblogs.com',
    doc_type: 'html'
  })
  start.headers['Cookie'] = 'GA1.2.182722864.1520590357; UM_distinctid=162105121121049-0b14491452f4e4-32667b04-1aeaa0-162105121136f2; _gid=GA1.2.1004931403.1527083606; __utma=59123430.182722864.1520590357.1527094058.1527094058.1; __utmc=59123430; __utmz=59123430.1527094058.1.1.utmcsr=cnblogs.com|utmccn=(referral)|utmcmd=referral|utmcct=/; __utmt=1; __utmb=59123430.3.10.1527094058'
  let $ = await start.so('http://zzk.cnblogs.com/s?t=b&w=node')
  $('div.searchItem').each(function (i, elem) {
    let item = $(this).children('.searchItemTitle')
    start.result_list[i] = {
      id: i + 1,
      title: item.text(),
      link: item.children('a').attr('href'),
      desc: $(this).children('.searchCon').text().substr(0, 80),
      from: '博客園',
      createdAt: $(this).children('.searchItemInfo').children('.searchItemInfo-publishDate').text(),
      comments: $(this).children('.searchItemInfo').children('.searchItemInfo-comments').text(),
      views: $(this).children('.searchItemInfo').children('.searchItemInfo-views').text(),
      key: start.set_key(item.text())
    }
  })
  // console.log(start.resultList)
  return start.result_list
}
so().then((data)=>{
    console.log(data[0])
})
module.exports = spider
``
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市史隆,隨后出現(xiàn)的幾起案子魂务,更是在濱河造成了極大的恐慌,老刑警劉巖泌射,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粘姜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡熔酷,警方通過查閱死者的電腦和手機(jī)孤紧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拒秘,“玉大人号显,你說我怎么就攤上這事√删疲” “怎么了押蚤?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)羹应。 經(jīng)常有香客問我揽碘,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任雳刺,我火速辦了婚禮劫灶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掖桦。我一直安慰自己本昏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布滞详。 她就那樣靜靜地躺著凛俱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪料饥。 梳的紋絲不亂的頭發(fā)上蒲犬,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音岸啡,去河邊找鬼原叮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛巡蘸,可吹牛的內(nèi)容都是我干的奋隶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼悦荒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼唯欣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起搬味,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤境氢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后碰纬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萍聊,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年悦析,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寿桨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡强戴,死狀恐怖亭螟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酌泰,我是刑警寧澤媒佣,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站陵刹,受9級(jí)特大地震影響默伍,放射性物質(zhì)發(fā)生泄漏欢嘿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一也糊、第九天 我趴在偏房一處隱蔽的房頂上張望炼蹦。 院中可真熱鬧,春花似錦狸剃、人聲如沸掐隐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)虑省。三九已至,卻和暖如春僧凰,著一層夾襖步出監(jiān)牢的瞬間探颈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工训措, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伪节,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓绩鸣,卻偏偏與公主長(zhǎng)得像怀大,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呀闻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評(píng)論 25 707
  • 四月化借,九州春暖,到處是動(dòng)人心魄的十里花海捡多,江蘇興華油菜千垛花開金滿地屏鳍,貴州杜鵑漫山遍野色彩繽紛綿延百里,洛陽(yáng)國(guó)...
    亦木_18d0閱讀 453評(píng)論 2 3
  • 昨天看你發(fā)的微博局服,你說關(guān)于他的心情發(fā)了刪,刪了又發(fā)驳遵,會(huì)不會(huì)時(shí)間長(zhǎng)了就忘記了淫奔。那一刻,我又心疼你了堤结。每次看到你發(fā)關(guān)于...
    牛小喬閱讀 569評(píng)論 2 0
  • 紅塵里即使是紅顏也有責(zé)任唆迁,不單單是愛情,人生至少少些遺憾竞穷,我們彼此還是最初的模樣唐责。 紅顏素心有誰(shuí)知...
    空城錦閱讀 114評(píng)論 0 0
  • 真正的內(nèi)心強(qiáng)大,不用爭(zhēng)辯瘾带。不用高音鼠哥。不用東風(fēng)壓倒西風(fēng)。任其風(fēng)騷,我只管好我的人生我的世界足矣朴恳。不要再激動(dòng)好么抄罕?
    老魚的迷妹閱讀 249評(píng)論 0 0