加油,每天总结一点点进步生活每一天!
cheerio模块
http模块配合cheerio爬数据
cheerio模块的作用是分析数据的内容/具体信息
用 fs.writeFile()方法下载数据
下载一张图片案例
var https=require("https")
var fs=require("fs")
var url="https://img04.sogoucdn.com/net/a/04/link?url=http%3A%2F%2Fimg03.sogoucdn.com%2Fapp%2Fa%2F100520024%2F968013acbc34fa232b16607a4b519eab&appid=122"
https.get(url,(res)=>{
// 如果你想下载文本就要设置成res.setEncoding('utf8');
res.setEncoding("binary")
// 如上 res.setEncoding("binary")
// 是吧把请求的格式转成图片的格式,不然下载的图片失效
var t=""
res.on("data",(chunk)=>{
t+=chunk
})
res.on("end",(chunk)=>{
fs.writeFile("./图片/tp.jpg",t,"binary",function(err){
if(err){
console.log("图片下载失败!",err);
}else{
console.log("图片下载成功!");
}
})
})
}).on("err",function(err){
if(err){
console.log("图片下载失败!");
}
}
)
爬虫爬取图片地址信息
// 通过http模块发起网络请求
const https=require("https");
const { on } = require("process");
const fs=require("fs")
var cheerio=require("cheerio")
let url="https://www.qunar.com/" //https://www.baidu.com/
//https://www.qunar.com/
https.get(url, (res) => {
// 请求成功的回调!
const { statusCode } = res;
const contentType = res.headers['content-type'];
console.log(statusCode,contentType);
var t=""
// 监听数据的改变,流读取,当一个数据片段传递完毕后触发
// 每个网页的数据片段都不一样的大小,和不一样的数据片段个数
// 所以上面let出来的url不同监听数据的改变也可能不一样的次数
// 可以理解为 on data事件和on end 事件他们都是监听作用
res.on("data",(chunk)=>{
t=t+chunk
// console.log("数据改变了!",chunk.toString());
})
// 数据传输结束,所有数据都传输完毕之后触发
res.on("end",()=>{
// fs.writeFile("./qunar.html",t,(err)=>{
// if(err){
// console.log("下载失败,报错信息为→",err);
// }else{
// console.log("完美下载成功!");
// }
// })
// console.log("全部整合的数据",t);
// console.log("数据传输结束!");
// 使用cheerio 分析数据的内容
const $=cheerio.load(t)
$("img").each((index,el)=>{
console.log("图片"+index);
console.log($(el).attr("src"));
})
})
}).on('error', (e) => {
// on方法通常是事件监听
// 监听如果请求错误就执行这个回调函数
console.error(`出现错误: ${e.message}`);
});
我们用request请求配合 fs.createWriteStream爬数据,以李宁官网数据为准.搞他数据就完事儿.
因为下面的cheerio使用的方法和jQuery方法类似所以我们先讲解方法
JQUERY的FIND()方法
find()
作用是查找某元素下的某后代元素的集合
find()方法的参数是js里面的选择器
$(".x").find(".sp")
如上代码表示获取类名为x的元素线下的子元素中所有类名为所有sp的元素
$(".x")是jquery的实例,如果想让jquery实例变成dom元素就写成
$(".x")[0]如果想让dom元素变成jquery实例就直接把dom
元素放到
$()方法的括号内部如
var x=document.querySelector(".x")
console.log(x);
console.log($(x));
例子如下:
$(".d1").find("span").each((idex, el) => {
console.log("索引:", idex, "元素:", el)
const name = $(el).text()
console.log(name)
})
JQUERY的EACH()方法
each(index,el)这个方法主要用作循环东西的参数一为index索引参数二为元素el,el这个参数用console.log(index,el);
打印出来会是一个dom元素
<body>
<div class="d1">
<span class="s1">心心相印</span>
<span class="s2">洁柔</span>
</div>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<script>
// jquery中each方法
// 有两种语法的写法
// 如下是法一:
// each()方法有两个参数
// 第一个是参数是数组或者对象,
// 第一个参数可以写在each()方法的小括号里面也可以写在小括号外面
// 但是第二个参数callback必须写在each()方法的小括号内部
// 第一个参数写在each()方法外面的时候连接方法的时候必须用.
$(".d1").find("span").each((idex, el) => {
console.log("索引:", idex, "元素:", el)
const name = $(el).text()
console.log(name)
})
// 如下是法二:
$.each($(".d1").find("span"), (idex, el) => {
console.log("索引:", idex, "元素:", el)
const name = $(el).text()
console.log(name)
})
</script>
下面是我们爬图正文代码
var express = require("express")
var cheerio = require("cheerio")
var request = require("request")
var path=require("path")
var fs=require("fs")
const {
response
} = require("express")
var app = express()
var url = "https://store.lining.com/shop/goodsCate-sale,desc,1,15s15_122,15_122,15_122_m,15_122s15_122_11-0-0-15_122_11-0s0-0-0-min,max-0.html"
// 获取目标地址的html结构
var shuju=0
request.get(url, (err, response, body) => {
// 定义一个空数组
var goodsArry = []
var $ = cheerio.load(body)
$(".cate_search_content").find(".selItem").each((index, el) => {
var $el=$(el)
var name=$el.find(".hgoodsName").text()
var price=$el.find(".price").text()
price=price.match(/[\d\.]+/)[0]
var imgurl=$el.find(".selMainPic img").attr("orginalsrc")
var fileName=path.basename(imgurl)
// 先创建好一个文件写入流,定义好在某个文件夹下写入什么文件,文件名和文件格式类型
const fileStream=fs.createWriteStream("./img/"+fileName)
// request(填入参数是地址的时候,返回值是一个数据流)
// 我们把请求回来的图片的数据流导入到文件写入流当中
// 他就把图片写入到文件夹里了
// ★const fileStream=fs.createWriteStream("./img/"+fileName)
// 这段代码其实可以是告诉程序要在某个地址当中写入某个文件,然而并没有数据可以写入
// 下面这行代码,是我们请求回来了数据然后,导入到创建好的写入流当中,已经有数据了所以就可以写入了
request(imgurl).pipe(fileStream)
// 可以有个简单的比喻如下去理解上面的流文件写入👇
// 比如说我先在千峰教育预定一个座位告诉招生老师我改天交学费,让老师给你在某个教室留了一个座位并且告诉班主任,这个座位是某某某名字同学预定了的座位,让班主任先别安排别的同学入座,过了几天后你交学费了,这个座位就名副其实了,你就坐到你预定的那个座位去上课了.
var goods={
name,
price,
imgurl:"./img/"+fileName
}
goodsArry.push(goods)
console.log(goodsArry);
shuju=goodsArry
})
})
app.get("/shuju",(req,res)=>{
res.send(shuju)
})
app.listen(3840, () => {
console.log("服务器端口号3840已经成功开启!");
})
经典总结
如上我们用了fs.write方法和文档写入流的形式爬取数据
二者之间的区别在哪里呢?
如果我们请求的数据或者下载的数据内存过大,使用fs.write这个方法很可能就会报错,而用流的形式是分段式写入所以不会报错.所以尽量用流的形式去请求或者下载数据。