Rumah > Artikel > hujung hadapan web > Tutorial lanjutan perangkak nodejs konkurensi tak segerak control_node.js
Saya menulis crawler kecil sebelum ini nampaknya sangat tidak sempurna sekarang penghujung jawapan, klik Muat Lagi dan jawapan akan dimuatkan semula Oleh itu, jika anda terus menghantar pautan permintaan kepada soalan, halaman yang diperolehi tidak akan lengkap. Selain itu, apabila kami memuat turun gambar dengan menghantar pautan, kami memuat turunnya satu persatu Jika terlalu banyak gambar, ia akan tetap dimuat turun selepas anda selesai tidur Lebih-lebih lagi, crawler yang kami tulis menggunakan nodejs tidak memuat turun gambar itu satu per satu satu. Ia sangat membazir sehingga ciri tak segerak dan serentak yang paling berkuasa bagi nodejs tidak digunakan.
Pemikiran
Perangkak kali ini ialah versi yang dinaik taraf daripada yang terakhir Namun, walaupun yang terakhir adalah mudah, ia sangat sesuai untuk dipelajari oleh orang baru. Kod perangkak kali ini boleh didapati di github saya => NodeSpider.
Idea keseluruhan perangkak adalah ini: Pada mulanya, kami merangkak sebahagian daripada data halaman melalui pautan soalan permintaan, dan kemudian kami mensimulasikan permintaan ajax dalam kod untuk memintas data halaman yang tinggal. Sudah tentu, ia juga boleh dilakukan secara tak segerak di sini Untuk mencapai konkurensi, untuk kawalan proses tak segerak berskala kecil, anda boleh menggunakan modul ini => eventproxy, tetapi saya tidak menggunakannya di sini! Kami memintas pautan semua gambar dengan menganalisis halaman yang diperoleh, dan kemudian melaksanakan muat turun kelompok gambar ini melalui serentak tak segerak.
Sangat mudah untuk menangkap data awal halaman, jadi saya tidak akan menerangkan banyak perkara di sini
/*获取首屏所有图片链接*/ var getInitUrlList=function(){ request.get("https://www.zhihu.com/question/") .end(function(err,res){ if(err){ console.log(err); }else{ var $=cheerio.load(res.text); var answerList=$(".zm-item-answer"); answerList.map(function(i,answer){ var images=$(answer).find('.zm-item-rich-text img'); images.map(function(i,image){ photos.push($(image).attr("src")); }); }); console.log("已成功抓取"+photos.length+"张图片的链接"); getIAjaxUrlList(); } }); }
Simulasikan permintaan ajax untuk mendapatkan halaman lengkap
Langkah seterusnya ialah cara mensimulasikan permintaan ajax yang dikeluarkan apabila mengklik untuk memuatkan lagi, pergi ke Zhihu dan lihat!
Dengan maklumat ini, anda boleh mensimulasikan penghantaran permintaan yang sama untuk mendapatkan data ini.
/*每隔毫秒模拟发送ajax请求,并获取请求结果中所有的图片链接*/ var getIAjaxUrlList=function(offset){ request.post("https://www.zhihu.com/node/QuestionAnswerListV") .set(config) .send("method=next¶ms=%B%url_token%%A%C%pagesize%%A%C%offset%%A" +offset+ "%D&_xsrf=adfdeee") .end(function(err,res){ if(err){ console.log(err); }else{ var response=JSON.parse(res.text);/*想用json的话对json序列化即可,提交json的话需要对json进行反序列化*/ if(response.msg&&response.msg.length){ var $=cheerio.load(response.msg.join(""));/*把所有的数组元素拼接在一起,以空白符分隔,不要这样join(),它会默认数组元素以逗号分隔*/ var answerList=$(".zm-item-answer"); answerList.map(function(i,answer){ var images=$(answer).find('.zm-item-rich-text img'); images.map(function(i,image){ photos.push($(image).attr("src")); }); }); setTimeout(function(){ offset+=; console.log("已成功抓取"+photos.length+"张图片的链接"); getIAjaxUrlList(offset); },); }else{ console.log("图片链接全部获取完毕,一共有"+photos.length+"条图片链接"); // console.log(photos); return downloadImg(); } } }); }
Siarkan permintaan ini dalam kod https://www.zhihu.com/node/QuestionAnswerListV2, salin pengepala permintaan asal dan parameter permintaan sebagai pengepala permintaan dan parameter permintaan kami, superagent Kaedah yang ditetapkan boleh digunakan untuk menetapkan pengepala permintaan, dan kaedah hantar boleh digunakan untuk menghantar parameter permintaan. Kami memulakan offset dalam parameter permintaan kepada 20, menambah 20 pada offset setiap masa tertentu, dan kemudian menghantar semula permintaan Ini bersamaan dengan kami menghantar permintaan ajax setiap masa dan mendapatkan 20 keping data terkini get the Apabila kami mendapat data, kami akan memproses data pada tahap tertentu dan mengubahnya menjadi satu perenggan keseluruhan HTML, yang akan memudahkan pengekstrakan dan pemprosesan pautan seterusnya. Selepas kawalan konkurensi tak segerak memuat turun gambar dan memperoleh semua pautan gambar, iaitu, apabila ia ditentukan bahawa response.msg kosong, kami akan memuat turun gambar-gambar ini adalah mustahil untuk memuat turunnya satu persatu, kerana seperti yang anda lihat, kami Gambar dah cukup
Ya, lebih daripada 20,000 gambar, tetapi mujurlah nodejs mempunyai ciri tak segerak satu benang ajaib, kami boleh memuat turun gambar ini pada masa yang sama. Tetapi kali ini timbul masalah. Saya mendengar bahawa jika terlalu banyak permintaan dihantar pada masa yang sama, alamat IP akan disekat oleh laman web! Adakah ini benar? Saya tidak tahu, saya belum mencubanya, kerana saya tidak mahu mencubanya ( ̄ー ̄〃), jadi pada masa ini kita perlu mengawal bilangan serentak tak segerak.
Modul ajaib digunakan di sini => async, yang bukan sahaja membantu kami menyingkirkan syaitan piramid panggil balik yang sukar diselenggara, tetapi juga membantu kami mengurus proses tak segerak dengan mudah. Sila rujuk dokumentasi untuk mendapatkan butiran Memandangkan saya tidak tahu cara menggunakannya sendiri, saya hanya menggunakan kaedah async.mapLimit yang berkuasa di sini. Ia sangat hebat.
var requestAndwrite=function(url,callback){ request.get(url).end(function(err,res){ if(err){ console.log(err); console.log("有一张图片请求失败啦..."); }else{ var fileName=path.basename(url); fs.writeFile("./img/"+fileName,res.body,function(err){ if(err){ console.log(err); console.log("有一张图片写入失败啦..."); }else{ console.log("图片下载成功啦"); callback(null,"successful !"); /*callback貌似必须调用,第二个参数将传给下一个回调函数的result,result是一个数组*/ } }); } }); } var downloadImg=function(asyncNum){ /*有一些图片链接地址不完整没有“http:”头部,帮它们拼接完整*/ for(var i=;i<photos.length;i++){ if(photos[i].indexOf("http")===-){ photos[i]="http:"+photos[i]; } } console.log("即将异步并发下载图片,当前并发数为:"+asyncNum); async.mapLimit(photos,asyncNum,function(photo,callback){ console.log("已有"+asyncNum+"张图片进入下载队列"); requestAndwrite(photo,callback); },function(err,result){ if(err){ console.log(err); }else{ // console.log(result);<=会输出一个有万多个“successful”字符串的数组 console.log("全部已下载完毕!"); } }); };
Tengok sini dulu=>
Foto parameter pertama kaedah mapLimit ialah tatasusunan semua pautan gambar, yang juga merupakan objek permintaan serentak kami mengehadkan bilangan permintaan serentak Tanpa parameter ini, lebih daripada 20,000 permintaan akan dihantar di masa yang sama. , IP anda akan berjaya disekat, tetapi apabila kami mempunyai parameter ini, sebagai contoh, nilainya ialah 10, ia hanya akan membantu kami mengambil 10 pautan dari tatasusunan pada satu masa dan melaksanakan permintaan serentak selepas 10 ini permintaan dijawab, 10 permintaan seterusnya dihantar. Beritahu Ni Meng, tidak mengapa untuk menghantar 100 mesej pada masa yang sama Kelajuan muat turun adalah sangat pantas