Манипулировать, обрабатывать HTML из командной строки - PullRequest
0 голосов
/ 15 февраля 2019

Я ищу способ обработки 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 голосов
/ 24 февраля 2019

Если этого можно избежать, не анализируйте HTML с помощью регулярных выражений .

Вместо этого попробуйте анализатор HTML с узлом, Python и т. Д.

Если у вас установлен докер,Вы можете попробовать этот простой скрипт:

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 в ваших руках.Использует jsdom с eval().Я принимал это здесь

0 голосов
/ 20 февраля 2019

Я не смог найти программу, которая бы делала то, что вы хотели.Итак, я сделал один. И теперь это работает!

#!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 является необязательным, и может быть неограниченное количество .classn s, разделенных >.Пример: div#geometry > .container > h2.
  • instruction - это инструкция вида:

    <command><parameters>
    

    , где command - одно из следующего:

    • d - удаляет элемент, не удаляя его дочерние элементы.Не принимает параметров.
    • r - заменяет начальный тег на parameters и удаляет конечный тег, не удаляя дочерние элементы.
    • i - имеет дваотдельные поведения, в зависимости от того, является ли тег самозакрывающимся.

      • Если он не является самозакрывающимся, префикс содержимого с первым параметром и суффикс содержимого вторым параметром.
      • Если он самозакрывающийся, вставляет первый параметр сразу после тега и игнорирует последующие параметры.

      parameters имеет вид:

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

      separator2 не должно встречаться ни в одном из параметров, и должно отличаться от separator.Он может иметь разные значения в отдельных вызовах.

    • k - удаляет элемент и его дочерние элементы.Не принимает параметров.
0 голосов
/ 23 февраля 2019

Прежде всего, вы устанавливаете этот пакет:

sudo apt-get install html-xml-utils

В этом пакете 31 инструмент, здеськраткое описание того, что они могут сделать:

  • cexport - создать заголовочный файл экспортированных объявлений из файла C

  • hxaddid - добавить идентификаторы к выбранным элементам

  • 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- номер ссылки и добавление таблицы URL-адресов в конце файла HTML

  • hxremove- удаление выбранных элементов из файла XML

  • hxtabletrans- транспонировать таблицу HTML или XHTML

  • hxtoc - вставить оглавление в файл HTML

  • hxuncdata -заменить разделы CDATA символьными объектами

  • hxunent - заменить предопределенный символ HTMLes в 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 container», затем вы передаете ему имя файла, которым вы хотите манипулировать, и, наконец, с помощью этого оператора «>» вы можете перенаправить вывод hxprune в другой файл.В выходных данных вы обрежете ветку .container дерева html.

0 голосов
/ 15 февраля 2019
 sed 's/<div class="container">/&\n      <div class="newdiv">/g' file_input.css

Это будет работать с sed, но, как вы говорите, не может быть пуленепробиваемым.Это также может вызвать проблемы с вашим отступом, но если оно постоянно, вы можете использовать его ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...