使用nodejs爬取图片
在运行代码前,请确保本机是否有nodejs环境
1 D:\ > node -v 2 v12.1.0 //版本号
需要用到的包
axios //请求页面 cheerio // 把get请求的页面 可以使用jq的方法来获取dom fs // 文件读写模块 request // 请求模块 用来请求下载地址 path // 路径模块 url // 路由模块
爬虫遵循的规则
- 遵守 Robots 协议,谨慎爬取
- 限制你的爬虫行为,禁止近乎 DDOS 的请求频率,一旦造成服务器瘫痪,约等于网络攻击
- 对于明显反爬,或者正常情况不能到达的页面不能强行突破,否则是 Hacker 行为
- 如果爬取到别人的隐私,立即删除,降低进局子的概率。另外要控制自己的欲望
本次案例百度图片表情包
1 const axios = require("axios") 2 const cheerio = require("cheerio") 3 const fs = require('fs'); 4 const request = require("request"); 5 const path = require('path'); 6 const url = require("url") 7 8 /** 9 * 休眠方法,因为一次访问太多次目标网址会被拉黑 10 * @param {Number} time 休眠时间以毫秒为单位 11 * @returns Promise 12 */ 13 const sleep = time => { 14 return new Promise(function (resolve, reject) { 15 setTimeout(function () { 16 resolve(); 17 }, time); 18 }) 19 } 20 21 /** 22 * 爬取某个地址下的img 图片 23 * @param {String} httpUrl 目标地址 24 * @param {Boolean} isBaidu 是否为百度图片地址 25 */ 26 async function start(httpUrl, isBaidu) { 27 28 // 获取目标网址的dom节点,使用jq的方法来获取dom节点 29 const { data } = await axios.get(httpUrl) 30 let img = []; 31 // 针对百度 进行爬取数据 32 if (isBaidu) { 33 img = getBaiDuImg(data) 34 } else { 35 const $ = cheerio.load(data) 36 // 根据dom获取img src 37 $(".imgbox img").each(function () { 38 let src = dealImgUrl($(this).attr("src"), httpUrl) 39 img.push(src) 40 }) 41 } 42 43 // 检测当前文件夹是否存在存储图片的文件夹 44 const random = parseInt(Math.random() * 100) 45 const dirPath = "./img" + random 46 if (!fs.existsSync(dirPath)) { 47 fs.mkdirSync(dirPath) 48 } 49 fs.writeFileSync("./data.json", JSON.stringify(img), "utf-8") 50 51 /** 52 * 在这里为什么不使用forEach遍历? 答案在下方链接 53 * https://blog.csdn.net/yumikobu/article/details/84639025 54 */ 55 for (let i = 0; i < img.length; i++) { 56 const item = img[i] 57 await sleep(1500) 58 59 console.log(item + "开始下载"); 60 // const extname = path.extname(item) || '.jpg' 61 // const filename = (i + 1) + extname 62 let myUrl = new URL(item) // node vision must v10 up! 63 let httpUrl = myUrl.origin + myUrl.pathname 64 let filename = i + path.basename(httpUrl) 65 const write = fs.createWriteStream(dirPath + "/" + filename) 66 request.get(item).on('error', function (err) { 67 console.log(item + "下载失败", err); 68 }).pipe(write) 69 console.log(item + "本地下载结束"); 70 } 71 } 72 process.on("exit", () => { 73 console.log("爬取结束"); 74 }) 75 /** 76 * 处理img的src 77 * @param {String} src 路径 78 * @param {String} pathurl url地址 79 * @returns 返回图片链接地址 80 */ 81 function dealImgUrl(src, pathurl) { 82 if (src.substr(0, 8) === 'https://' || src.substr(0, 7) === 'http://' || src.substr(0, 2) === "//" || src.substr(0, 5) === "data:") { 83 return src 84 } 85 return url.resolve(pathurl, src) 86 } 87 88 /** 89 * 90 * @param {String} str 百度返回的html字符 91 * @returns {Array} 返回图片链接地址 92 */ 93 function getBaiDuImg(str) { 94 var reg = /app\.setData\(\'imgData\'\,\s+\{(.*?)\"data\":(.*?)\]\}/g 95 var result = reg.exec(str)[2] + ']' 96 result = result.replace(/\'/g, '"') 97 result = JSON.parse(result) 98 const img = [] 99 result.forEach(item => { 100 if (item.objURL) { 101 img.push(item.objURL) 102 } 103 }) 104 return img 105 } 106 // const HTTPURL = "https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=result&fr=&sf=1&fmq=1605517415488_R&pv=&ic=0&nc=1&z=&hd=&latest=©right=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&hs=2&sid=&word=%E7%94%B5%E8%84%91%E5%A3%81%E7%BA%B8++%E6%A2%85%E8%A5%BF" 107 const HTTPURL = "https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&sf=1&fmq=1389861203899_R&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&ala=6&ori_query=%E8%A1%A8%E6%83%85%E5%8C%85&fr=ala&ala=1&alatpl=adress&pos=2&oriquery=%E8%A1%A8%E6%83%85%E5%8C%85&alaTag=0&&word=%E8%A1%A8%E6%83%85%E5%8C%85%20%E6%90%9E%E7%AC%91&hs=2&xthttps=111111" 108 // 开始调用start函数进行爬取图片 109 start(HTTPURL, true)
仔细分析了一下,百度图片并不是在请求页面的时候就把图片的数据渲染到dom上了,而是通过js脚本创建出img的.所以使用正则匹配到img的数据,就可以知道图片链接地址了.
若是一般常规的网站图片 可以使用cheerio来操作dom获取图片路径地址
在代码里使用了一个sleep函数就是为了多次访问网站防止被拉黑,请勿频繁请求!