js实现完整页面节点提取
最近在工作中遇到了使用js提取页面中的div的需求,由于是在对cheerio做替代方案,所以不能使用cheerio和其他的节点解析器。于是我想到可以使用正则,经过一番寻找,发现了一个名为平衡组的东西,可以实现我想要的效果,但是看了一段时间后才发现js不支持平衡组,找了一些其他的可能的解决方案大都没有我所要的功能,这就非常奇怪,因为感觉这个功能应该还挺有市场,竟然没有找到现成的解决方案(当然我的搜索能力比较有限也是其中一个原因),于是我用一上午自己动手实现了一下。
为了匹配成对的这种标签,我们当然首先想到的是用一个栈来做
大体的思路如下:
首先,函数的参数为pagecontent, dom, feature,pagecontent是原始页面,dom是一个标签类型,比如div、span等,feature是一个独一无二的特征,比如id=“abc”,class=“starfish”等,有了这两者,就可以确定我们要找哪一个节点。
然后我们要对开头和结尾进行精准定位,首先搜索feature可以定位开头,然后将随便什么内容入栈,向后搜索,搜索到标签开头则入栈,遇到标签结尾则出栈,最终栈长度归0时就是结尾的位置,将这开头和结尾之间的部分截取下来就是一个完整的目标节点。
主体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| const fs = require('fs'); const Stack = require("./stack")
exports.getdiv = async function (pagecontent, dom, feature) { let hasdiv = pagecontent.indexOf(feature) if (hasdiv > 0) { let feature_position = hasdiv + feature.length let start = pagecontent.substring(0, feature_position).lastIndexOf("<" + dom) let stack = new Stack() stack.push("<") let find_position = start + 1 + dom.length let findcontent while (stack.length() > 0) { findcontent = pagecontent.substring(find_position,) find_position += await findnextdom(findcontent, stack, dom) } let targetdom = pagecontent.substring(start, find_position + 1)
return targetdom } else { return "" } }
async function findnextdom(findcontent, stack, dom) { let left_pos = findcontent.indexOf("<" + dom) let right_pos = findcontent.indexOf("</" + dom) if (left_pos < right_pos) { stack.push("<") return left_pos + 1 + dom.length } else if (left_pos > right_pos) { stack.pop() return right_pos + 2 + dom.length } }
|
栈相关的代码是网上找的一段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| const Stack = function () { let arr = [];
function push(element) { arr.push(element); } function pop() { return arr.pop(); } function top() { return arr.length > 0 ? arr[arr.length - 1] : null; } function clear() { arr = []; } function length() { return arr.length; } this.push = push; this.pop = pop; this.top = top; this.clear = clear; this.length = length; } module.exports = Stack;
|
整个代码非常简单,效率也没有想象中那么低,几乎可以说很好的完成了我们的需求,后续的使用也非常顺手,也许可以在此基础上改一个更强大的工具出来