首先我们看下数据来源:
来源于这个网站:https://z1.m1907.cn/
可以说这个网站上能找到很多你想看的很多电影或电视剧,最重要的是很多电影电视剧在别的网站是收费的,但是在这里看是免费的,之前也经常在这个网站中看。
不过这个网站有些缺点:在微信中被屏蔽了网址。在夸克浏览器上如果播放到某集,夸克播放器就会覆盖掉原生播放器,导致切换下一集时不好切换过去,因此,希望能开发一个自己的网站,获取该网站的数据来呈现。
一般来说,我们只需要拿到这些数据的url接口就行了。但是看了这个网站的network请求,发现这个接口的某个参数是可变的,而且还是必须要的。
这个url就算获取视频列表的接口,但是中间的z参数是必填的,而且每过一段时间就需要更换参数。
这个参数它不来源于上一个接口的某个结果,它是通过js进行了md5之后生成的。这就难办了。怎么去获取这个值呢?
我们点击这里,然后点击这个小图标
断点发现,生成z参数的就是这个p变量。
然后,因为内部代码已被压缩,所以不好理清楚里面的逻辑了,所以就采用了fiddler抓包工具,将这个js文件进行代理到本地js中。篡改js文件做一些外加功能。
我使用fiddler代理篡改了这段代码,就是将这个z参数显示在dom中。
因此,dom中就有了这么一个dom元素,那么这有什么用呢?这样我就能在自己的服务器中拿到这个值?
是的,我使用的是nodejs。前段时间在网上找到了一个有意思npm包,用来在服务器中模拟浏览器操作,自然在服务器中就能获取到浏览器中渲染的dom了。那就是标题里说的puppeteer。
因为之前用fiddler代理将这个只放在了dom中,因此我们也就可以使用puppeteer模块从dom中拿到这个值,曲线救国。
请看实现(使用koajs服务端,ctx.response.body
即可输出这个z参数)
const puppeteer = require('puppeteer'); /** * 获取https://z1.m1907.cn/的动态z 需求开fiddler */ module.exports = async(ctx) => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://z1.m1907.cn/?jx=bilibili'); //注入脚本 const dimensions = await page.evaluate(async() => { let z = '' if (document.querySelector('#z')) { z = document.querySelector('#z').innerText; } return { z, } }); await browser.close(); ctx.response.body = dimensions; return dimensions;//这个return是给下一个接口调用的 }
page.evaluate
可以将浏览器的js代码注入到dimensions的隐藏浏览器中。就能通过document.querySelector('#z').innerText
拿到那个z参数,然后通过node返回了。
拿到了。这个数据一般能使用几个小时,几个小时后又需要重新获取新的值了,重新执行接口即可。
拿到这样要获取数据就容易多了。
看后续写法:
const { loadPage } = require('../../utils/utils'); const getId = require('./getId'); const setting = require('./setting'); /** * 主程序 */ const videos = async(ctx) => { const { title = 'bilibili', z = null } = ctx.query // console.log(setting.z) z && (setting.z = z); //手动输入z参数 const content = await loadPage(`https://a1.m1907.cn/api/v/?z=${setting.z}&jx=${title}&s1ig=11402&g=`); if (content.includes('获取json版api地址')) {//获取数据错误 重新获取z参数 const obj = await getId(ctx); console.log(obj) setting.z = obj.z await videos(ctx) return; } ctx.response.body = content; } module.exports = videos;
loadPage
是封装的请求页面的方法,getId
是之前用于返回z参数的方法,setting
是用于储存获取到的z参数,失效了才重新获取。
这样就能返回数据了。
访问接口,拿到当前电视剧/电影的所有剧集的m3u8播放地址,这样在支持m3u8的播放器中就可以直接播放了。
(完)