话说,关于正则表达式有一个梗,大意是:
假如你有一个问题,想用正则来解决,于是你就有了两个问题
这句话侧面反映了精通正则是一件不容易的事。比如我今天遇到的诡异事件。
情景回放
这两天练手写了一个爬用户在博客园所有文章阅读量的简单爬虫。可以输出某用户的随笔总阅读量。如在命令行输入:
node index imgss
得到总阅读量是3602.
地址在readcounter;
其中有这么一段代码:
var re = /阅读\((\d+)\)/g;
while(true) {
if(!re.exec(html))//第一次调用
break; //此处会浪费一次re匹配,导致第一个匹配到的从第二个开始。
var match = re.exec(html)[1]; //第二次调用。匹配阅读量数据
console.log(match);
}
现在假设html是下面的字符串
var html=`posted @ 2017-03-17 22:32 nobody-junior 阅读(12) 评论(0) 编辑posted @ 2017-03-05 22:55 nobody-junior 阅读(29) 评论(0) 编辑posted @ 201
7-03-02 22:28 nobody-junior 阅读(588) 评论(4) 编辑posted @ 2017-02-23 18:38 nobody-junior 阅读(58) 评论(0) 编辑posted @ 2017-02-20 21:
52 nobody-junior 阅读(5) 评论(0) 编辑posted @ 2017-02-18 23:19 nobody-junior 阅读(16) 评论(0) 编辑posted @ 2017-02-14 18:45 nobody-jun
ior 阅读(9) 评论(0) 编辑posted @ 2017-02-11 20:24 nobody-junior 阅读(7) 评论(0) 编辑posted @ 2017-01-18 23:16 nobody-junior 阅读(125)
评论(0) 编辑posted @ 2017-01-06 20:38 nobody-junior 阅读(208) 评论(0) 编辑`;
运行的结果如下:
也就是跳过第一个12,匹配了29,然后跳过588,匹配到58
原因分析
没有理解re.exec
的用法。阮大的文章说的很详细RegExp对象;
exec方法返回的是一个数组:比如这里返回的:
数组的第一项是匹配到的字符串,这里是阅读(12),
第二项是re中用()括起来的部分,这里是12,
第三项是原始字符串,这里是html.
以上面的为例:re=/阅读\((\d+)\)/g
,用来匹配字符串中的“阅读()“部分。
在上面的while循环中,调用了两次exec
方法。一次是为了判断匹配是否结束,匹配到之后,这里是”阅读(12)“没有对方法的返回值进行处理,直接进行了下一次调用。
再次调用的时候,就会跳到下一次匹配到的地方,这里是'阅读(29)',所以出现了只匹配偶数的情况。
解决
将循环部分的代码改一下:
while(true) {
var match = re.exec(html); //匹配阅读量数据
if(match)
console.log(match[1]);
else
break;
}
这样将每次exec方法的返回值赋值给match
变量。然后对match
进行匹配,不会让exec
因为判断多执行一次。
这下就完全匹配了。
小爬虫完整代码:见哲理