使用sed、awk、grep提取HTML<p>标签内容遇问题求助
提取HTML
标签内容的问题排查与解决
现有命令失效的原因
sed -n '/<p>/,<\/p>/p' FILE返回整个文件:sed的区间匹配是按行生效的,要是你的<p>和</p>不在同一行,或者标签带属性(比如<p class="test">)导致匹配不上,sed就会把整个文件都当作匹配区间输出。而且sed默认是从第一个匹配的<p>到最后一个匹配的</p>全选,逻辑不符合需求。- 你的awk命令返回反结果:原脚本里
/<p>/{flag=1; next}是遇到<p>就跳过当前行,然后只打印flag=1的行,这相当于把<p>到</p>之间的内容之外的部分打出来了,逻辑搞反了,而且同样没考虑标签带属性的情况。 - 正则
<\s*p(\s+.*?>|>).*?<\s*/\s*p\s*>本地无效:GNU sed默认用基础正则(BRE),这个正则里的+、?是扩展正则语法,得加-r参数启用,而且sed默认逐行处理,标签跨行的话.*?匹配不了换行。
可行的解决方法
1. 适配GNU sed的跨行处理方案
如果<p>内容可能跨行,先用-z让sed读取整个文件,再配合扩展正则:
# 提取所有<p>标签内的内容,输出合并为一段 sed -z -r 's/.*?<\s*p(\s+[^>]+)?>(.*?)<\s*\/\s*p\s*>.*/\2/g' FILE # 每个<p>内容单独输出,过滤空行 sed -z -r 's/[^<]*<\s*p(\s+[^>]+)?>(.*?)<\s*\/\s*p\s*>/\2\n/g' FILE | grep -v '^$'
2. 修正后的awk脚本
解决标签带属性、跨行的问题,同时正确输出标签内内容:
awk ' /<\s*p(\s+[^>]+)?>/{ flag=1 sub(/.*<\s*p(\s+[^>]+)?>/, "") if ($0 != "") print $0 next } /<\s*\/\s*p\s*>/{ flag=0 sub(/<\s*\/\s*p\s*>.*$/, "") if ($0 != "") print $0 next } flag' FILE
3. 用Perl处理更省心
Perl对跨行正则和复杂匹配支持更好,适合HTML这类格式:
perl -0777 -ne 'while (/<\s*p(\s+[^>]+)?>(.*?)<\s*\/\s*p\s*>/gs) { print "$2\n" }' FILE
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

