js实现完整页面节点提取
easterling

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;

整个代码非常简单,效率也没有想象中那么低,几乎可以说很好的完成了我们的需求,后续的使用也非常顺手,也许可以在此基础上改一个更强大的工具出来

 Comments
Comment plugin failed to load
Loading comment plugin