Htmlcode abfragen und ausgeben

seahawk

Mitglied
Hallo alle zusammen,
ich bin noch ein Java-Anfänger möchte aber eine kleine Android-App programmieren, welche den html-code eines Vertretungsplans aufruft, das ganze in eine csv-datei schreibt, bzw mir spezifisch für eine Klasse alles wichtige raussucht.
Mein Htmlcode sieht so aus:
HTML:
<tr class="normal">
<td class="VBlock">7d</td>
<td class="VBlock">Mi 1.</td>
<td class="VBlock">xxxxx / E</td>
<td class="VBlock">entfällt</td>
<td class="VBlock"></td>
<td class="VBlock"></td>
<td class="VBlock" style="white-space:normal"></td>
</tr>
<tr class="normal Leselinie">
<td class="VBlock">7d</td>
<td class="VBlock">Mi 2.</td>
<td class="VBlock">xxxxx / E</td>
<td class="VBlock">entfällt</td>
<td class="VBlock"></td>
<td class="VBlock"></td>
<td class="VBlock" style="white-space:normal"></td>
</tr>
usw..und jetzt würde ich halt gerne zB nur alles anzeigen was für die 7d entfällt.
Mein Code sieht bisher so aus:
C:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.io.IOException;


public class Main2Activity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        Reading();
    }

    protected void Reading()throws IOException{
        URL url = new URL("https://list-gymnasium.de/vertretungsplan/upload/Vertretungen-Sa.html");
        InputStream in = url.openStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        String s;

        while((s = reader.readLine()) != null){
            System.out.println(s);
        }
    }
}
Kann mir jemand sagen, was die beste Methode ist, das ganze zu "filtern"?
Vielen Dank schonmal im Voraus!
seahawk
 

Robat

Top Contributor
Bevor du dir anfängst deinen eigenen Parser zu bauen solltest du ggf. lieber auf eine Library wie JSoup zurückgreifen.
Damit kannst du ganz einfach auf bestimmte Elemente aus deiner HTML Datei zugreifen und so zum Beispiel über alle Reihen iterieren und dir von jeder Reihe alle Spalteninhalte holen.
 
X

Xyz1

Gast
Wieso ist bei mir der Body die Seite leer?
Code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                      "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Vertretungsplan</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<style type="text/css">
# hier Style-Informationen
</style>
<link rel="stylesheet" type="text/css" href="Vertretungen-S.CSS"/>
</head>
<body>

</body>
</html>


Nichtsdestominder ist JSoup eine gute , erste Anlaufstelle...
Wenn Du mir den Vertretungsplan schicken magst, kann ich mal schauen, wie man daraus die relevanten Daten extrahieren und weiter-bearbeiten könnte.
Mich würd auch _nicht_ interessieren, wie oft welcher Unterricht bei welchem Lehrkörper entfällt.... Ich weiß ja gar nicht, wo List liegt. :D
 

seahawk

Mitglied
Erstmal vielen dank für eure schnellen Antworten!
@Tobias-nrw ja das liegt daran, dass der Vertretungsplan momentan offline ist, da Ferien sind. Ich habe allerdings einen alten Code gespeichert..hab ich auch oben einen Ausschnitt gepostet..brauchst du mehr?
Ich hab das mit jsoup versucht und denk auch, dass das die beste Art ist..aber irgendwie kann ich Jsoup nicht importieren. Ich arbeite in Android-Studio. Mein Code:
C:
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import org.jsoup.nodes.Elements;


public class Main2Activity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        Reading();
    }

    protected void Reading(){
        //Document doc = Jsoup.connect("http://example.com/").get(); das nehm ich wieder, wenn die website wieder online ist
        File input = new File("/Desktop/input.html");
        Document doc = Jsoup.parse(input, "UTF-8", "");
        String treffer = doc.select("tr:has(td.VBlock:containsOwn(klasse))");
        System.out.println(treffer);
    }
}
mein Error:
Code:
error: package org.jsoup does not exist   
error: package org.jsoup.nodes does not exist   
error: package org.jsoup.nodes does not exist   
error: cannot find symbol class File   
error: cannot find symbol class File   
error: cannot find symbol class Document   
error: cannot find symbol variable Jsoup
Ich mach glaub irgendwas beim Importieren falsch..und bei der Kodierung. Was für eine muss ich da nehmen, dass Umlaute angezeigt werden?
 
X

Xyz1

Gast
der Body die Seite leer?
Oh man, ich sollte auch mal lesen, was ich so schreibe. :D

... auch peinlich, schließlich weiß ich ungefähr, wo Reutlingen liegt... und das nachdem ich selber zu einem Gymi ging das nach einem meiner Vorfahren benannt wurde. :(

um näher helfen zu können, ja

aber irgendwie kann ich Jsoup nicht importieren
pom.xml, innerhalb von <dependencies>:
Code:
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.12.1</version>
</dependency>


Ich arbeite in Android-Studio
Das habe ich zurzeit nicht installiert...
 

seahawk

Mitglied
ok, also ich hab das jetzt zum Laufen gebracht..
C:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.content.Intent;
import android.widget.Button;
import android.widget.EditText;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

import java.io.IOException;


public class MainActivity extends AppCompatActivity {
    private Button button;
    private String klasse;
    private EditText etklasse;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                etklasse = (EditText)findViewById(R.id.editText);
                klasse = etklasse.getText().toString();
                openActivity2();
                Reading();
            }
        });
    }

    public void openActivity2() {
        Intent intent = new Intent(this, Main2Activity.class);
        startActivity(intent);
    }

    public static void Reading()throws IOException {
        //Document doc = Jsoup.connect("http://example.com/").get(); //das nehm ich wieder, wenn die website wieder online ist
        File input = new File("/Desktop/input.html"); //ist die htmldatei die ich schonmal gepostet habe
        Document doc = Jsoup.parse(input, "UTF-8");
        Elements treffer = doc.select("tr:has(td.VBlock:containsOwn(klasse))");
        System.out.println(treffer);
    }
}
mein .get() wirft mir allerdings eine Exception, die ich dann oben ja auch reingeschrieben habe..allerdings kann ich die Methode gar nicht aufrufen, weil die onCreate Methode keine Exceptions haben darf
 

Robat

Top Contributor
Generell darfst du Netzwerk-Operationen in Android sowieso nicht auf den Haupthread ausführen, sonder musst es auf einen anderen Thread auslagern. Dazu eignen sich in Android der so genannte AsnycTask.
Java:
public class ReadHtmlTask extends AsyncTask<String, Void, Elements> {
    @Override
    protected Elements doInBackground(String... urls) {
        try {
            Document document = JSoup.connect(urls.get(0)).get();
            Elements treffer = document.select("tr:has(td.VBlock:containsOwn(klasse))");
            return treffer;
        } catch (IOException e) {
           e.printStackTrace();
           return null;
        }
        return null;
    }
    @Override
     protected void onPostExecute(Elements result) {
         // display result ... 
     }
}
Benutzen kannst du das ganze in deiner Activity dann wie folgt:
Java:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                etklasse = (EditText)findViewById(R.id.editText);
                klasse = etklasse.getText().toString();
                ReadHtmlTask task = new ReadHtmlTask();
                task.execute("http://www.example.com");
            }
        });
    }
Nebenbei gesagt macht deine aktuelle Logik recht wenig Sinn. Du startest erst eine neue Activity und rufst danach Reading() auf.
 

seahawk

Mitglied
Also wenn ich dich richtig verstanden habe, müsste das ja so aussehen:
C:
public class Main2Activity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                etklasse = (EditText)findViewById(R.id.editText);
                klasse = etklasse.getText().toString();
                ReadHtmlTask task = new ReadHtmlTask();
                task.execute("http://www.example.com");
            }
        });
    }
}

public class ReadHtmlTask extends AsyncTask<String, Void, Elements> {
    @Override
    protected Elements doInBackground(String... urls) {
        try {
            Document document = JSoup.connect(urls.get(0)).get();
            Elements treffer = document.select("tr:has(td.VBlock:containsOwn(klasse))");
            return treffer;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return null;
    }
    @Override
    protected void onPostExecute(Elements result) {
        // display result ...
    }
}
Muss ich für das AsnycTask irgendwas importieren, weil momentan zeigt er mir viele Errors
 

Robat

Top Contributor
Sieht deine Java Datei denn genau so aus wie du sie gepostet hast? Die ReadHtmlTask Klasse sollte schon eine eigene Java Datei oder eine innere Klasse der Activity sein. Ansonsten kommt AsyncTask aus dem Package android.os.*;

Mehr kann man nicht sagen .. da müsstest du schon mal preisgeben, was für Fehler das sind

PS: Der Code war auch nicht dazu gedacht dass du ihn einfach kopierst. Du solltest dich schon damit beschäftigen und verstehen was dort passiert.
 

seahawk

Mitglied
Oki, danke erstmal für die Tipps! Ich hab mir AsyncTask angeschaut und verstanden. Hab jetzt das ganze auch nochmal überarbeitet.
Eine Frage ist mir aber geblieben..
// display result ...
also wenn ich das richtig verstanden habe, gibt die onPostExecute-Methode ihr result gleich an den UI-Thread weiter. Wenn ich jetzt mit den Daten aber erstmal weiterabreiten möchte..zb erstmal in variablen speichern, wie mach ich das dann?
Und ich kann ja auch nichts anfänglich übergeben, weil ich mir das ja eigentlich in genau dem Schritt erst hole. Und nochmal zur Absicherung..Netzwerkoperationen NUR und GANZ in einem Asynctask?
 

Robat

Top Contributor
Weitergeben tut die nichts. Es ist aber sichergestellt, dass diese Methode im UI Thread aufgerufen wird. Somit kannst du dort ohne Sorgen das Resultat anzeigen.
Wenn du vorher mit den Daten noch etwas machen möchtest kannst du das natürlich gerne tun. Darin hundert dich niemand

Und ja: Netzwerkoperationen sollten ausschließlich in einem separaten Thread laufen
 

Robat

Top Contributor
der AsyncTask muss recht kurz laufen, damit man Probleme (z. B. configuration change) minimiert.
Ja meine Aussage war etwas salop, da hast du natürlich recht. Aber so ein Vertretungsplan sollte jetzt nicht unendlich lange zum parsen brauchen.
Darf ich das dann in der Activity mit dem AsyncTask machen oder in der Main Activity
Das kannst du ruhig im AsyncTask machen. Das ist btw keine Activity sondern eine Klasse :)
 

seahawk

Mitglied
Wieso wirft er mir hier einen Error?
C:
    @Override
    protected Elements doInBackground(String... urls) {
        try {
            Document document = Jsoup.connect("http://vertretungsplan.jkg-reutlingen.de/Vertretungen-Sa.html").get();
            System.out.print(document);
            Elements treffer = document.select("tr:has(td.normal:containsOwn(Aufbau))");
            System.out.print(treffer);
            return treffer;

        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return null;
    }
Er sagt bei dem unteren return..es sei ein unreachable statment
 

Robat

Top Contributor
Weil es niemals ausgeführt wird. Bei deinem try-catch Block gibt es genau 2 Fälle die eintreten können. Entweder geht alles gut, dann gibst du treffer zurück. Wenn eine Exception fliegt geht er in den catch-Block und gibt dort null zurück.
Es gibt also keine Möglichkeit wie die Zeile nach deinem try-catch Block ausgeführt werden kann
 

seahawk

Mitglied
Mmh ja so weit war ich auch..nur dann wirft er mir unzählige errors wegen JSoup
Code:
Duplicate class org.jsoup.parser.HtmlTreeBuilderState$6 found in modules jsoup-1.12.1.jar (jsoup-1.12.1.jar) and jsoup-1.12.1.jar (org.jsoup:jsoup:1.12.1)
Duplicate class org.jsoup.parser.HtmlTreeBuilderState$7 found in modules jsoup-1.12.1.jar (jsoup-1.12.1.jar) and jsoup-1.12.1.jar (org.jsoup:jsoup:1.12.1)
Duplicate class org.jsoup.parser.HtmlTreeBuilderState$8 found in modules jsoup-1.12.1.jar (jsoup-1.12.1.jar) and jsoup-1.12.1.jar (org.jsoup:jsoup:1.12.1)
Duplicate class org.jsoup.parser.HtmlTreeBuilderState$9 found in modules jsoup-1.12.1.jar (jsoup-1.12.1.jar) and jsoup-1.12.1.jar (org.jsoup:jsoup:1.12.1)
Duplicate class org.jsoup.parser.HtmlTreeBuilderState$Constants found in modules jsoup-1.12.1.jar (jsoup-1.12.1.jar) and jsoup-1.12.1.jar (org.jsoup:jsoup:1.12.1)
usw
 

seahawk

Mitglied
Wie kann ich jetzt aber, wenn ich in meiner doInBackground-Methode eine Variable mit Daten erstelle, aus der onExecute_Methode darauf zugreifen um bspw ein TextView mit den Daten zu machen
 

mihe7

Top Contributor
onPostExecute bekommt doch das Ergebnis von doInBackground. Wenn Du Daten weitergeben willst, gibst Du diese einfach zurück (ggf. musst Du den Typ anpassen).
 

Ähnliche Java Themen


Oben