Node.js meitulu圖片批量下載爬蟲1.051

原有1.05版程序沒有斷點續傳模式,如今在最近程序基礎上改寫一版1.051.html

//======================================================
// meitulu圖片批量下載爬蟲1.051
// 用最近的斷點續傳框架改寫原有1.05版程序
// 2017年11月21日
//======================================================

// 內置https模塊
var https=require("https");

// 內置http模塊
var http=require("http");

// 用於解析gzip網頁(ungzip,https獲得的網頁是用gzip進行壓縮的)
var zlib = require('zlib'); 

// 內置文件處理模塊,用於建立目錄和圖片文件
var fs=require('fs');

// 用於轉碼。非Utf8的網頁如gb2132會有亂碼問題,須要iconv將其轉碼
var iconv = require('iconv-lite');

// cheerio模塊,提供了相似jQuery的功能,用於從HTML code中查找圖片地址和下一頁
var cheerio = require("cheerio");

// 請求參數JSON。http和https都有使用
var options;

// request請求
var req;

// 圖片數組,找到的圖片地址會放到這裏
var pictures=[];

// 存放圖片的目錄
var folder="";

//--------------------------------------
// 爬取網頁,找圖片地址,再爬
// pageUrl sample:https://www.meitulu.com/item/12161.html
// pageUrl sample:
//--------------------------------------
function crawl(pageUrl){
    console.log("Current page="+pageUrl);

    // 獲得hostname和path
    var currUrl=pageUrl.replace("https://","");
    var pos=currUrl.indexOf("/");
    var hostname=currUrl.slice(0,pos);        
    var path=currUrl.slice(pos);    
    //console.log("hostname="+hostname);
    //console.log("path="+path);
    
    // 初始化options  
    options={
        hostname:hostname,
            port:443,
            path:path,// 子路徑
          method:'GET',        
    };

    req=https.request(options,function(resp){
        var html = [];

        resp.on("data", function(data) {
            html.push(data);
        })
        resp.on("end", function() {
            var buffer = Buffer.concat(html);
            //var body = buffer.toString();
            //console.log(body);

            zlib.gunzip(buffer, function(err, decoded) {
                if(err){
                    console.log("[findPageUrls]不能獲得頁面:"+batchPageUrl+"對應的html文本,錯誤是:"+err);
                    console.log(err);
                }else{
                    var body=decoded.toString();   
                    //console.log(body);
                    var $ = cheerio.load(body);        
                    var picCount=0;

                    // 找圖片放入數組
                    $(".content  img").each(function(index,element){
                        var picUrl=$(element).attr("src");
                        console.log(picUrl);

                        if(picUrl.indexOf('.jpg')!=-1){
                            pictures.push(picUrl); 
                            picCount++;
                        } 
                    })   
                    console.log("找到圖片"+picCount+"張.");                
                    
                    var nextPageUrl=null;
                    // 找下一頁
                    $("#pages a").each(function(index,element){
                        var text=$(element).text();
                        if(text.indexOf('下一頁')!=-1){
                            nextPageUrl=$(element).attr("href");  
                            nextPageUrl="https://www.meitulu.com"+nextPageUrl;
                            console.log("找到下一頁="+nextPageUrl);
                        }         
                    })

                    if(nextPageUrl==null || nextPageUrl==pageUrl){
                        console.log(pageUrl+"已是最後一頁了.\n");
                        saveFile(pageUrl,pictures);// 保存
                        download(pictures);
                    }else{
                        console.log("繼續下一頁");
                        crawl(nextPageUrl);
                    }               
                }    
            })
            
            
            
        }).on("error", function() {
            saveFile(pageUrl,pictures);// 保存
            console.log("crawl函數失敗,請進入斷點續傳模式繼續進行");
        })
    });

    // 超時處理
    req.setTimeout(7500,function(){
        req.abort();
    });

    // 出錯處理
    req.on('error',function(err){
        console.log('請求發生錯誤'+err);  
        saveFile(pageUrl,pictures);// 保存
        console.log("crawl函數失敗,請進入斷點續傳模式繼續進行");
    });

    // 請求結束
    req.end();
}

//--------------------------------------
// 下載圖片
//--------------------------------------
function download(pictures){

    var total=0;
    total=pictures.length;
    console.log("總計有"+total+"張圖片將被下載.");
    appendToLogfile(folder,"總計有"+total+"張圖片將被下載.\n");
    for(var i=0;i<pictures.length;i++){
        var picUrl=pictures[i];
        downloadPic(picUrl,folder);
    }
}

//--------------------------------------
// 寫log文件
//--------------------------------------
function appendToLogfile(folder,text){
    fs.appendFile('./'+folder+'/log.txt', text, function (err) {
        if(err){
            console.log("不能書寫log文件");
            console.log(err);
        }
    });
}

//--------------------------------------
// 取得當前時間
//--------------------------------------
function getNowFormatDate() {
    var date = new Date();
    var seperator1 = "-";
    var seperator2 = "_";
    var month = date.getMonth() + 1;
    var strDate = date.getDate();
    if (month >= 1 && month <= 9) {
        month = "0" + month;
    }
    if (strDate >= 0 && strDate <= 9) {
        strDate = "0" + strDate;
    }
    var currentdate =date.getFullYear() + seperator1 + month + seperator1 + strDate
            + " " + date.getHours() + seperator2 + date.getMinutes()
            + seperator2 + date.getSeconds();
    return currentdate;
}

//--------------------------------------
// 下載單張圖片
// picUrl sample:http://mtl.ttsqgs.com/images/img/12161/41.jpg
//--------------------------------------
function downloadPic(picUrl,folder){
    console.log("圖片:"+picUrl+"下載開始");

    // 獲得hostname,path和port
    var currUrl=picUrl.replace("http://","");
    var pos=currUrl.indexOf("/");
    var hostname=currUrl.slice(0,pos);        
    var path=currUrl.slice(pos);

    // 有端口加端口,沒有端口默認80
    var port=80;

    //console.log("hostname="+hostname);
    //console.log("path="+path);
    //console.log("port="+port);

    var picName=currUrl.slice(currUrl.lastIndexOf("/"));
    
    // 初始化options  
    options={
        hostname:hostname,
            port:port,
            path:path,
          method:'GET',
        /* headers:{
            'Referer':'https://www.meitulu.com',
          },*/
    };

    req=http.request(options,function(resp){
        var imgData = "";
        resp.setEncoding("binary"); 

        resp.on('data',function(chunk){
            imgData+=chunk;            
        });

        resp.on('end',function(){        

            // 建立文件
            var fileName="./"+folder+picName;
            fs.writeFile(fileName, imgData, "binary", function(err){
                if(err){
                    console.log("[downloadPic]文件   "+fileName+"  下載失敗.");
                    console.log(err);
                    appendToLogfile(folder,"文件  "+picUrl+"  下載失敗.\n");
                }else{
                    appendToLogfile(folder,"文件  "+picUrl+"  下載成功.\n");
                    console.log("文件"+fileName+"下載成功");
                }
            });    
        });
    });

    // 超時處理
    req.setTimeout(7500,function(){
        req.abort();
    });

    // 出錯處理
    req.on('error',function(err){
        if(err){
            console.log('[downloadPic]文件   '+picUrl+"  下載失敗,"+'由於'+err);
            appendToLogfile(folder,"文件"+picUrl+"下載失敗.\n");
        }
    });

    // 請求結束
    req.end();
}

//--------------------------------------
// 程序入口 
//--------------------------------------
function getInput(){
    process.stdin.resume();    
    process.stdout.write("\033[33m 新建模式輸入第一頁URL,斷點續傳模式輸入0,請輸入: \033[39m");// 草黃色
    process.stdin.setEncoding('utf8');
    
    process.stdin.on('data',function(text){
        var input=text.trim();
        process.stdin.end();// 退出輸入狀態    

        if(text.trim()=='0'){
            process.stdout.write("\033[36m 進入斷點續傳模式. \033[39m");    // 藍綠色

            // Read File
            fs.readFile('./save.dat','utf8',function(err,data){
                if(err){
                    console.log('讀取文件save.dat失敗,由於'+err);
                }else{
                    //console.log(data);
                    var obj=JSON.parse(data);

                    pictures=obj.pictures;
                    console.log('提取圖片'+pictures.length+'張');

                    folder=obj.folder;

                    // 建立目錄
                    fs.mkdir('./'+folder,function(err){
                        if(err){
                            console.log("目錄"+folder+"已經存在");
                        }
                    });

                    crawl(obj.url);        
                }
            });
            
            // Resume crawl
        }else{
            process.stdout.write("\033[35m 進入新建模式. \033[039m");    //紫色

            folder='pictures('+getNowFormatDate()+")";
            // 建立目錄
            fs.mkdir('./'+folder,function(err){
                if(err){
                    console.log("目錄"+folder+"已經存在");
                }
            });

            crawl(input);            
        }
    });    
}

//--------------------------------------
// 將爬行中信息存入數據文件
//--------------------------------------
function saveFile(url,pictures){
    var obj=new Object;
    obj.url=url;
    obj.pictures=pictures;
    obj.folder=folder;
    var text=JSON.stringify(obj);

    fs.writeFile('./save.dat',text,function(err){
        if(err){
            console.log('寫入文件save.dat失敗,由於'+err);
        }
    });
}

// 調用getInput函數,程序開始
getInput();

2017年11月21日10:19:20數組

相關文章
相關標籤/搜索