从命令行操作,处理HTML

时间:2019-02-14 22:40:52

标签: html linux command-line-interface

我正在寻找一种从命令行处理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文件。

命令行还有其他工具吗?

4 个答案:

答案 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是一个单个字符,不能出现在selectorinstruction中。
  • 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分支。