spider(爬蟲)
推薦抓取工具:火車采集器\火車頭采集器
爬蟲抓取網(wǎng)頁
//node 中http模塊,可以用來開啟服務(wù)器(http.createServer),也可以用來開啟客戶端(http.request)
//網(wǎng)頁抓取核心代碼如下 --> server.js
const http = require('http');
const https = require('https');
const urllib = require('url');
const fs = require('fs');
const pathlib = require('path');
const assert = require('assert');
/*
封裝http.request,用來自動(dòng)分別http與https
url --> 網(wǎng)址
headers --> options需要的headers
*/
function reqUrl(url,headers){
//使用url模塊解析url,得到url對(duì)象
let urlObj = urllib.parse(url);
//用來獲取http或者h(yuǎn)ttps
let httpMode = null;
if(urlObj.protocol == 'http:'){
httpMode=http;
}else if(urlObj.protocol == 'https:'){
httpMode=https;
}else{
console.log(urlObj.protocol);
throw new Error('錯(cuò)誤');
}
//http.request的optios參數(shù)
const options = {
host:urlObj.host,//要爬取的網(wǎng)站
path:urlObj.path,//要爬取的路徑
header:headers//某些網(wǎng)站爬取需要的headers
};
//因http.request是個(gè)異步函數(shù),所以此處return一個(gè)promise
return new Promise((resolve,reject)=>{
//http.request,作為一個(gè)客戶端向服務(wù)端發(fā)起請(qǐng)求
let req = httpMode.request(options,res=>{
// res.statusCode作為訪問成功與否的標(biāo)準(zhǔn)
if(res.statusCode>=200&&res.statusCode<300||res.statusCode==304){
let arr = [];
res.on('data',function (data){
arr.push(data);
});
res.on('end',function (){
let buffer = Buffer.concat(arr);
//接收處理結(jié)果交由resolve()
resolve({
statusCode:200,
body: buffer,
headers: res.headers
})
})
//如果是301或者302,也是成功這是需要跳轉(zhuǎn)
}else if(res.statusCode==301||res.statusCode==302){
resolve({
statusCode:res.statusCode,
body: null,
headers: res.headers
})
//出錯(cuò)了
}else{
reject({
statusCode:res.statusCode,
body: null,
headers: res.headers
});
}
});
//請(qǐng)求的時(shí)候就出錯(cuò)了,例如網(wǎng)址不存在
req.on('error',err=>{
console.log('error',err);
});
req.write('');
//end()表示正式開始請(qǐng)求
req.end();
})
}
async function req(url){
try{
//循環(huán)處理多次301或者302的情況
while(true){
let {statusCode,body,headers} = await reqUrl(url);
console.log(statusCode,body,headers);
if(statusCode==200){
return {body,headers}//成功之后 退出循環(huán)
}else{
//let {statusCode,body,headers:head} = await reqUrl(headers.location);
assert(headers.location);//此處使用斷言一定有此屬性
url=headers.location;//不成功將url重新賦值,繼續(xù)循環(huán)
}
}
}catch(e){
console.log('進(jìn)到catch',e);
}
}
//因req是一個(gè)async函數(shù),所以此處也需要使用async函數(shù)接收數(shù)據(jù),此處使用了自調(diào)用函數(shù)
(async ()=>{
let {body,headers} = await req('http://www.tmall.com');
fs.writeFile('spider',body,err=>{
if(err){
console.log('寫入失敗');
}else{
console.log('寫入完成');
}
})
})()
jsdom.js
原因:因爬蟲抓取的是html網(wǎng)頁,所以需要進(jìn)行數(shù)據(jù)解析,jsdom第三方模塊可以實(shí)現(xiàn)
作用:將html反向解析成dom對(duì)象
安裝:cnpm i jsdom -D
使用:
const JSDOM = require('jsdom').JSDOM;
const fs=require('fs');
fs.readFile('test.html', (err, buffer)=>{
if(err){
console.log('讀取失敗');
}else{
let html=buffer.toString();
//創(chuàng)建jsdom對(duì)象,并將需要解析的對(duì)象傳參進(jìn)去
let jsdom=new JSDOM(html);
//獲取dom對(duì)象
let document=jsdom.window.document;
//創(chuàng)建$
let $=document.querySelectorAll.bind(document);
//按照J(rèn)S操作dom對(duì)象方式進(jìn)行操作即可
let oTxt=$('input.txt1')[0];
console.log(oTxt.value);
}
});
爬蟲抓取數(shù)據(jù)
1. 使用爬蟲抓取網(wǎng)頁;(此處以天貓手機(jī)網(wǎng)頁為例,部分網(wǎng)站數(shù)據(jù)應(yīng)該是抓取shr數(shù)據(jù))
2. 使用jsdom對(duì)網(wǎng)頁進(jìn)行解析
3. 封裝存庫,繼續(xù)向下抓取,等等等操作
4. 詳見4-19.zip --> tmall_shouji_spider2.js
某些頁面編碼是gbk然而node本身不認(rèn)gbk編碼,所以buffer.toString('gbk')指定編碼的方法不可用,這里需要使用第三方gbk模塊;
cnpm i gbk -D
const gbk=require('gbk');
gbk.toString('utf-8',buffer);//使用gbk模塊將buffer編碼變成utf-8