linux 从命令行操作、处理HTML

ef1yzkbh  于 2023-03-22  发布在  Linux
关注(0)|答案(4)|浏览(205)

我正在寻找一种方法来处理一个HTML代码从命令行(可能使用XPATH)。
例如,我想删除.container类或在.container类之后添加新的<div>

输入:

<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文件。
是否有其他工具可用于命令行?

zz2j4svz

zz2j4svz1#

我找不到一个程序来做你想要的。所以我做了一个。现在它工作了!

#!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的东西,其中只能有一个#idtag是可选的,可以有无限数量的.classn,由>分隔。示例:div#geometry > .container > h2.
  • instruction是以下形式的指令:
<command><parameters>

其中command是以下之一:

  • d-删除元素而不删除其子元素。不带参数。
  • r-用parameters替换开始标签,并删除结束标签,但不删除元素的子元素。
  • i-有两个独立的行为,取决于标签是否自关闭。
  • 如果它不是自动关闭的,则在内容前面加上第一个参数,在内容后面加上第二个参数。
  • 如果它是自关闭的,则将第一个参数紧接着标记插入,并忽略后续参数。

parameters的形式为:

<separator2><first parameter><separator2><second parameter>[<separator2>discarded]

separator2不能出现在任何一个参数中,并且必须与separator不同。它可以在单独的调用中具有不同的值。

  • k-删除元素及其子元素。不接受任何参数。
0kjbasz6

0kjbasz62#

如果可以避免,请不要使用正则表达式解析HTML。
相反,尝试使用node,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>

'

演示了一个简单的remove / append。JQuery的强大功能。它使用jsdom和eval()。我托管了它here

7xllpg7q

7xllpg7q3#

sed 's/<div class="container">/&\n      <div class="newdiv">/g' file_input.css

这将与sed一起工作,但正如你所说,它可能不是防弹的。这也可能导致缩进问题,但如果它始终一致,你可以使用它...

jv4diomz

jv4diomz4#

首先,安装这个包:
sudo apt-获取安装html-xml-实用程序
此软件包中有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文件创建目录
  • hxname 2 id-将一些ID=或NAME=从A元素移动到其父元素
  • hxnormalize -漂亮地打印HTML文件
  • hxnum -HTML文件中节标题的编号
  • hxpipe-将XML转换为更易于用Perl或AWK解析的格式
  • hxprintlinks-在HTML文件的末尾添加链接和URL表
  • hxremove-从XML文件中删除选定的元素
  • hxtabletrans-转置HTML或XHTML表
  • hxtoc -在HTML文件中插入目录
  • hxuncdata -用字符实体替换CDATA节
  • hxunent -将HTML预定义字符实体替换为UTF-8
  • hxunpipe-将管道输出转换回XML格式
  • hxunxmlns -将“全局名称”替换为XML命名空间前缀
  • hxwls -列出HTML文件中的链接
  • hxxmlns -将XML命名空间前缀替换为“全局名称”
  • asc 2xml,xml 2asc-在UTF8和实体之间转换
  • hxref -生成交叉引用
  • hxselect-提取匹配(CSS)选择器的元素

这里有你需要的所有工具来操作一个html文件或xml文件。如你所愿。
示例hxprune:
hxprune -c container index.html〉index2.html
你可以选择你的html选择器,在这个例子中,是一个类“-c container”,然后你把你想要操作的文件名传递给它,最后用这个操作符“〉”你可以把hxprune的输出重定向到另一个文件,在输出中你将剪切html树的.container分支。

相关问题