有时候需要拦截网页的某个请求,收集响应数据,单个请求直接操作就好了;如果是多个相同 API 的请求,如果有个批处理数据的工具岂不美哉。于是,它来了。本来打算弄成一个油猴插件,但是浏览器总是会把插件的打印日志给 “吞掉”,那就先直接在控制台运行脚本,更简单明了。
介绍
用到了一个插件 ajaxhook,官方是这样介绍的:
在
XMLHttpRequest
对象发起请求之前、收到响应内容之后以及发生错误时获得处理权,通过它你可以提前对请求、响应以及错误进行一些预处理。
要想在网站上用其他的脚本,首先需要将它引入,这里是通过动态创建 script
标签的方式,将这个标签插入到当前页面中,然后浏览器会自动获取这个 JS
资源。
const script = document.createElement("script")
script.setAttribute("type", "text/javascript")
script.setAttribute(
"src",
"https://unpkg.com/ajax-hook@2.1.3/dist/ajaxhook.min.js"
)
document.documentElement.appendChild(script)
创建完标签还不行,还要检测当前是否已经引入完成了。引入完成后,ajaxhook
会在全局定义一个名叫 ah
的变量,那么就可以根据这个变量来判断。
通过在一个 Promise
实例里,定时检测是否 ah !== undefined
来判断。虽然看起来很笨拙,但是很实用 🤭。
new Promise((resolve, reject) => {
try {
var ajaxHookTimer = setInterval(() => {
if (ah !== undefined) {
clearInterval(ajaxHookTimer)
resolve()
}
}, 500)
} catch (e) {
reject(e)
}
})
脚本其余部分都是按照官网案例,可以自行查阅。
脚本
完整的脚本代码如下:
;(function () {
/* --- 收集数据 Start --- */
// 用来收集数据的数组对象
const resData = []
// 需要拦截的请求地址
const url = ""
// 自定义的数据格式化方法
function formatData(data) {
/* 处理 data */
console.log(resData)
}
/* --- 收集数据 End --- */
injectAjaxHook().then(() => {
console.log("开启拦截")
openIntercept()
})
// 注入第三方插件
function injectAjaxHook() {
console.log("拦截准备中...")
const script = document.createElement("script")
script.setAttribute("type", "text/javascript")
script.setAttribute(
"src",
"https://unpkg.com/ajax-hook@2.1.3/dist/ajaxhook.min.js"
)
document.documentElement.appendChild(script)
return new Promise((resolve, reject) => {
try {
var ajaxHookTimer = setInterval(() => {
if (ah !== undefined) {
clearInterval(ajaxHookTimer)
resolve()
}
}, 500)
} catch (e) {
reject(e)
}
})
}
// 开启数据拦截
function openIntercept() {
ah.proxy(
{
// 请求发起前进入
onRequest: (config, handler) => {
handler.next(config)
},
// 请求发生错误时进入,比如超时;注意,不包括http状态码错误,如404仍然会认为请求成功
onError: (err, handler) => {
console.log(err.type)
handler.next(err)
},
// 请求成功后进入
onResponse: (res, handler) => {
try {
const url = res.config.url
if (isUsefulUrl(url)) {
const data = JSON.parse(res.response)
formatData(data)
}
handler.next(res)
} catch (e) {
console.log(e)
}
}
},
window
)
// 是否是有效链接
function isUsefulUrl(testUrl) {
let path = testUrl.match(/(\.\w+)?(\/\w+)+(?=(\?|$))/)
if (path) {
path = path[0]
}
return testUrl.indexOf(url) > -1 || url.indexOf(path) > -1
}
}
})()
示例
以百度图片搜索为例,每次切换图片时,会发起这个请求。
假如需要收集响应数据中的 bfe_log_id
字段。
那么在收集数据部分就可以写成下面这样:
/* --- 收集数据 Start --- */
// 用来收集数据的数组对象
const resData = []
// 需要拦截的请求地址
const url = "https://image.baidu.com/simple/simplesearch"
// 自定义的数据格式化方法
function formatData(data) {
resData.push(data.bfe_log_id)
console.log(resData)
}
/* --- 收集数据 End --- */
打开控制台,粘贴脚本代码,回车。当控制台出现【开启拦截】时,说明拦截器已经加载完毕,就可以发请求了。
大功告成,快去试试吧。