nodejs之爬虫
爬虫
使用场景:目标服务器无接口,采用SSR方式渲染的网站
- 分析html结构,找出需要爬取的区域特点
- 使用特定工具
- request 用于获取目标html结构
- cheerio 用户过滤和获取数据(一个类似与jquery的工具,可以使用jq选择器)
- 把得到的数据写入数据库
- 下载图片到本地
stream数据流
- fs.readFile() 写入文件
- fs.createReadStream() 读取文件流
- fs.createWriteStream() 写入文件流
/**
*
* 1、请求的网站数据
* 2、将数据保存本地文件
*
*/
// 通过一个http请求,获取一个网站的信息
const http = require('http')
const fs=require('fs')
let url = 'http://www.baidu.com/'
http.get(url, (res) => {
// 数据分段,只要接收数据就会触发data 事件 chunk 每次接收的数据片段
// 用于拼接 chunk 数据片段 就是下面的字符串拼接
let rawData = ''
res.on('data', (chunk) => {
// console.log('数据传输')
// console.log(chunk) // chunk 指每次传输过来的数据有哪些
// console.log(chunk.toString('utf8'))
rawData += chunk.toString('utf8')
})
res.on('end', () => {
// 将请求的数据保存到本地
fs.writeFileSync('./baidu.html',rawData)
console.log('数据库传输完毕')
})
}).on("error", (err) => {
console.log('请求错误')
})
下面说一个模块 cheerio :用户过滤和获取数据(一个类似与jquery的工具,可以使用jq选择器)
const cheerio = require('cheerio')
let $ = cheerio.load('<div><p>hello world</p><img src="http://baidu1.com"><img src="http://baidu2.com"><img src="http://baidu3.com"></div>')
// 将一组HTML格式的字符串转化为类DOM元素,之后可以通过jq的语法选中其中的元素
console.log($('img').attr('src'))
console.log($('p').html())
// 遍历
$('img').each((index, el) => {
console.log($(el).attr('src'))
})
/**
*
* 1、请求的网站数据
* 2、将数据保存本地文件
*
*/
// 通过一个http请求,获取一个网站的信息
const http = require('https')
const fs = require('fs')
const cheerio = require('cheerio')
let url = 'https://www.csdn.net'
// http.get 可以是 json 看官网文档
// let json = 'http://nodejs.org/dist/index.json'
http.get(url, (res) => {
// 安全判断
const {
statusCode
} = res; //状态码
const contentType = res.headers['content-type']; //文件类型
// console.log(statusCode, contentType)
let error = null;
// 任何 2xx 状态码都表示成功的响应,但是这里只检查 200。
if (statusCode !== 200) {
error = new Error('请求失败\n' + `状态码: ${statusCode}`);
} else if (!/^text\/html/.test(contentType)) {
error = new Error('无效的 content-type.\n' + `期望的是 text/html 但接收到的是 ${contentType}`);
}
// error 为真 两个判断出错
if (error) {
console.error(error.message);
// 响应的数据来释放内存。
res.resume();
return;
}
// 数据处理
// 数据分段,只要接收数据就会触发data 事件 chunk 每次接收的数据片段
// 用于拼接 chunk 数据片段 就是下面的字符串拼接
let rawData = ''
res.on('data', (chunk) => {
// console.log('数据传输')
// console.log(chunk) // chunk 指每次传输过来的数据有哪些
// console.log(chunk.toString('utf8'))
rawData += chunk.toString('utf8')
})
res.on('end', () => {
// 将请求的数据保存到本地
// fs.writeFileSync('./baidu.html', rawData)
console.log('数据库传输完毕')
// 通过 cheerio 分析
let $ = cheerio.load(rawData) //将请求的网页进行转化
$('img').each((index, el) => {
console.log($(el).attr('src')) //爬取网页中img的src
})
})
}).on("error", (err) => {
console.log('请求错误')
})
下面用到第三方 request 和 cheerio模块
/**
* 目标网址:http://store.lining.com/shop/goodsCate-sale,desc,1,15s15_122,15_122_m,15_122_ls15_122_10,15_122_10_m,15_122_10_l-0-0-15_122_10,15_122_10_m,15_122_10_l-0s0-0-0-min,max-0.html
* 工具
* request
* cheerio
*/
const fs = require('fs');
const path = require('path');
const request = require('request');
const cheerio = require('cheerio');
// 1.获取目标html结构
const url = 'http://store.lining.com/shop/goodsCate-sale,desc,1,15s15_122,15_122_m,15_122_ls15_122_10,15_122_10_m,15_122_10_l-0-0-15_122_10,15_122_10_m,15_122_10_l-0s0-0-0-min,max-0.html';
request(url, (err, res, body) => {
let $ = cheerio.load(body);
const goodslist = []
$('.cate_search_content').find('.selItem').each((idx, ele) => {
const $ele = $(ele);
const name = $ele.find('.hgoodsName').text();
let price = $ele.find('.price').text()
price = price.match(/[\d\.]+/)[0]
const imgurl = $ele.find('.selMainPic img').attr('orginalsrc')
// 2. 爬取图片到本地
// request请求图片地址,返回一个数据流
const filename = path.basename(imgurl);
const fileStream = fs.createWriteStream('./img/' + filename);
request(imgurl).pipe(fileStream);
const goods = {
name,
price,
imgurl: 'img/' + filename
}
goodslist.push(goods);
});
console.log(goodslist);
})