我正在寻找一种从命令行处理HTML代码的方法(可能使用XPATH)。
例如,我想在.container
类中删除,或者在<div>
类之后添加新的.container
。
输入:
<div class="bg-detail2" id="geometry">
<div class="container">
<h2>Title</h2>
<div class="line"></div>
<div class="fix"></div>
<div class="col50">
Content
</div>
<div class="col50">
Another Content
</div>
</div>
</div>
输出:
<div class="bg-detail2" id="geometry">
<div class="container">
<div class="newdiv>
<div class="line"></div>
<div class="fix"></div>
<div class="col50">
Content
</div>
<div class="col50">
Another Content
</div>
</div>
</div>
</div>
我的第一个想法是使用sed
,但这不是防弹方法。我知道xmllint
,但是它只能读取HTML文件。
命令行还有其他工具吗?
答案 0 :(得分:2)
我找不到能做您想要的程序。所以我做了一个。 现在可以使用了!
#!python3
from html.parser import HTMLParser
class HTMLPass(HTMLParser):
def __init__(self, *a, convert_charrefs=False, **k):
super().__init__(*a, convert_charrefs=convert_charrefs, **k)
def handle_starttag(self, tag, attrs):
print(end=self.get_starttag_text())
@staticmethod
def handle_endtag(tag):
print(end="</" + tag + ">")
handle_startendtag = handle_starttag
@staticmethod
def handle_data(data):
print(end=data)
@staticmethod
def handle_entityref(name):
print(end="&"+name+";")
@staticmethod
def handle_charref(name):
print(end="&#"+name+";")
@staticmethod
def handle_comment(data):
print(end="<!--"+data+"-->")
@staticmethod
def handle_decl(decl):
print(end="<!"+decl+">")
@staticmethod
def handle_pi(data):
print(end="<?"+data+">")
unknown_decl = handle_decl
class HTMLPassMod(HTMLPass):
def __init__(self, *a, argv=None, **k):
super().__init__(*a, **k)
self.stack = []
self.args = debugremoveme = []
if argv is None:
import sys
argv = sys.argv[1:]
for arg in argv:
# Horrible string parsing
# Should turn "/a#link-1.external/d" into
# [d, ['a', ('id', 'link-1'), ('class', 'external')]]
sel, act = arg[1:].split(arg[0])
self.args.append([act])
for selector in sel.split(">"):
self.args[-1].append([])
selector = selector.strip()
if "." not in selector and "#" not in selector:
self.args[-1][-1].append(selector)
continue
if "." not in selector:
self.args[-1][-1][:] = selector.split("#")
self.args[-1][-1][1:] = zip(["id"]*(len(self.args[-1][-1])-1), self.args[-1][-1][1:])
continue
if "#" not in selector:
self.args[-1][-1][:] = selector.split(".")
self.args[-1][-1][1:] = zip(["class"]*(len(self.args[-1][-1])-1), self.args[-1][-1][1:])
continue
if selector.index(".") < selector.index("#"):
tag, selector = selector.split(".", maxsplit=1)
selector = "." + selector
else:
tag, selector = selector.split("#", maxsplit=1)
selector = "#" + selector
self.args[-1][-1].append(tag)
while selector:
if "#" not in selector:
self.args[-1][-1].extend(zip(["class"]*len(selector), selector.split(".")))
break
if "." not in selector:
self.args[-1][-1].extend(zip(["id"]*len(selector), selector.split("#")))
break
if selector[0] == ".":
if "." not in selector[1:] or selector.index("#") < selector.index("."):
axa, selector = selector[1:].split("#", maxsplit=1)
else:
axa, selector = selector[1:].split(".", maxsplit=1)
self.args[-1][-1].append(("class", axa))
else:
if "#" not in selector[1:] or selector.index(".") < selector.index("#"):
axa, selector = selector[1:].split(".", maxsplit=1)
else:
axa, selector = selector[1:].split("#", maxsplit=1)
self.args[-1][-1].append(("id", axa))
def handle_starttag(self, tag, attrs):
if self.stack and self.stack[-1][2] is not None and self.stack[-1][2][0] == 'k':
# kill means kill
self.stack.append((tag, attrs, None))
return
self.stack.append((tag, attrs, None))
for arg in self.args:
for frame, a in zip(self.stack[::-1], arg[:0:-1]):
a_tag = a[0].replace("*", "").strip()
if a_tag and frame[0] != a_tag:
break
for attr, val in frame[1]:
if attr == "class":
frame_classes = val.split()
break
else:
frame_classes = []
for attr, val in a[1:]:
if attr == "class":
if val not in frame_classes:
break
else:
for a, v in frame[1]:
if a == attr and v == val:
break
else:
break
else:
continue
break
else:
self.stack[-1] = (tag, attrs, arg[0])
if arg[0][0] in "drk": # delete / replace / kill
if arg[0][0] == "r":
print(end=arg[0][1:])
return
if arg[0][0] == "i": # insert (inside / after)
super().handle_starttag(tag, attrs)
print(end=arg[0][2:].split(arg[0][1])[0])
break
else:
super().handle_starttag(tag, attrs)
def handle_startendtag(self, tag, attrs):
self.handle_starttag(tag, attrs)
self.stack.pop()
def handle_endtag(self, tag):
if self.stack[-1][0] != tag:
# TODO: Implement proper HTML-isn't-XML behaviour
pass
frame = self.stack.pop()
if frame[2] is None:
return super().handle_endtag(tag)
if frame[2][0] in "drk": # delete / replace / kill
return
if frame[2][0] == "i":
super().handle_endtag(tag)
print(end=frame[2][2:].split(frame[2][1])[1])
def handle_data(self, data):
if self.stack and self.stack[-1][2] is not None and self.stack[-1][2][0] == 'k':
return
super().handle_data(data)
def handle_entityref(self, name):
if self.stack and self.stack[-1][2] is not None and self.stack[-1][2][0] == 'k':
return
super().handle_entityref(name)
def handle_charref(self, name):
if self.stack and self.stack[-1][2] is not None and self.stack[-1][2][0] == 'k':
return
super().handle_charref(name)
def handle_comment(self, data):
if self.stack and self.stack[-1][2] is not None and self.stack[-1][2][0] == 'k':
return
super().handle_comment(data)
def handle_decl(self, decl):
if self.stack and self.stack[-1][2] is not None and self.stack[-1][2][0] == 'k':
return
super().handle_data(decl)
def handle_pi(self, data):
if self.stack and self.stack[-1][2] is not None and self.stack[-1][2][0] == 'k':
return
super().handle_pi(data)
def unknown_decl(self, data):
if self.stack and self.stack[-1][2] is not None and self.stack[-1][2][0] == 'k':
return
super().unknown_decl(data)
def run(pass_through=HTMLPassMod):
x = pass_through()
while True:
try:
i = input()
except EOFError:
break
x.feed(i + '\n')
x.close()
if __name__ == "__main__":
run()
此代码糟糕,但实际上在许多情况下都可以正常运行。
用法示例:
wizzwizz4@wizzwizz4Laptop:~$ cat example_input.html
<div class="bg-detail2" id="geometry">
<div class="container">
<h2>Title</h2>
<div class="line"></div>
<div class="fix"></div>
<div class="col50">
Content
</div>
<div class="col50">
Another Content
</div>
</div>
</div>
wizzwizz4@wizzwizz4Laptop:~$ <example_input.html ./rubbish_program.py ~div.newdiv~r<h2>Title</h2>
<div class="bg-detail2" id="geometry">
<div class="container">
<h2>Title</h2>
<div class="line"></div>
<div class="fix"></div>
<div class="col50">
Content
</div>
<div class="col50">
Another Content
</div>
</div>
</div>
wizzwizz4@wizzwizz4Laptop:~$ cat example_input_2.html
<div class="bg-detail2" id="geometry">
<div class="container">
<h2>Title</h2>
<div class="line"></div>
<div class="fix"></div>
<div class="col50">
Content
</div>
<div class="col50">
Another Content
</div>
</div>
</div>
wizzwizz4@wizzwizz4Laptop:~$ <example_input_2.html ./rubbish_program.py 'Jdiv.containerJi~<div class="newdiv">~</div>' '\.container > h2\k'
<div class="bg-detail2" id="geometry">
<div class="container"><div class="newdiv">
<div class="line"></div>
<div class="fix"></div>
<div class="col50">
Content
</div>
<div class="col50">
Another Content
</div>
</div></div>
</div>
./rubbish_program.py [argument...]
其中argument
的格式为:
<separator><selector><separator><instruction>
其中:
separator
是一个单个字符,不能出现在selector
或instruction
中。selector
是一系列类似于tag.class1.class2#id.class3
的事物,其中只能有一个#id
,而tag
是可选的,并且可以有无限数量的{{ 1}},以.classn
分隔。例如:>
。 div#geometry > .container > h2
是以下形式的指令:
instruction
其中<command><parameters>
是以下之一:
command
–删除元素而不删除其子元素。不接受任何参数。d
–用r
替换开始标签,并删除结束标签,而不删除元素的子元素。 parameters
–取决于标签是否是自动闭合的,具有两种不同的行为。
i
的格式为:
parameters
<separator2><first parameter><separator2><second parameter>[<separator2>discarded]
不能出现在任何一个参数中,并且必须与separator2
不同。在单独的调用中它可以具有不同的值。
separator
–删除元素及其子元素。不接受任何参数。答案 1 :(得分:1)
如果可以避免,请do not parse HTML with regular expressions。
相反,尝试使用带有节点,Python等的HTML解析器。
如果已安装docker,则可以尝试以下简单脚本:
docker run --rm -i phil294/jquery-jsdom '$("#geometry h2").remove(); $("#geometry").append("<div class=\"newdiv\"/>"); $("#geometry").prop("outerHTML")' <<< '
<div class="bg-detail2" id="geometry">
<h2>Title</h2>
</div>
'
演示一个简单的删除/附加。 JQuery的强大功能。它与eval()
一起使用jsdom。我托管了here
答案 2 :(得分:0)
sed 's/<div class="container">/&\n <div class="newdiv">/g' file_input.css
这将与sed一起使用,但是正如您所说的,它可能不是防弹的。这也可能导致您的缩进出现问题,但是如果整个过程都一致,则可以使用它...
答案 3 :(得分:0)
首先,您安装此软件包:
sudo apt-get install html-xml-utils
此软件包中有31种工具,下面是它们可以做什么的摘要:
cexport –从C文件创建导出的声明的头文件
hxaddid –将ID添加到所选元素
hxcite-用超链接替换书目参考
hxcite-mkbib-扩展引用并创建书目
hxcopy-在保留相对链接的同时复制HTML文件
hxcount –计算HTML或XML文件中的元素和属性
hxextract –提取选定的元素
hxclean –应用启发式方法更正HTML文件
hxprune –从HTML文件中删除标记的元素
hxincl-扩展包含的HTML或XML文件
hxindex –创建按字母顺序排序的索引
hxmkbib –从模板创建书目
hxmultitoc-为一组HTML文件创建目录
hxname2id-将一些ID =或NAME =从A元素移到其父元素
hxnormalize –漂亮地打印HTML文件
hxnum – HTML文件中的数字部分标题
hxpipe-将XML转换为更易于使用Perl或AWK解析的格式
hxprintlinks-编号链接并在HTML文件末尾添加URL表
hxremove-从XML文件中删除选定的元素
hxtable转置HTML或XHTML表
hxtoc –在HTML文件中插入目录
hxuncdata –用字符实体替换CDATA节
hxunent –将HTML预定义字符实体替换为UTF-8
hxunpipe-将管道的输出转换回XML格式
hxunxmlns –用XML命名空间前缀替换“全局名称”
hxwls –列出HTML文件中的链接
hxxmlns –用“全局名称”替换XML命名空间前缀
asc2xml,xml2asc-在UTF8和实体之间转换
hxref –生成交叉引用
hxselect-提取与(CSS)选择器匹配的元素
您需要操纵html文件或xml文件的所有工具。如您所愿。
hxprune示例:
hxprune -c容器index.html> index2.html
您可以选择html选择器,在这种情况下,该类是“ -c容器”类,然后将要操作的文件的名称传递给它,最后使用此运算符“>”,您可以重定向输出hxprune到另一个文件。 在输出中,您将剪切html树的.container分支。