使用nodeJs从汽车之家爬取汽车外观图片

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_33594380/article/details/89023389

最近在做毕业设计的时候需要用到大量的汽车图片作为数据集进行训练,一开始尝试从两个渠道获取图片:

一是去找ImageNet 中的汽车分类的图片,结果发现这里面的汽车图片总量比较少,且不同车种差异较大(我只想要一般的私家车图片)。还有一个问题是访问ImageNet 需要外网,从别人那里接了一台能翻墙的服务器,选了合适的种类,折腾半天,好不容易下载下来,再清洗一下,能用的只剩五千张左右;

二是从百度图片上以汽车为关键词爬取图片,这个方案的问题在于爬到的图片各式各样,虽然关键词是汽车,但是什么图片都可能爬得到,而且图片重复率高,质量差,清洗的成本非常高。

于是开始思考从汽车之家上爬取图片,去看了一下,汽车之家上的图片质量都很高,而且数量非常多,就用nodeJs 写个脚本开始爬。本文主要记录了在写脚本过程中的思考和尝试过程,并给出了最终的代码。为了使整个过程看起来逻辑更清晰,把这个完整的任务分成图片Url 获取图片获取两部分来实现。Url 获取的结果是一个或若干个写满了图片Url 地址的txt 文件(在线传输成本低),图片获取的结果是根据Url 文件中的内容爬到的图片。

1、图片Url 获取

所有的图片Url 都是从汽车之家的网页上拿到的,所以首先需要对包含汽车外观图片的页面进行分析,以便搞清楚我们要怎么拿到网页中图片的Url,这里以如下页面为例进行分析:

https://car.autohome.com.cn/pic/series/2288-1.html#pvareaid=2042220

页面图片如下所示:

从图中可以看到series 系列网页中所有车身外观的图片都位于具有.uibox-con.carpic-list03 类名的一个div 下,其具体位置可以表示如下:

div.uibox-con.carpic-list03 ul li a img

从图中可以看到的另个一问题是img 的src 属性有点异常,不是以http 开头的,而是以// 开头,因此拿到这个值之后,需要将// 替换为http://.

下面一个问题是如果该页面有很多张图片,需要翻页怎么办?针对这个问题,同样可以分两步来解决,一是弄清楚有无翻页,有翻页的话一共有多少页;二是获取到每页的Url。怎么样得到这两个问题的答案呢,操作步骤跟上面就类似,要去看页面两个地方是怎么写的:

 

分析完了,从技术层面的角度来讲,nodeJs 中的包https 和cheerio 分别可以帮我们请求到页面和解析页面的DOM 结构,具体细节不再进行描述,获取图片Url 的代码如下所示:

var https = require('https');
var request = require('request');
var cheerio = require('cheerio');
var fs = require('fs'); //用来操作文件
var urlBase = 'https://car.autohome.com.cn/pic/series/'
var url = 'https://car.autohome.com.cn/pic/series/2288-1-p1.html' //定义要爬的页面

saveDir = ''

function getAllHtml(urlBase, startIndex, maxIndex, txt) {
  var nowIndex = startIndex;
  saveDir = txt

  var intId = setInterval(function() {
    getHtmlAllPagesImg(urlBase, nowIndex)
    nowIndex++;
    if(nowIndex > maxIndex) {
      clearInterval(intId);
    }
  }, 4000)
}

// 获取一个html 下所有分页的img
function getHtmlAllPagesImg(url, nowIndex) {

  fullUrl = url + nowIndex + '-1' + '.html';

  console.log(fullUrl);

  https.get(fullUrl, function(res){
    var html = '',
        titles = [];

    res.setEncoding('utf-8') //防止中文乱码
    res.on('data', function(chunk){
      html += chunk;    //监听data事件 每次取一块数据
    })
    res.on('end', function() {
      var allStr = '';
      var $ = cheerio.load(html);  //获取数据完成后,解析html

      // console.log(nowIndex + ' box length: ' + $('.uibox-con.carpic-list03').length);

      // 如果存在车身外观图片则继续进行
      if($('.uibox-con.carpic-list03').length > 0) {
        console.log('Series_' + nowIndex + ' 有车身图片box,哈哈哈...')

        // 如果需要翻页的话
        if($('.pagecont .page').length > 0) {
          // 当前显示的图片总页数
          var pageNum = $('.pagecont .page').children().length - 2;
          // console.log(pageNum)
          if(pageNum > 0) {
            console.log('Series_' + nowIndex + '需要翻页...')
            for (var i = 1; i <= pageNum; i++) {
              getSinglePageImg(url + nowIndex + '-1' + '-p' + i + '.html');
            }
          } else {
            getSinglePageImg(fullUrl);
          }
        } else {
          getSinglePageImg(fullUrl);
        }
      }
      else {
        console.log('Series_' + nowIndex + '无车身图片box!');
        console.log('================')
        return;
      }
    })
  })
}

// 获取某个html 下单页的图片src
function getSinglePageImg(url) {

  https.get(url, function(res){
    var html = '',
        titles = [];

    res.setEncoding('utf-8') //防止中文乱码
    res.on('data', function(chunk){
      html += chunk;    //监听data事件 每次取一块数据
    })
    res.on('end', function() {
      var nowImgStr = '';
      var $ = cheerio.load(html);  //获取数据完成后,解析html

      // console.log(url + '......' + $('.uibox-con.carpic-list03 ul li').length)
      $('.uibox-con.carpic-list03 ul li').each(function(index, val) {
        nowImgStr += 'http:' + $(this).children('a').children('img').attr('src') + '\n'
      });

      if (nowImgStr) {
        console.log(url + ' 成功获取到图片...');
        fs.writeFile(saveDir, nowImgStr, { 'flag': 'a' }, function(err, data) {
          if(err) {
            console.log('Failure.');
          } else {
            console.log('Saved.')
          }
          console.log('****************************')
        })
      } else {
        console.log(url + ' 没有获取到图片!');
        console.log('----------------------')
      }

    })
  })
}

module.exports = { getAllHtml }

2、图片获取

拿到图片的Url 之后,第二步就很简单了,只要遍历一下txt 文件中的Url,挨个获取,就能拿到所有的相关图片,代码如下:

var fs = require('fs'); 
const { runCmd } = require('./cmd')

function getImg(txt, dir) {
  fs.readFile(txt, 'utf-8', function (err, data) {
    var imgList = data.split('\n');
    var imgNum = imgList.length;

    for(var i = 1; i <= imgNum; i++) {
      if(imgList[i]) {
        runCmd('start img_' + i,
          'cd ' + dir + ' && wget ' + imgList[i] + ' -O img_' + i + '.jpg' ,
          'done img_' + i,
          i * 10)
      }
    }
  })
}

module.exports = { getImg };


此处的runCmd 实际上是用node-cmd 模块写的一个简单的工具类,用来执行shell 命令,完整代码可以去github 看。

3、Github

Github 地址:https://github.com/TerminatorSd/carCrawler

在网上找一些简单工具或者算法的开源实现时经常遇到的一个问题是,由于环境或者其他各种问题,代码根本跑步起来(大部分情况是因为很多人都不写readme),然后在博客上留言或者github 上提issue 之后都没有人回复。这种情况时常让我觉得非常苦恼,所以我在github 上放东西的时候一般都会写readme。代码或者内容有问题的话,直接博客留言,或者提issue,也基本可以回复,因为每天都要上博客和github,希望这样能够让信息流通的快一点。

展开阅读全文

没有更多推荐了,返回首页