0

我正在使用Jericho HTML Parser来解析一些格式错误的 html。特别是我试图获取所有文本节点,处理文本然后替换它。

我想跳过处理中的特定元素。例如,我想跳过所有元素,以及任何具有属性 class="noProcess" 的元素。所以,如果一个 div 有 class="noProcess" 那么我想跳过这个 div 和所有子进程。但是,我确实希望这些跳过的元素在处理后返回到输出。

Jericho 为所有节点提供了一个迭代器,但我不确定如何从迭代器中跳过完整的元素。这是我的代码:

private String doProcessHtml(String html) {
        Source source = new Source(html);
        OutputDocument outputDocument = new OutputDocument(source);

        for (Segment segment : source) {
            if (segment instanceof Tag) {
                Tag tag = (Tag) segment;
                System.out.println("FOUND TAG: " + tag.getName());

                // DO SOMETHING HERE TO SKIP ENTIRE ELEMENT IF IS <A> OR CLASS="noProcess"

            } else if (segment instanceof CharacterReference) {
                CharacterReference characterReference = (CharacterReference) segment;
                System.out.println("FOUND CHARACTERREFERENCE: " + characterReference.getCharacterReferenceString());
            } else {
                System.out.println("FOUND PLAIN TEXT: " + segment.toString());
                outputDocument.replace(segment, doProcessText(segment.toString()));
            }
        }

       return outputDocument.toString();
    }

它看起来不像使用 ignoreWhenParsing() 方法对我有用,因为解析器只是将“忽略”元素视为文本。

我在想,如果我可以将 Iterator 循环转换为 for (int i = 0;...) 循环,我可能能够通过修改 i 指向 EndTag 来跳过元素及其所有子元素,然后继续循环....但不确定。

4

3 回答 3

0

通过使用标签的 Element 对象的 getEnd() 方法,设法获得了一个可行的解决方案。这个想法是如果元素的结束位置小于您设置的位置,则跳过元素。因此,您找到要排除的元素的结束位置,并且在该位置之前不处理任何其他内容:

final ArrayList<String> excludeTags = new ArrayList<String>(Arrays.asList(new String[] {"head", "script", "a"}));
final ArrayList<String> excludeClasses = new ArrayList<String>(Arrays.asList(new String[] {"noProcess"}));

Source.LegacyIteratorCompatabilityMode = true;
Source source = new Source(htmlToProcess);
OutputDocument outputDocument = new OutputDocument(source);

int skipToPos = 0;
for (Segment segment : source) {
    if (segment.getBegin() >= skipToPos) {
        if (segment instanceof Tag) {
            Tag tag = (Tag) segment;
            Element element = tag.getElement();

            // check excludeTags
            if (excludeTags.contains(tag.getName().toLowerCase())) {
                skipToPos = element.getEnd();
            }

            // check excludeClasses
            String classes = element.getAttributeValue("class");
            if (classes != null) {
                for (String theClass : classes.split(" ")) {
                    if (excludeClasses.contains(theClass.toLowerCase())) {
                        skipToPos = element.getEnd();
                    }
                }
            }

        } else if (segment instanceof CharacterReference) { // for future use. Source.LegacyIteratorCompatabilityMode = true;
            CharacterReference characterReference = (CharacterReference) segment;
        } else {
            outputDocument.replace(segment, doProcessText(segment.toString()));
        }
    }
}

return outputDocument.toString();
于 2013-06-27T18:14:18.200 回答
0

这应该有效。

String skipTag = null;
for (Segment segment : source) {
    if (skipTag != null) { // is skipping ON?
        if (segment instanceof EndTag && // if EndTag found for the
            skipTag.equals(((EndTag) segment).getName())) { // tag we're skipping
            skipTag = null; // set skipping OFF
        }
        continue; // continue skipping (or skip the EndTag)
    } else if (segment instanceof Tag) { // is tag?
        Tag tag = (Tag) segment;
        System.out.println("FOUND TAG: " + tag.getName());
        if (HTMLElementName.A.equals(tag.getName()) { // if <a> ?
            skipTag = tag.getName(); // set
            continue; // skipping ON
        } else if (tag instanceof StartTag) {
            if ("noProcess".equals( // if <tag class="noProcess" ..> ?
                    ((StartTag) tag).getAttributeValue("class"))) {
                skipTag = tag.getName(); // set
                continue; // skipping ON
            }
        }
    } // ...
}
于 2013-06-27T14:43:14.263 回答
0

我认为您可能需要考虑重新设计您的细分市场的构建方式。有没有办法解析 html,使得每个段都是包含子元素嵌套列表的父元素?这样你就可以做类似的事情:

for (Segment segment : source) {
        if (segment instanceof Tag) {
            Tag tag = (Tag) segment;
            System.out.println("FOUND TAG: " + tag.getName());

            // DO SOMETHING HERE TO SKIP ENTIRE ELEMENT IF IS <A> OR CLASS="noProcess"
            continue;

        } else if (segment instanceof CharacterReference) {
            CharacterReference characterReference = (CharacterReference) segment;
            System.out.println("FOUND CHARACTERREFERENCE: " + characterReference.getCharacterReferenceString());
            for(Segment child : segment.childNodes()) {
                //Use recursion to process child elements
                //You will want to put your for loop in a separate method so it can be called recursively.
            }
        } else {
            System.out.println("FOUND PLAIN TEXT: " + segment.toString());
            outputDocument.replace(segment, doProcessText(segment.toString()));
        }
    }

如果没有更多的代码来检查,就很难确定重组段元素是否可能或值得付出努力。

于 2013-06-27T15:10:13.747 回答