用node写爬虫系列三:代理IP池

之前的文章中我们实现了两个简单的爬虫,这两个爬虫的爬取频率都不会很高,如果我们现在有一个爬虫,他对目标网站的爬取频率非常高的时候,就会面临一个问题,IP被封,如果一旦IP被封我们需要换一个新的IP才能正确的爬取到我们需要的信息,或者利用代理IP,当然也可以通过重启路由器来获取一个新的IP,本文的重点是代理IP。

对于代理IP,获取的渠道很多,网上有很多平台都有出售代理IP,这些平台的代理IP其实很多也是收集于互联网,所以有些可能也不可用,而且价格还是不便宜的,对于我们一个个人的小爬虫来说,花高昂的价格去买代理IP着实有些不划算,怎么办呢?既然有些平台的代理IP也是收集于互联网,那么我们也可以在互联网上采集代理IP。

这里,我将代理IP的采集分为2个步骤。

1、去目标网站采集代理IP信息,这里我们的目标网站是西刺代理,我们准备爬取西刺代理的高匿代理IP,地址:https://www.xicidaili.com/nn/,在开始之前需要确保项目已经初始化好,这里主要还是使用request和cheerio库,具体的步骤这里就不再提了,不知道怎么弄的可以去看看我前面的我文章。
在项目依赖安装好后,我么先分析一下西刺代理页面的结构,

通过截图可以看出页面的布局还是使用的老土的table布局,而我们需要的信息都被包含在td中,在分析了页面的结构后,实现代码就比较简单了,如下:

function grab() {
  request({
    url: 'https://www.xicidaili.com/nn/'
  }, (err, response, body) => {
    if (err) {
      return;
    }

    const $ = cheerio.load(body);
    const $container = $('#ip_list');
    const $list = $container.find('tr');

    $list.each((index, item) => {
      const _this = $(item);
      const $td = _this.find('td');

      if ($td.length) {
        const ip = $td.eq(1).text();
        const port = $td.eq(2).text();
        const address = $td.eq(3).text();
        const type = $td.eq(5).text();
        const speed = Number($td.eq(6).find('.bar_inner').attr('style').replace(/[^\d]/g, ''));

        console.log(ip, port, address, type, speed);
      }
    });
  });
}

这里提取了ip,端口,位置,类型(http/https),还有就是西刺代理对于该代理IP的测速,如果我们直接按照西刺代理的测速结果去选取代理IP使用,会发现很多代理IP是不可用的,呆滞这个问题的原因很多,比如网络环境的差异,还有就是代理的存活时间问题,所以如果我们想要选取的代理IP都是可用的,我们需要自己实现一个测速方法,测速怎么测呢?用程序设置当前代理IP,并去请求一个公共网站,比如百度,看请求是都成功响应了,花费了多长时间,下面是代码:

function testSpeed(item) {
  const { ip, port, type }  = item;
  const protocol = type.toLowerCase();
  const proxyUrl = `${protocol}://${ip}:${port}`;
  const testUrl = `${protocol}://www.baidu.com`;
  const time = Date.now();
  request({
    url: testUrl,
    timeout: 2000,
    proxy: proxyUrl
  }, (err, response, body) => {
    if (err) {
      return;
    }
    const speed = Date.now() - time;
    console.log(item);
  });
}

这里设置超时时间为2s,2s内正确收到了响应,就判定此代理IP可用,当然你也可以根据实际情况修改这个超时的值。这样选取出来的代理IP在一段时间内肯定是可用的。

完成的代码:

const request = require('request');
const cheerio = require('cheerio');

grab();

function grab() {
  request({
    url: 'https://www.xicidaili.com/nn/'
  }, (err, response, body) => {
    if (err) {
      return;
    }

    const $ = cheerio.load(body);
    const $container = $('#ip_list');
    const $list = $container.find('tr');

    $list.each((index, item) => {
      const _this = $(item);
      const $td = _this.find('td');

      if ($td.length) {
        const ip = $td.eq(1).text();
        const port = $td.eq(2).text();
        const address = $td.eq(3).text();
        const type = $td.eq(5).text();
        const speed = Number($td.eq(6).find('.bar_inner').attr('style').replace(/[^\d]/g, ''));

        if (speed > 90) {
          testSpeed({
            ip,
            port,
            address,
            type,
            speed
          });
        }
      }
    });
  });
}

function testSpeed(item) {
  const { ip, port, type }  = item;
  const protocol = type.toLowerCase();
  const proxyUrl = `${protocol}://${ip}:${port}`;
  const testUrl = `${protocol}://www.baidu.com`;
  const time = Date.now();
  request({
    url: testUrl,
    timeout: 2000,
    proxy: proxyUrl
  }, (err, response, body) => {
    if (err) {
      return;
    }
    const speed = Date.now() - time;
    console.log(item);
  });
}

运行结果:

没使用代理的IP信息:

使用了代理IP后:

系列文章导航:

第1节: 用node写爬虫系列一:小说下载器

第2节: 用node写爬虫系列二:纪念币预约提醒

  • 支付宝二维码 支付宝
  • 微信二维码 微信
相关文章