扯淡

本文来源于视频 从零开始写一个 nodejs 爬虫

逻辑

  1. 声明起始 Url
  2. 获取页面数组
  3. 遍历页面数组,在每个页面获取所有图链数组
  4. 遍历图链数组,下载图片

关键 API

页面请求响应

request 函数可以发出网址,接收响应字符串

request(网址, function(err, response) {
  console.log(response)  // response 就是响应字符串
})
下载图片

注:图片地址如果包含文件夹,必须是存在的文件夹
request.pipe(fs).on

注 2:此处最好分离 request(网络地址),以监听错误

request(网络地址).pipe(fs.createWriteStream(图片地址))
.on('close', function() {
  console.log(`${name} 下载成功`)
  callback()
})
cheerio 解析页面元素

cheerio 可以把响应字符串用 jQuery 解读

var $ = cheerio.load(response.body)  // request 函数中的 response.body 是字符串
var imgs = $('img')
imgs.each(function() {
  console.log($(this).attr('src'))
})
async.mapLimit

遍历请求页面,图片下载
async(目标数组, 最大请求并发数, 回调)
这个函数类似于 for, forEach,用它的目的是控制请求并发数,以免被限制 ip

async.mapLimit(pageUrl, 5, function(page, cb) {
  request(page, function(err, response) {})
})
async.mapLimit(imgArr, 5, function(src, callback) {
  request(src).pipe(...).on('close', ...)
})

细节

图片网址解析图片名

注1:正则表达式的值不是字符串
注2:字符串.match(reg) 的结果如果不存在,值为 null(不为[])

function getName(src) {
  var reg = /(\w)+.(jpg|png|gif)/g
  var result = src.match(reg)
  if(result) {
    return result[0]
  }

  return parseInt(Math.random() * 100000000) + '.jpg'
}

代码

var fs = require('fs')
var cheerio = require('cheerio')
var request = require('request')
var async = require('async')

var startUrl = 'https://danbooru.donmai.us/posts'

function getName(src) {
    var reg = /(\w)+.(jpg|gif|png)/g
    var result = src.match(reg)
    if(result) {
        return result[0]
    }

    return parseInt(Math.random() * 1000) + '.jpg'
}

request(startUrl, function(err, response) {
    if(err) {
        console.log(err)
    }

    var $ = cheerio.load(response.body)

    var amount = parseInt($('.more').prev().text() || $('.arrow').prev().text())

    var pageUrl = []

    for(var i=1; i<=amount; i++) {
        pageUrl.push(`https://danbooru.donmai.us/posts?page=${i}`)
    }

    async.mapLimit(pageUrl, 5, function(page, callback) {
        request(page, function(err, response) {
            if(err) {
                console.log(err)
            }

            var $ = cheerio.load(response.body)
            var imgs = $('picture >img')

            var imgArr = []

            imgs.each(function() {
                imgArr.push($(this).attr('src'))
            })

            async.mapLimit(imgArr, 5, function(src, callback) {

                var name = getName(src)
                request(src).pipe(fs.createWriteStream('./img/' + name))
                .on('close', function() {
                    console.log(name + '下载成功')
                    callback()
                })
            })

            callback()
        })
    })
})

补充

爬取 anime-share 的图片时遇到了 unhandled xx 的问题

解决办法在这里找到:stackoverflow

更改原来的图片下载语句,分离出 request() 语句监听错误即可

var readStream = request(src)
readStream.on('error', function(err) {
    console.log(src, err)
})

readStream.pipe(fs.createWriteStream('./hentai/' + name))
.on('close', function() {
    console.log(name + '下载成功')
    callback()
})