爬虫:按照一定规则 自动抓取网络信息的程序,搜索引擎就是一个超级大的爬虫;
反爬虫:
user-agent: 如果user-agent是爬虫的话 就拒绝;
验证码
单位时间 访问量的限制
关键信息图片混淆
异步加载 返回空的 html, 使用 利用js 异步来 加载页面;
写爬虫一般都是通过两个工具来实现的 ;
superagent 帮助我们发送请求 得到html;
cheerio 把 html内容 转换成一个 jq 对象,就可以使用jq 去操作html的内容了;但是 他饶不过反爬虫规则;
# 这里使用puppeteer来实现;
google chrome 的 headless/puppeteer 可以使用谷歌浏览器来 操作;所有的操作都是异步的,可以饶过反爬虫规则;
安装 headless/puppeteer cnpm i -S puppeteer
新建 文件 screenshot.js
// 引入 puppeteer
const puppeteer = require("puppeteer");
const path = require("./config/default");
//写一个立即执行函数或者写一个函数下面调用; 反正都是异步函数; 所有的操作都是异步的;
(async () => {
// 实例化一个浏览器对象
const browser = await puppeteer.launch()
// 实例化一个浏览器对象的页面对象;
const page = await browser.newPage()
// goto baidu.com
await page.goto("https://baidu.com")
// 截屏 保存地址为 根目录 下的screenshot 文件夹
await page.screenshot({
path:`${path.screenshot}/${new Date()}.png`
})
// 关闭浏览器
await browser.close();
})();
新建文件夹 config 其内新建文件 default.js
const path = require("path");
module.exports={
// 路径为根目录 下的screenshot 文件夹 // 需要预设文件夹否则报错;
screenshot:path.resolve(__dirname,"../screenshot")
};
预设储存 截屏图片的 文件夹 screenshot ,否则允许报错;
开始操作
主代码 ;
// 引入 puppeteer
const puppeteer = require("puppeteer");
const path = require("./config/default");
const srcToimg=require("./helper/srcToimg");
//写一个立即执行函数或者写一个函数下面调用; 反正都是异步函数; 所有的操作都是异步的;
(async () => {
// 实例化一个浏览器对象
const browser = await puppeteer.launch({headless: false})
// 实例化一个浏览器对象的页面对象;
const page = await browser.newPage()
// goto image.baidu.com
await page.goto("https://image.baidu.com/")
console.log( "image.baidu.com");
//设置 窗口的大小变得很大 以获取更多的图片 方便抓取;
await page.setViewport({
width:1920,
height:1080
})
console.log("reset viewport")
// 使得搜索框聚焦 模拟键入 "狗" 模拟点击搜索;
await page.focus("#kw");
await page.keyboard.sendCharacter("狗");
console.log(22);
await page.click('.s_search');
console.log("go to search list");
// 异步加载完成以后; 由于回调函数要异步 所以 要用到 async
await page.on('load', async ()=>{
console.log("page loading done , start fetch");
// 使用page.evaluate 来拿到dom
const srcs = await page.evaluate(()=>{
//。拿到所有的图片集合 ,但是集合不是数组 ,所以不能调用map方法
const imgs = document.querySelectorAll('.main_img');
//使用数组的map方法绑定 ,并且处理 拿到 原来item的src,变成一个所有图片src的集合
return Array.prototype.map.call(imgs,(img)=>{return img.src});
});
//循环 处理这个页面图片src的数据;
srcs.forEach(async (src)=>{
//sleep 休息200毫秒再去调用 这样做是为了反爬虫;
await page.waitFor(200);
await srcToimg(src,path.mn);
})
})
// 关闭浏览器
// await browser.close();
})();
工具函数
//引入http和https模块;
const http = require("http");
const https = require("https");
// 引入fs
const fs = require("fs");
//引入path
const path = require("path");
//引入 异步模块
const {promisify} = require("util");
//使用异步模块处理fs。WriteFile;
const writeFile=promisify(fs.writeFile)
//处理src 储存的 函数;
module.exports = async (url, dir) => {
var reg = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)\s*$/i;
//判断 是否是base的;来匹配方法
if(!reg.test(url)){
await urlToimg(url, dir)
}else {
await base64Toimg(url, dir)
}
};
// url to img// 由于之后要使用到异步函数; 所以这个函数要promisify 一下 方便使用 之后使用await
const urlToimg =promisify ( (url,dir,callback) => {
//拿到url的后缀名 (带.点的);
const ext = path.extname(url);
//文件的存储路径 使用传入的路径 + 后缀名 拼接成的;
const filePath = path.join(dir,`${Date.now()}${ext}`)
// 分清楚url是http还是https; 分别使用不同的模块去处理地址;
//如果是https开头的那么 就使用https模块否则使用http模块;
const mod = /^https:/.test(url)?https:http;
// 使用对应的模块使用 get方法 拿到对应的处理 结果 ;是一个流文件;
mod.get(url,(res)=>{
// 把流 pipe到磁盘上; 使用 fs的书写方法 书写到文件路径上去;
res.pipe(fs.createWriteStream(filePath)).on("finish",()=>{
callback();
console.log(filePath)
})
})
})
//base64 to img
const base64Toimg =async (base64,dir) => {
try{
//data:image/jpeg;base64,/qwqwdq base64格式;
//使用match 匹配 base64的字符串;规则
// 意思是 以 data: 开头的, +?(关闭贪婪模式)遇到 ;base64, 停止
// 这样就有2个 .+ 的两段内容; 被放置到 match的数组里了;
const matches = base64.match(/^data:(.+?);base64,(.+)$/)
const ext =matches[1].split("/")[1].replace("jpeg","jpg"); //取到jpeg,如果遇到jpeg 那么就换成jpg;
//文件的存储路径 使用传入的路径 + 后缀名 拼接成的;
const fileName = path.join(dir,`${Date.now()}.${ext}`)
//三个参数,文件名,内容,编码格式(默认buffer);
await writeFile(fileName,matches[2],"base64");
console.log(fileName);
}catch (e) {
console.log("base64is not")
}
};
路径类
const path = require("path");
module.exports={
// 路径为根目录 下的screenshot 文件夹 // 需要预设文件夹否则报错;
screenshot:path.resolve(__dirname,"../screenshot"),
mn:path.resolve(__dirname,"../mn")
};