이전에 작성한 작은 크롤러는 지금은 매우 불완전한 것 같습니다. 예를 들어 Zhihu에서 질문을 클릭하면 해당 답변이 모두 로드되지 않습니다. 답변이 끝나면 더 보기를 클릭하면 답변이 다시 로드됩니다. 따라서 질문에 대한 요청 링크를 직접 보내면 얻은 페이지가 불완전해집니다. 또한 링크를 보내 사진을 다운로드할 때 사진이 너무 많으면 잠을 자고 난 후에도 다운로드됩니다. 게다가 우리가 nodejs를 사용하여 작성한 크롤러는 사진을 하나씩 다운로드하지 않습니다. 하나. nodejs의 가장 강력한 비동기 및 동시성 기능을 사용하지 않는 것은 너무 낭비입니다.
생각
이번 크롤러는 지난 크롤러의 업그레이드 버전입니다. 하지만 지난 크롤러는 간단했지만 초보자가 배우기에 매우 적합합니다. 이번 크롤러 코드는 제 github => NodeSpider에서 확인하실 수 있습니다.
전체 크롤러의 아이디어는 이렇습니다. 처음에는 요청 질문의 링크를 통해 페이지 데이터의 일부를 크롤링한 다음 코드에서 Ajax 요청을 시뮬레이션하여 물론, 여기서는 비동기적으로 수행할 수도 있습니다. 소규모 비동기 프로세스 제어를 위해 이 모듈 => eventproxy를 사용할 수 있지만 여기서는 사용하지 않습니다. 획득한 페이지를 분석하여 모든 사진의 링크를 가로채고 비동기 동시성을 통해 이러한 사진의 일괄 다운로드를 구현합니다.
페이지의 초기 데이터를 캡쳐하는 방법은 매우 간단하므로 여기서는 많은 설명을 생략하겠습니다
/*获取首屏所有图片链接*/ 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(); } }); }
전체 페이지를 얻기 위해 Ajax 요청 시뮬레이션
다음 단계는 추가 로드를 클릭할 때 발행된 Ajax 요청을 시뮬레이션하는 방법입니다. Zhihu로 이동하여 살펴보세요!
이 정보를 사용하면 이 데이터를 얻기 위해 동일한 요청 전송을 시뮬레이션할 수 있습니다.
/*每隔毫秒模拟发送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(); } } }); }
이 요청을 코드 https://www.zhihu.com/node/QuestionAnswerListV2에 게시하고 원래 요청 헤더와 요청 매개변수를 요청 헤더와 요청 매개변수로 복사합니다. superagent set 메소드는 다음을 수행할 수 있습니다. 요청 헤더를 설정하는 데 사용되며, send 메소드를 사용하여 요청 매개변수를 보낼 수 있습니다. 처음에는 요청 매개변수의 오프셋을 20으로 설정하고, 특정 시간마다 오프셋에 20을 추가한 다음 요청을 다시 보냅니다. 이는 특정 시간마다 Ajax 요청을 보내고 매번 최신 20개의 데이터를 가져오는 것과 같습니다. 데이터를 얻으면 데이터를 어느 정도 처리하고 이를 HTML의 전체 단락으로 변환하여 후속 추출 및 링크 처리를 용이하게 합니다. 비동기 동시성 제어가 그림을 다운로드하고 모든 그림 링크를 얻은 후, 즉 response.msg가 비어 있다고 판단되면 이러한 그림을 하나씩 다운로드하는 것이 불가능합니다. 사진이 충분해요
예, 20,000개가 넘는 사진이 있습니다. 하지만 다행스럽게도 nodejs에는 마법의 단일 스레드 비동기 기능이 있어 이러한 사진을 동시에 다운로드할 수 있습니다. 그런데 이번에는 문제가 발생합니다. 동시에 너무 많은 요청이 전송되면 웹사이트에서 IP 주소가 차단된다고 들었습니다. 이것이 사실입니까? 모르겠어요, 시도하지 않았기 때문에 시도하고 싶지 않기 때문에( ̄— ̄〃) 이때 비동기 동시성 수를 제어해야 합니다.
여기서 마법의 모듈이 사용되었습니다. => async는 유지 관리가 어려운 콜백 피라미드 악마를 제거하는 데 도움이 될 뿐만 아니라 비동기 프로세스를 쉽게 관리하는 데도 도움이 됩니다. 자세한 내용은 설명서를 참고해주세요. 직접 사용하는 방법을 모르기 때문에 여기서는 강력한 async.mapLimit 메소드만 사용합니다. 정말 굉장해요.
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("全部已下载完毕!"); } }); };
먼저 여기를 보세요=>
mapLimit 메소드의 첫 번째 매개변수 사진은 동시 요청의 대상이기도 한 모든 사진 링크의 배열입니다. asyncNum은 이 매개변수가 없으면 동시 요청 수를 제한합니다. 동시에 귀하의 IP는 성공적으로 차단되지만, 예를 들어 이 매개변수의 값이 10인 경우 배열에서 한 번에 10개의 링크를 가져오고 이 10개 이후에 동시 요청을 실행하는 데만 도움이 됩니다. 요청이 응답되면 다음 10개의 요청이 전송됩니다. 니멍한테 메시지 100개 보내도 괜찮다고 해주세요. 다운로드 속도가 엄청 빨라요. 더 올라갈지는 모르겠네요...
위에서는 Nodejs 크롤러 고급 튜토리얼에서 비동기 동시성 제어에 대한 관련 지식을 소개했습니다. 도움이 되기를 바랍니다.