W jaki sposób policzyć częstość występowania poszczególnych tagów HTML ?

0

Cześć, mam napotkałem powyższy problem, googluje kilka dni i nie znalazłem sensownego rozwiązania ... Tak jak w pytaniu chodzi o policzenie ilości występowania poszczególnych tagów HTML w Stringu. A potem posortowania ich według ich częstości występowania. Na stackoverflow znalazłem coś takiego, taki parser wykorzystujący bibliotekę jsoup.


Map<String, Long> counts = new HashMap<>();
String html = " your html string goes here ";    

Document doc = Jsoup.parse(html);

Elements elements = document.body().select("*");
recursiveWalk(elements, counts);

// your map here, sort it

// method to walk the document
private void recursiveWalk(List<Element> elements, Map<String, Long> counts) {
    for (Element el : elements) {
        String tag = el.tagName();
        long number = counts.getOrDefault(tag, 0L) + 1;
        counts.put(tag, number);
        recursiveWalk(elements.children(), counts);
    }
}

Problem z powyższym rozwiązaniem polega na tym, że jeśli zawartość strony zawiera jakąś nazwę znacznika, na przykład "div" albo "body" to również to jest liczone jako znacznik. Podczas gdy to stanowi tylko część jakiegoś zdania napisanego na stronie...
Będę wdzięczny za pomoc :)

0

Nie znam się dobrze na Javie, ale chyba najprościej to można zrobić dodając do zwróconej nazwy elementu znaki < oraz > i dopiero potem liczyć.

0

regex

0

Jsoup parsuje escapowane znaki, czyli jak na stronie wyświetlany jest

<div>

to w źródle strony wygląda to tak

&lt;div&gt;

Problemem jest właśnie to, że zamienia sobie wyescapowane znaki i dlatego je Tobie zlicza. Rozwiązaniem powinno być

String html = " your html string goes here ".replaceAll("&([^;]+?);", "**$1;"); 
 

W tym momencie wyescapowane znaki będą poprzedzone podwójną gwiazdką, jeżeli na końcu chcesz ponownie uzyskać poprawny rezultat, bez gwiazdek, trzeba znów zrobić replaceAll

doc.outerHtml().replaceAll("\\*\\*([^;]+?);", "&$1;")
0

No właśnie String który chce sparsować wgląda mniej więcej tak:

<div class="content__holder"> <h2>Zadanie stażowe</h2> <h3>01_Zadanie</h3> <div class="container desktop-3-top"> <div class="desktop-12 laptop-12 tablet-12 phone-12 align--left list-icon-lin.....

Kod który udało mi się napisać:

public class HTMLCounter
{
    private Map<String, Long> counts = new HashMap<>();
    private Document document;
    Elements elements;

    public HTMLCounter( String htmlString)
    {
        System.out.println(htmlString);

        htmlString.replaceAll("&([^;]+?);", "**$1;");
        document = Jsoup.parse(htmlString);
        document.outerHtml().replaceAll("\\*\\*([^;]+?);", "&$1;");
        elements = document.body().select("*");
    }

    public void countAndSortTags()
    {
        recursiveWalk(elements, counts);
        System.out.println(counts);

    }

    private void recursiveWalk(List<Element> elements, Map<String, Long> counts)
    {
        for (Element el : elements)
        {
            String tag = el.tagName();
            long number = counts.getOrDefault(tag, 0L) + 1;
            counts.put(tag, number);
            recursiveWalk(el.children(), counts);
        }
    }
}

Wynik działania:

{div=36, p=35, br=12, h2=3, h3=6, body=1}

No ale sami widzicie że policzyło 1 raz znacznik body, w jakimś zdaniu znajdującym się na stronie

1

Zacząłbym od regexa w stylu: <([^/ \r\n]*) + jakieś special casy na komentarze, case sensitivity itp.. Pobaw się...
Jak chcesz się bardziej pobawić to bierzesz dowolny xmlreader/htmlreader i czytasz, jeśli napotkałeś na element to czytasz nazwe, dodajesz i tyle. Regex jest o tyle dobry, że jest bardziej odporny na źle sformatowane htmle i prawdopodobnie nie ma znaczenia czy pomylisz się o 1 czy 2 tagi. Jeśli ma to znaczenie to lepiej użyj parsera, jako że lepiej obsłuży special casy, a speca raczej nikomu nie chce się czytać spec-a i bawić regexy w takim przypadku.

0

dałem w komencie, ale niech się inni wypowiedzą:

gdybyś jeno znał htmla to byś wiedział, że na jedną stronę może być jedno <body>

0

Poprawka, powinno być

 htmlString = htmlString.replaceAll("&([^;]+?);", "**$1;");

Bo wcześniej ta linijka nic nie robiła.
No ale w dalszym ciągu jest tak jak było

To mi zwraca string ale co mam z tym zrobić ?

document.outerHtml().replaceAll("\\*\\*([^;]+?);", "&$1;");  

Dokument jest już sparsowany i htmlString = htmlString.replaceAll("&([^;]+?);", "**$1;");

Już policzone ( błędnie) tagi mam w:

Map<String, Long> counts = new HashMap<>();
1

@BuxBleed: dodaję odpowiedź poniżej, bo jak zaczniemy robić to w komentarzach to później nikt się nie dogrzebie :)

Wybiera całą zawartość dokumentu

przypuszczam, że to działa w ten sposób:
wybrałeś elements = document.body().select("*"); więc to już wskazuje tej bibliotece/parserowi, że Document posiada <body> i dlatego może to zliczać

nie wiem co to za klasa Elements, ale zdaje mi się, że bazuje ona na specyfikacji w3, jak jesteś dociekliwy i nie masz przyjaciół, polecam specyfikacje HTML/CSS: https://www.w3.org

poza tym, zdaje mi się, że ten jsoup daje radę, nie przejmuj się tym, że masz dodatkowo policzone <body>, po prostu je pomiń w wyniku :P

0

Dzięki wszystkim za pomoc ! Problem został rozwiązany. Program zdaje się działać poprawnie, po prostu na sam koniec usuwam policzony znacznik <body> który jest wynikiem działania:

    public HTMLCounter(String htmlString)
    {
        Document document = Jsoup.parse(htmlString);
        elements = document.body().select("*");
    }

Całość kodu, póki co bez sortowania wgląda tak:

public class HTMLCounter
{
    private Map<String, Long> counts = new HashMap<>();
    private Elements elements;

    public HTMLCounter(String htmlString)
    {
        Document document = Jsoup.parse(htmlString);
        elements = document.body().select("*");
    }

    public void countHTMLTags()
    {
        for (Element el : elements)
        {
            String tag = el.tagName();
            long number = counts.getOrDefault(tag, 0L) + 1;
            counts.put(tag, number);
        }
        counts.remove("body");
        System.out.println(counts);
    }
}
0

xpath & count()

1 użytkowników online, w tym zalogowanych: 0, gości: 1