# HTML mit Hilfe Jsoup einlesen



## TheMan (30. Aug 2018)

Guten Tag,

ich bin gerade dran mit Hilfe des Jsoup Parsers einige Informationen aus einer HTML Datei zu extrahieren.

Leider komme ich noch nicht ganz mit der Referenzierung klar. Und zwar möchte ich aus der folgenden Datei folgende Informationen in folgendem Schema am Besten in einer Liste oder Map abspeichern, falls möglich.

SVR_53, 33, 0, 0 ,33
SVR_152, 12, 4, 0, 16
usw.


```
<td class="passed-cell">
<span> SVR_53</span>
</td>
<td class="number-cell">
<span>33</span>
</td>
<td class="number-cell"/>
<span>0</span>
</td>
<td class="number-cell"/>
<span>0</span>
</td>
<td class="number-cell">
<span>33</span>
<td class="passed-cell">
<span> SVR_152</span>
</td>
<td class="number-cell">
<span>12</span>
</td>
<td class="number-cell"/>
<span>4</span>
</td>
<td class="number-cell"/>
<span>0</span>
</td>
<td class="number-cell">
<span>16</span>
</td>
```

Könnt Ihr mir sagen wie ich die jeweiligen Bereiche auslesen kann?

Besten Dank


----------



## httpdigest (30. Aug 2018)

Siehe: https://jsoup.org/cookbook/extracting-data/selector-syntax
Du willst ja alle <span> Elemente innerhalb von <td> Elementen mit Klasse "passend-cell" oder "number-cell". Vielleicht ist die class-Abfrage auch unnötig. Kommt darauf an, ob es eventuell noch mehr <table>-Elemente gibt, die du nicht selektiert haben willst.


----------



## TheMan (30. Aug 2018)

Ich hatte angefangen mit


```
Element masthead = doc.select("td.passed-cell")
```

Hier könnte ich iterieren und mit Hilfe eines Integer der innerhalb der Schleife inkrementiert wird alle Werte unter passed-cell speichern.


```
ArrayList passedList = new ArrayList();
int iD = 0;

for (Element e : masthead)
{
     passedList(iD, e.text());
     iD++;
}
```

Aber wie schaffe ich es, die 4 nachfolgenden "number-cells" dem entsprechenden SVR zuzuordnen?

Vielen Dank


----------



## looparda (31. Aug 2018)

Bevor man jetzt groß versucht zu parsen: Ist es gewollt oder Teil des Problems, dass das HTML nicht valide ist?


----------



## TheMan (31. Aug 2018)

Was meinst du in dem Fall mit nicht valide?

Ich habe lokal eine .html Datei und aus der möchte ich wie oben beschrieben jeweils die Zellen der Tabelle auslesen. Ich habe ja lediglich den relevanten Ausschnitt geposted.


```
<table class="overview-new">
<tr>
<td class="passed-cell">
<span>SVR_52</span>
</td>
<td class="number-cell">
<span>33</span>
</td>
<td class="number-cell"/>
<td class="number-cell"/>
<td class="number-cell">
<span>33</span>
</td>
</tr>
<tr>
<td class="passed-cell">
<span>SVR_53</span>
</td>
<td class="number-cell">
<span>33</span>
</td>
<td class="number-cell"/>
<td class="number-cell"/>
<td class="number-cell">
<span>33</span>
</td>
</tr>
</table>
```

Wie schaffe ich es nun hier sequentiell auszulesen was zusammengehört? Vor allem an der Stelle, in der die html-File nichts einträgt, was für mich aber gleichbedeutend mit 0 ist

Beispiel neuer Code-Schnipsel
SVR_52, 33, 0, 0, 0, 33
SVR_53, 33, 0, 0, 0, 33

Vielen Dank


----------



## looparda (31. Aug 2018)

Ich beziehe mich auf das HTML im ersten Beitrag:
Bsp. kein <table>, <tr>
Bsp. Eine leere Zelle, danach ein span und dann eine nicht-geöffnete Zelle schließen
	
	
	
	





```
<td class="number-cell"/>
<span>0</span>
</td>
```


----------



## looparda (31. Aug 2018)

```
<table>
    <tr>
        <td class="passed-cell">
            <span> SVR_53</span>
        </td>
    </tr>
    <tr>
        <td class="number-cell">
            <span>33</span>
        </td>
    </tr>
    <tr>
        <td class="number-cell">
            <span>0</span>
        </td>
    </tr>
    <tr>
        <td class="number-cell">
            <span>0</span>
        </td>
    </tr>
    <tr>
        <td class="number-cell">
            <span>33</span>
    <tr>
        <td class="passed-cell">
            <span> SVR_152</span>
        </td>
    </tr>
    <tr>
        <td class="number-cell">
            <span>12</span>
        </td>
    </tr>
    <tr>
        <td class="number-cell">
            <span>4</span>
        </td>
    </tr>
    <tr>
        <td class="number-cell">
            <span>0</span>
        </td>
    </tr>
    <tr>
        <td class="number-cell">
            <span>16</span>
        </td>
    </tr>
</table>
```

Hier ein Ansatz ohne Fehlerbehandlung. Ich hole mir alle Zeilen und bestimme den Index der "passed-cell"'s. Per index kann ich mir die Zeile holen und die nächsten 4:

```
private void extract(Document doc) {
        Elements rows = doc.select("tr");
        final List<Integer> indexOfPassedCells = rows
                                          .stream()
                                          .map(e->e.select("td"))
                                          .filter(e-> e.hasClass("passed-cell"))
                                          .map(e->rows.indexOf(e.parents().get(0)))
                                          .collect(Collectors.toList());

        for (int indexOfPassedCell : indexOfPassedCells) {
            Element svr = rows.get(indexOfPassedCell);
            Element element1 = rows.get(indexOfPassedCell + 1);
            Element element2 = rows.get(indexOfPassedCell + 2);
            Element element3 = rows.get(indexOfPassedCell + 3);
            Element element4 = rows.get(indexOfPassedCell + 4);
            printElement(svr);
            printElement(element1);
            printElement(element2);
            printElement(element3);
            printElement(element4);
        }
    }

    private void printElement(Element e) {
        System.out.println(e.text());
    }
```
Ausgabe:

```
SVR_53
33
0
0
33
SVR_152
12
4
0
16
```
Hängt stark davon ab wie statisch dein Eingangs-HTML ist, ob dieser Zugriff per Index-gefrickel solide genug ist. Je nachdem ob noch andere Elemente dazwischen sein können kann man vielleicht auch einfach per Index "abzählen" (bspw. liegt an jedem Index, wenn man über alle tr iteriert bei i + i % 5 == 0 eine "passed-cell" usw.).


----------



## TheMan (31. Aug 2018)

Danke für die schnelle Hilfe.
Die Eingangs html bleibt so statisch...immer SVR + die 4 Spalten
Meine Ausgabe ist aber leider mit dem kompletten hmtl-tags


```
<tr>
<td class="passed-cell"> <span>SVR</span> </td>
<td class="number-cell"> <span>1</span> </td>
<td class="number-cell"></td>
<td class="number-cell"></td>
<td class="number-cell"> <span>1</span> </td>
</tr>
```

Und am Ende ein out of range Fehler

```
java.lang.IndexOutOfBoundsException: Index: 63, Size: 63
    at java.util.ArrayList.rangeCheck(Unknown Source)
    at java.util.ArrayList.get(Unknown Source)
```

Evtl eine Idee? 

ps. Lässt sich anstatt der hasClass auch mit contains(SVR) nur danach suchen oder geht das nicht?


----------



## looparda (31. Aug 2018)

Ich habe wie gesagt mit dem angegebenen HTML gearbeitet, das eine andere Struktur hat als dein neues.Plötzlich sind mehrere Zellen in einer Zeile. Wenn sich die Struktur Ändern muss man auch den Parser anpassen:

```
private void extract_bak(Document doc) {
    Elements cells = doc.select("tr td");
    final List<Integer> indexOfPassedCells = cells
                                      .stream()
                                      .filter(e-> e.hasClass("passed-cell"))
                                      .map(e->cells.indexOf(e))
                                      .collect(Collectors.toList());

    for (int indexOfPassedCell : indexOfPassedCells) {
        final Element svr = cells.get(indexOfPassedCell);
        final Element element1 = cells.get(indexOfPassedCell + 1);
        final Element element2 = cells.get(indexOfPassedCell + 2);
        final Element element3 = cells.get(indexOfPassedCell + 3);
        final Element element4 = cells.get(indexOfPassedCell + 4);
        printElement(svr);
        printElement(element1);
        printElement(element2);
        printElement(element3);
        printElement(element4);
    }
}
```



TheMan hat gesagt.:


> ps. Lässt sich anstatt der hasClass auch mit contains(SVR) nur danach suchen oder geht das nicht?




```
final List<Integer> indexOfPassedCells = cells
                                          .stream()
                                          .filter(e-> e.text().contains("SVR"))
                                          .map(e->cells.indexOf(e))
                                          .collect(Collectors.toList());
```
Da du gesagt hast das HTML bleibt statisch kann man auch direkt den index berechnen statt abzufragen:

```
private void extract(Document doc) {
    Elements cells = doc.select("tr td");
    for (int i = 0; i < cells.size(); i+=5) {
        int pos = i + i % 5;
        final Element svr = cells.get(pos);
        final Element element1 = cells.get(pos + 1);
        final Element element2 = cells.get(pos + 2);
        final Element element3 = cells.get(pos + 3);
        final Element element4 = cells.get(pos + 4);
        printElement(svr);
        printElement(element1);
        printElement(element2);
        printElement(element3);
        printElement(element4);
    }
}
```


----------



## TheMan (31. Aug 2018)

Danke aber ich stehe vor einem anderen Problem mit dem parser.

Ausgehend von deinem CODE


```
private void extract_bak([URL='http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+document']Document[/URL] doc) {
        Elements cells = doc.select("tr td");
        final List<Integer> indexOfPassedCells = cells
                                          .stream()
                                          .filter(e-> e.hasClass("passed-cell"))
                                          .map(e->cells.indexOf(e))
                                          .collect(Collectors.toList());

        for (int indexOfPassedCell : indexOfPassedCells) {
            final [URL='http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+element']Element[/URL] svr = cells.get(indexOfPassedCell);
            final [URL='http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+element']Element[/URL] element1 = cells.get(indexOfPassedCell + 1);
            final [URL='http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+element']Element[/URL] element2 = cells.get(indexOfPassedCell + 2);
            final [URL='http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+element']Element[/URL] element3 = cells.get(indexOfPassedCell + 3);
            final [URL='http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+element']Element[/URL] element4 = cells.get(indexOfPassedCell + 4);
            printElement(svr);
            printElement(element1);
            printElement(element2);
            printElement(element3);
            printElement(element4);
        }
    }
```

Wie schaffe ich es, das er nach gewissen Kriterien die Tabelle durchsucht.
Beispielsweise nur die Elemente nimmt die SVR im Namen tragen?

Ich möchte verhindern, falls ein anderer Name auftaucht, dieser gelesen wird. Es sollen nur die Tabellenzeilen und Spalten mit dem Namen SVR ausgelesen werden

```
<td class="passed-cell">
<span>Ergebniss</span>
</td>
<td class="number-cell">
<span>33</span>
</td>
<td class="number-cell"/>
<td class="number-cell"/>
<td class="number-cell">
<span>33</span>
</td>
```

Folgendes funktioniert nicht

```
if(svr .text().contains("SVR"))
```


----------



## looparda (31. Aug 2018)

Keine Ahnung, was du mit Eintragen meinst.
Aber filtern nach einem bestimmten SVR geht doch:

```
svr.text().contains("SVR_53")
```

Ah, inzwischen ist ein HTML in deiner bearbeiteten Antwort:
Auch nur die SVR filtern, wenn es noch anderen Typen gibt.

```
for (int indexOfPassedCell : indexOfPassedCells) {
    final Element svr = cells.get(indexOfPassedCell);
    if(svr.text().contains("SVR_")) {
        // ...
    } else {
       // Unknown type    
    }
}
```


----------



## TheMan (31. Aug 2018)

Hatte dann doch funktioniert.


Vielen Dank für deine Mühen


----------

