我有两段代码,用来抓取学校的新闻图片,由于每个页面的url都是大概这样的格式*****.com/1 *****.com/2 *****.com/3 所以我就用一个循环把所有的页面穷举一遍,然后对每个循环请求一次url,抓取里面的新闻链接,存在news里面,在访问news里面的url,把照片down下来。代码大概是这样的:
var cheerio = require('cheerio'),
request = require('request'),
_ = require('underscore'),
gm = require('gm'),
fs = require('fs');
var fileNumber = 0;
var picNum = 0;
var news = [];
const address = 'http://ssdut.dlut.edu.cn';
const url = "http://ssdut.dlut.edu.cn/index.php/News/index/p/";
const _dirname= "E:/pictures/"
function newsdown (){
for(var i=0;i<82;i++){
request(url+i,function(err,res,body){
//上面的i如果换成filenumber,结果永远是访问第一个页面
fileNumber++;
//这里如果我fileNumber写成i,结果永远是82
//我也不知道,为什么,所以用了两个计数器
console.log(url+fileNumber+'<-page');
var $ = cheerio.load(body);
//console.log($('table').eq(2).find('table').eq(1).find('tr').slice(1,13).html())
$('table').eq(2).find('table').eq(1).find('tr').slice(1,13)
.each(function(index,ele){
//console.log(address+this.find('a').attr('href'));
//把url存在news里面
news.push(this.find('a').attr('href'));
});
});
};
};
//访问news里的url,把img标签里的图片存在电脑里
function picdown (){
_.each(news,function(){
request(address+this.find('a').attr('href'),function(err,res,body){
var $ = cheerio.load(body);
$('img').slice(1).each(function(index,ele){
gm(address+this.attr('src')).write(_dirname+picNum+'.jpg',function(err){
if(!err) console.log('No:'+picNum+':done!');
picNum++;
});
});
});
});
}
newsdown();
picdown();
这段代码的问题在于……我写成了同步的,newsdown这部分还没只得到结果,picdown就运行了,结果news里面全是null。 然后我写成回调的形式在最后调用picdown,但实际上还是上面的代码没把news得出结果,回调函数就执行了。最后没有办法,我直接把picdown嵌套在迭代的内部,变成下面这段:
var cheerio = require('cheerio'),
request = require('request'),
_ = require('underscore'),
gm = require('gm'),
fs = require('fs');
//23
var fileNumber = 0;
var picNum = 0;
var news = [];
const address = 'http://ssdut.dlut.edu.cn';
const url = "http://ssdut.dlut.edu.cn/index.php/News/index/p/";
const _dirname= "E:/pictures/"
function newsdown (){
for(var i=0;i<82;i++){
request(url+i,function(err,res,body){
fileNumber++;
console.log(url+fileNumber+'<-page');
var $ = cheerio.load(body);
//console.log($('table').eq(2).find('table').eq(1).find('tr').slice(1,13).html())
$('table').eq(2).find('table').eq(1).find('tr').slice(1,13)
.each(function(index,ele){
//console.log(address+this.find('a').attr('href'));
request(address+this.find('a').attr('href'),function(err,res,body){
var $ = cheerio.load(body);
$('img').slice(1).each(function(index,ele){
gm(address+this.attr('src')).write(_dirname+picNum+'.jpg',function(err){
if(!err) console.log('No:'+picNum+':done!');
picNum++;
});
});
});
});
});
};
};
newsdown();
图片是能down下来了,但是运行的测试信息是这样的:
部分截图
http://ssdut.dlut.edu.cn/index.php/News/index/p/77<-page
http://ssdut.dlut.edu.cn/index.php/News/index/p/78<-page
http://ssdut.dlut.edu.cn/index.php/News/index/p/79<-page
http://ssdut.dlut.edu.cn/index.php/News/index/p/80<-page
http://ssdut.dlut.edu.cn/index.php/News/index/p/81<-page
http://ssdut.dlut.edu.cn/index.php/News/index/p/82<-page
No:0:done!
No:1:done!
No:3:done!
No:4:done!
No:7:done!
也就是外层循环全部执行完了,才来执行内层的循环。(还是说外层的循环先返回结果,而I\O量更大的内层才返回?) 还有就是每次执行都会漏掉一些图片,我就跑了很多遍,我又是按迭代的number来命名的,所以有些图片可能占了 4.jpg ,8.jpg,9.jpg几个名字的情况,要么就是把前面down的图片覆盖了,要么就是被别的图片覆盖了。
然后我又写了一个模拟登陆QQ的代码,发送信息,奇怪的地方写在注释里了,代码大概是这样的:
var request = require('request');
var qqinfo = {
loginType:2,//1:不登录 2:隐身登录 3:在线登录
qq:123456,//qq号
pwd:'123456'//qq密码
};
var Vdata ={
sid:''
};
var userAgend = 'Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13';
var PostHead = {'User-Agent' : userAgend , 'Content-Type' : 'application/x-www-form-urlencoded'};
var address = 'http://pt.3g.qq.com/handleLogin';
var Post = {
url:address,
headers:PostHead,
method:'POST',
form:qqinfo
};
var Friends = [];
//模拟webqq post消息,登陆以后用正则表达式抓取sid
request(Post,function(err,res,body){
if(res.statusCode == 302){
var reg = new RegExp('sid=(.[^&]+)','ig');
//console.log(res.headers.location);
reg.exec(res.headers.location);
var sid = RegExp.$1
Vdata.sid = sid;
//console.log(sid);
console.log('login!');
getFriendsPage(1,function(){
console.log('\nonline friends:'+Friends.length+'\n');
for(var i = 0 ;i<Friends.length;i++){
var qqInfo = Friends[i];
console.log(i+1+':'+qqInfo.qq+' '+'\t['+qqInfo.name+']');
};
if(sendmsg(1,'hi')) console.log('ok!');
});
return;
}
if(res.body.indexOf('验证码')>=0){
console.log('需要输入验证码');
return;
}
});
//用拿到的sid请求好友列表界面
function getFriendsPage(page,cb) {
Friends =[];
var chatMain = 'http://q16.3g.qq.com/g/s?sid=$SID&aid=nqqchatMain&p=$Page';
chatMain = chatMain.replace('$SID',Vdata.sid);
chatMain = chatMain.replace('$Page',page);
request({url:chatMain,headers:{'User-Agent':userAgend,'Cache-Control':'max-age=0'},method:'GET'},function(err,res,body){
var regx = /u=(\d+)[\s\S]+?class="name" >(.*?)<\/span>/ig
//如果我在这里写一个console.log(boody)什么的,下面的body却是null。
while(regx.exec(body)){
console.log('qq'+RegExp.$1);
var qqInfo = {qq:RegExp.$1,name:RegExp.$2};
Friends.push(qqInfo);
};
cb();
return;
});
};
//模拟信息发送
function sendmsg(index,msg){
var qqInfo = Friends[index-1];
var form ={
'u':qqInfo.qq,
'msg':msg,
'aid':'发送'
}
if(!qqInfo) return false;
var pUrl = 'http://q32.3g.qq.com/g/s?sid=$SID';
pUrl = pUrl.replace('$SID',Vdata.sid);
request({url:pUrl,form:form,headers:{'User-Agent':userAgend,'Cache-Control':'max-age=0','Content-Type': 'application/x-www-form-urlencoded'},method:'POST'},function(err,res,body){
if(body.indexOf('重新登录')>=0 && body.indexOf('书签可能有误')>=0){
console.log('发送失败');
console.log(form);
return false;
}else{
return true;
}
});
}
如果我删掉之前的调试信息,就勉强能够运行,是不是就是说,我的同步代码要建立在之前的I\O要比之后的I\O更快得出结果的前提下?
这让我很困惑,怎样写才能符合异步的风格呢? 仅仅回调好像也不代表异步………… 如果用事件触发又该是怎样的呢……