1.Puppetter介绍
Puppeteer是一个由google提供的node.js库,它提供高级API,通过DevTools 协议控制 Chromium 或 Chrome,可以模拟大多数的浏览器操作,puppeteer有以下作用:
生成网页截图和PDF;
自动化表单提交,UI测试,键盘输入等;
创建一个最新的自动化测试环境。使用最新的 JavaScript 和浏览器功能,可以直接在最新版本的 Chrome 中运行测试;
捕获站点的时间线跟踪,以帮助诊断性能问题;
爬取网页内容;
2.安装
npm install --save-dev puppeteer
由于安装包需要执行node install.js来安装Chromium内核,由于国内封网,所以可能出现如下情况:
ERROR: Failed to download Chromium r599821! Set “PUPPETEER_SKIP_CHROMIUM_DOWNLOAD” env variable to skip download.
出现这种情况表示下载失败,可以翻墙或者使用第二种方式,通过设置环境变量或者npm config中的PUPPETEER_SKIP_CHROMIUM_DOWNLOAD跳过下载(npm config set puppeteer_skip_chromium_download = 1),手动下载chromium,网址是:链接
然后再使用的时候手动引入
const browser =await puppeteer.launch({
headless:false,
executablePath:'./Chromium.app/Contents/MacOS/Chromium',
});
3.初始化项目
const puppeteer=require('puppeteer');
//使用puppeteer打开浏览器
(
async ()=>{
const browser =await puppeteer.launch({
headless:true,//表示是否以headless模式打开,false的时候会打开浏览器进行操作
executablePath:'./Chromium.app/Contents/MacOS/Chromium',
});
}
)
4.新建页面,开始模拟操作,抓取内容
//新建一个页面
const page = await browser.newPage();
//跳转到对应的页面
await page.goto("https://v.qq.com/");
//此处停留1s,等待页面完全加载完
//官网有page.waitForNavigation(options)方法,但是打开新页面时次发发放无效
await page.waitFor(1000)
//往选中元素中注入文本内容
await page.type('#keywords',"斗破苍穹");
/模拟说表操作
await page.click('.search_btn');
//等待跳转到新的页面
await page.waitFor(2000);
await page.screenshot({ //生网页截图
path: 'example.png'
})
如上操作会有一个奇怪的现象,先打开了腾讯视频首页,然后在搜索框中输入’斗破苍穹’,单机页面后会跳转到另一个页面,然后我们在进行截取网页操作,但是打开example.png一看,截到的图却是腾讯视频的首页,并不是搜索的结果,这时候我们修改一下headless
const browser =await puppeteer.launch({
headless:false,
executablePath:'./Chromium.app/Contents/MacOS/Chromium',
});
这时候发现浏览器先打开了腾讯视频首页,输入内容后点击搜索后跳转到了新搜索页,但是我们开始截图时又调回了首页,查询API发现这时浏览已经存在多个page,我们需要拿到自己需要的page
let pages= await browser.pages();
//由于浏览器会打开一个空白的页签,所以索引值为2
let content = await pages[2].content();//拿到页面内容
5.分析内容,这里使用cheerio库进行分析,相对jquery,cheerio对内容要求更松,适合进行页面分析
const cheerio = require('cheerio');
let content = await pages[2].content();
let $ = cheerio.load(content);
//因为有多部搜索结果,所以需要先获取每个搜索结果的ID,以便后面请求结果
let result = $('.result_item_v');
let movie = { id: $(element).data('id') };
//获取每个结果的海报图片
$(element).children('._infos').each((i, el) => {
movie.poster = $(el).find('img').attr('src');
//获取评分
$(el).find('.result_title').each((ii, ele) => {
$(ele).find('a').each((iii, v) => {
movie.name = $(v).text().trim();
})
});
movie.score = $(el).find('.result_score').text().trim();
//爬取剧情结果
try {
let url = `https://s.video.qq.com/get_playsource?id=${movie.id}&plat=2&type=4&range=1-1000`;
https.get(url, (res) => {
res.on('data', (chunk) => {
movieHtml += chunk;
});
res.on('end', () => {
movieHtml+='</html>'
let $m = cheerio.load(movieHtml);
resolve3($m);
})
})
} catch (error) {
reject3(error);
}
});
//提取结果
promise3.then(($m) => {
let videoList = [];
console.log($m('videoPlayList').length);
$m('videoPlayList').each((index, ele) => {
let video = {
id: $m(ele).find('id').text().trim(),
title: $m(ele).find('title').text().trim(),
number: $m(ele).find('episode_number').text().trim(),
pic: $m(ele).find('pic').text().trim(),
playUrl: $m(ele).find('playUrl').text().trim()
}
videoList.push(video);
})
movie.videoList = videoList;
movies.push(movie);
resole2();
})
6.编写http服务器,把结果反馈给前端
const search=async (req,res)=>{
let pathName = url.parse(req.url).pathname;
console.log(pathName);
//允许跨越
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader("Access-Control-Allow-Methods", "*");
//必须使用,因为复杂的请求浏览器会先发一次OPTIONS的试探性请求
res.setHeader("Access-Control-Allow-Headers", 'Content-Type');
//拦截试探性请求
if (req.method =='OPTIONS'){
res.end('ok');
return;
}
//简单的路由
if (pathName !='/search'){
res.writeHead(200, { "Content-Type": "text/plain;charset=utf-8" });
res.write('请求错误');
res.end();
return ;
}
res.writeHead(200, { "Content-Type": "application/json;charset=utf-8" });
res.write(JSON.stringify(movies));
res.end();
}
http.createServer(search).listen(3000);
7.编写前端页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
</head>
<style>
.videoList{
border: 1px solid #eee;
}
.video-box{position: absolute;display: none;width: 800px;height: 400px;left: 50%;top: 30%;margin-left: -400px;}
</style>
<body style='min-height:100%;min-width:100%'>
<div id='video'></div>
<div class="video-box"></div>
<script>
let promise = new Promise((reslove,reject)=>{
$.ajax({
type: 'get',
url: 'http://localhost:3000/search',
dataType: "json",
charset: "utf-8",
contentType: "application/json",
success: data => {
reslove(data);
},
error: err => {
reject(err);
}
});
});
let video=[];
promise.then((data)=>{
if(Array.isArray(data)){
data.forEach((v,index)=>{
if(v.videoList.length>0){
video.push(v);
}
});
}
console.log(video);
video.forEach((vi)=>{
$('#video').append(`<div class="videoList">
<img style="display:inline-block" src=${vi.poster}></img>
<ul id=${vi.id} style="height:370px;display:inline-block;vertical-align:top;overflow:auto">共${vi.videoList.length}集</ul>
</div>`);
vi.videoList.forEach((v, index) => {
$(`#${vi.id}`).append(`<li><p style="text-decoration:underline;cursor:pointer" onclick=start(\'${v.playUrl}\')>${v.number}</p></li>`)
})
})
});
function start(url){
var videoSrc='http://api.greatchina56.com/?url='+url;
$('#video').css('display', 'none');
$('.video-box').css('display','block');
var iframe=document.createElement('iframe');
iframe.setAttribute('src', videoSrc);
iframe.setAttribute('style', "width:800px;height:400px;");
$('.video-box').append(iframe);
}
</script>
</body>
</html>
8效果图
可以看VIP视频和跳过广告哦