4 回答
TA贡献1824条经验 获得超6个赞
正如其他答案所指出的那样,这是一个复杂的正则表达式,可以针对网页源代码的大部分执行。一种可能的解决方法包括使用 Promises 或 Web Worker 来利用浏览器的异步功能来解冻 UI,但我认为您对具体解决此问题不感兴趣。似乎您正在尝试抓取网络数据,因此在此过程中 UI 是否被冻结都不会产生影响。
我的建议是分而治之。让我们分别处理每个选择器和它们。
script[type="application/json"]
这似乎很简单。您可能只需要获取其内部内容,瞧,您就有了一个 JSON。
div[class*="json"]
我相信这是一种非标准的方式来指定网页的初始状态。它可能会落入与上述相同的解析器。您可能只需要获取其内部文本并尝试将其解析为 JSON。
script[type="text/javascript"]
这是最棘手的部分,因为我们不再处理 JSON,而是处理可能包含或不包含 JSON 数据的可执行 JavaScript。对于这个,您可以使用简化的正则表达式,但我会更进一步并提出其他建议。
您可以检查 JavaScript 对象并尝试将它们转换为 JSON。这可以通过内置 API 或使用 JavaScript 解析器轻松完成(例如,如果您使用的是 Scrapy 之类的东西,则可以使用 js2py)。我不确定这项任务的性能,但我相信它会比复杂的正则表达式更快,可能值得一试。
它适用于类似的情况,var initialState = { ... };
但在尝试处理像 . 这样的内联值时可能会带来一些挑战hypedFramework.init({ ... })
。在后一种情况下,您可能需要一些 JavaScript 解析来隔离这些值。但这仍然是可能的。快速浏览一下https://esprima.org/demo/parse.html,看看它是如何从函数参数中提取对象表达式的。
TA贡献1876条经验 获得超6个赞
正则表达式运行时是非多项式的,这意味着对于复杂的模式,它可能需要一段时间!!你有两个选择;要么在主线程之外运行正则表达式,以确保页面保持响应,要么找到一种更有效的方法来实现您尝试使用正则表达式做的事情,或者至少找到一个更好的(CPU 密集度较低的)正则表达式;
对于第一个选择,您可以使用web workers,这是一个干净的解决方案,或者您可以使用 kinda hacky workaround 并使用setTimeout()或使用 promise;但我强烈建议您使用网络工作者,如果它的浏览器支持适合您的用例(谁关心 IE?)
下面是一个Promise利用 CPU 密集型任务远离主线程的示例:
const inefficientPattern = /({(?:\s|\n)*(?:"|')(?:\s|\n|.)*?(?:"|'):(?:\s|\n)*(?:"|'|\[|{)(?:\s|\n|.)+?})(?:;|$)/g;
for (let el of document.querySelectorAll(['div[class*="json"]', 'script[type="text/javascript"]', 'script[type="application/json"]'])) {
if (el.innerHTML) {
new Promise( function (resolve, reject) {
resolve(el.innerHTML.match(inefficientPattern))
}).then( matches => {
console.log(matches)
})
}
}
有趣的东西:立即执行承诺回调;我错了 👍 查看这个答案:Are JavaScript Promise asynchronous?
TA贡献1863条经验 获得超2个赞
我认为您可以使用 setTimeout 将 for 循环拆分为多个迭代。这样浏览器就可以有时间在每次调用繁重的正则表达式解析之间进行渲染。
const results = [];
let checkAll = (elements) => {
results.push(checkOne(elements[0]));
if (elements.length > 1) {
setTimeout(0, () => checkAll(elements.slice(1)));
} else {
// do something with results ...
}
}
TA贡献1839条经验 获得超15个赞
尝试在 try/catch 组中使用 JSON.parse,就像这样
for (el of document.querySelectorAll(['div[class*="json"]', 'script[type="text/javascript"]', 'script[type="application/json"]'])) {
if (el.innerHTML) {
try {
let matches = JSON.parse(el.innerHTML)
console.log(JSON.stringify(matches))
catch (err) {
console.log('element was not a json')
}
}
}
如果元素的内容确实是有效的 JSON 语法,它将执行而不会抛出错误,否则你可以在 catch 组中做一些特殊的事情
精确
这不会在元素中找到 JSON 位,整个元素需要是有效的 JSON 语法
添加回答
举报