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();//由于浏览器会打开一个空白的页签,所以索引值为2let 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视频和跳过广告哦
