HTML Parsing auf mehrere Webseiten anwenden

Hallo zusammen,
ich möchte die Anzahl von Links, Wörtern, Bildern etc. auf einer Datenbank eines Servers von tausenden durch HTTRACK gespeicherten Webseiten herauslesen.

Dafür gibt es ja Opensourceparser wie z.B. Jsoup. Diese können aber immer nur jeweils eine Seite online ‘fetchen’. Könnt ihr mir sagen, wie ein Beispiel in Java aussehen könnte, um meine gespeicherten Seiten anzusprechen? Dies soll anschließend auf unserer HTML-Oberfläche gesammelt ausgegeben werden. Ich bin mehr oder weniger Javaanfänger und möchte zunächst einmal die Hintergründe verstehen, wie so etwas ablaufen könnte, um einen Anfang zu finden. Also welche Methoden und Klassen müssten angesprochen werden um alle meine Seiten mit den geparsten Elementen anzusprechen?

Vielen vielen Dank
Greetz
Kevla

Auch wenn ich HTTRACK nicht direkt kenne, scheint es, als würden damit die HTML-Seiten einfach 1:1 auf die Platte gespült. Demnach sollte man sie einfach als Files öffnen bzw. mit einem FileInputStream auslesen können. WIE man sie ausliest - ja, da hat man dann das ganze Repertoire von HTML-Parsern zur Verfügung, das es eben so gibt. Auch jsoup habe ich noch nicht verwendet, aber das hängt letztlich nur davon ab, welcher die benötigten Funktionen anbietet, bzw. welche API einem besser gefällt.

Ja genau HTTRACK speichert die Webseiten 1:1, dies erfolgte, damit Seiten zu einem bestimmten Zeitpunkt weiterhin zu jeder Zeit vergleichbar bleiben. Ja JSoup liest solche Einzelheiten aus, das Problem welches ich hier allerdings habe ist, dass all diese Java HTML Parser immer nur einzelne Seiten nach manueller Eingabe auslesen können, es gibt doch bestimmt aber eine Möglichkeit, einen Javacode zu schreiben, sodass meine gesamte Datenbank der gespeicherten Websitefiles automatisch angesprochen wird, ich möchte letztendlich ja eine Oberfläche haben, die dieses Auslesen vereinfacht, sodass ich nicht jedes Mal bei einem Vergleich Jsoup oder ähnliches mit dem Befehl z.B. oder ansprechen muss.

Oh der letzte Post war natürlich ich, war nicht angemeldet.

Diese HTML-Parser sind meistens Bibliotheken, die man auf unterschiedliche Arten ansteuern kann. Die Beispielprogramme sind wohl oft welche, wo irgendeine (hartverdrahtete, also direkt im Quelltext angegebene, einzelne) HTML-Seite geparst wird. Aber wenn man sich da ein paar Zeilen drumrum schreibt, kann man auch eine gesamte Verzeichnisstruktur durchlaufen, und automatisch jede Datei verarbeiten lassen. (Ich persönlich habe bisher nur http://jericho.htmlparser.net/docs/index.html etwas ausführlicher verwendet, und fand ihn für meine Zwecke sehr gut, aber das hängt wie gesagt vom Anwendungsfall ab).

Dann scheint es mir ja, als wäre Jericho ähnlich wie Jsoup und befasst sich wie du schon gesagt hast jeweils immer mit einer Seite, mich interessiert jetzt ganz genau die angesprochenen paar Zeilen drumrum (wahrscheinlich puren Javacode), falls da jemand schon einmal ähnliches gemacht hat, bitte melden;), Ich denke nicht das Jericho das auch kann, bzw. sonst würden mir auch gerne andere Ansätze helfen oder auch sonst auch wo ich nachlesen kann, wie man ein Verzeichnis durchläuft und wie mehrere HTML’s durch ein verbindendes Javaarrangement über einen HTML-Parser angesprochen werden können. Danke schon mal an dich, Marco13.

Irgendwie ist mir nicht klar, wo die Frage ansetzt bzw. wo eine Antwort ansetzen sollte. Bei http://docs.oracle.com/javase/tutorial/essential/io/ steht alles, was man braucht, aber eine websuche nach “java how to … (was man machen will)” liefert bei solchen praktisch immer schnell Beispielcode. Auf der Hauptseite von Jericho sind auch ein paar Beispielprogramme, die man einfach mal starten und testen kann - du müßtest die Frage schon etwas eingrenzen…

Genau, mein Ziel ist es ein Script zu erhalten, welches in einem Verzeichnis von Websites die Javascript-Codes Links, Wörter, Bilder zählen integriert! Als Ausgabe soll der Nutzer die Anzahl der Links, Wörter, Bilder der ausgewählten Webseiten erhalten.

Auf der oracle essential Seite hört sich vom Thema ‘Walking the File Tree’ z.B. schon sehr gut an, doch ist dies alles sehr sehr komplex, mein Thema ist auch sehr speziell, sodass ich bei meiner “java how to…” Suche konkret nichts finde.
Bisher weiß ich eben nur wie ich online Links und Bilder herausparse, den Code bzw. die API dahinter (z.B. von jsoup oder jericho) kann ich vielleicht noch herausfinden, das vereinen und absuchen meiner gespeicherten Datenbank stellt wie gesagt das Problem dar, ich versuche noch das ‘walkingthefiletree’ zu verstehen. Was jetzt konkret eine weitere Frage darstellt, vielleicht sollte ich ein neues Thema aufmachen, und zwar wie man Wörter zählt. Dies ist ja nicht so einfach wie die anderen Sachen, ich habe nach sehr langer Recherche nur ein vernünftiges Onlinetool www.webwordcount.com gefunden, da dies aber nur limitiert Suchen zulässt, denke ich nicht, dass diese mit der API rausrücken:/

Ich dachte, der Jericho kann auch sowas wie „Extract Text“ relativ leicht (die Seite ist seltsamerweise gerade down … !?), ein bißchen kommt’s drauf an, was ein „Wort“ denn genau sein soll :wink: Aber im wesentlichen schmeißt das Beispielprogramm da iirc einfach alles weg, was HTML-Tags sind.

Das „Walking the File Tree“ könnte schon das richtige sein, allerdings muss ich zugeben, mit dieser neuen API noch nicht viel gemacht zu haben (bis Java 6 ging das etwas anders, nur manuell über File#listFiles), und ich mir auch nicht sicher bin, WIE die Seiten durchsucht werden sollen: Die Dateien, nacheinander, oder soll den Links gefolgt werden? (Das hängt ein bißchen davon ab, wie dieses HTTRACK mit Links umgeht - ich nehme an, dass es die auf lokale Dateien umbiegt…?)

Ja genau HTTRACK speichert lokal, meine Einstellungen sind so vorgenommen, dass pro Seite noch 2 weitere untere Ebenen interner Unterseiten mitgespeichert werden. Die Links sind dabei egal, einfach nacheinander absuchen reicht.

Hallo zusammen,

ich habe annaehernd das, was ich gewuenscht hatte durch einen befreundeten Programmierer umgesetzt bekommen und schicke hiermit den Code, denn vielleicht kann jemand detaillerter beschreiben, was genau dort gemacht wurde. Denn ich habe mir zwar einige Notizen gemacht und er hat ja auch einige Kommentare geschrieben, ich als Javaneuling verstehe dennoch trotzdem wenig, was dort im Detail gemacht wurde. So bin ich mir jetzt selbst ueber die Quelle nicht mehr sicher, er muesste eine Library von Jsoup implementiert haben, es koennte die commons-io-2.4-bin.zip sein, weil ich diese gerade noch auf dem Rechner finde. Naja aber auch sonst eine einfache Erklaerung zu den Zeilen wuerde mir stark helfen, es ist ja nicht besonders viel Code, bis jetzt weiss ich nur, dass bei der Ausgabe 4 Elemente einer Webseite gezaehlt werden. Woerter, Bilder, Links und Eingabefelder, aber auf welcher Grundlage dies beruht wuerde ich gerne auch noch verstehen. Ich weiss nur noch, dass er meinte, dass es schwierig ist zu definieren, was nun genau ein Bild auf einer Webseite ist, also zaehlen auch Buttons dazu oder auch kleine Icons, das selbe bei den Worten, wird nur allgemeiner Text gezaehlt oder jedes einzelne Wort. Nun ja vielleicht kann ja jemand diesen Code in einfache Worte fassen, wuerde mich retten, vielen Dank schon mal!!

Hier der Code:

package org.jsoup.examples;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
import org.jsoup.Jsoup;
import org.jsoup.helper.StringUtil;
import org.jsoup.helper.Validate;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import org.jsoup.select.NodeTraversor;
import org.jsoup.select.NodeVisitor;

/**
 * HTML to plain-text. This example program demonstrates the use of jsoup to convert HTML input to lightly-formatted
 * plain-text. That is divergent from the general goal of jsoup's .text() methods, which is to get clean data from a
 * scrape.
 * <p/>
 * Note that this is a fairly simplistic formatter -- for real world use you'll want to embrace and extend.
 *
 * @author Jonathan Hedley, [email]jonathan@hedley.net[/email]
 */
public class HtmlTextExtractor {
    private static final String HTRACK_INDEX_END_MARKER = "\"";
    private static final String HTTRACK_INDEX_NAME = "index.html";
    private static final String HTTRACK_INDEX_START_MARKER = "URL=";
    public static void main(String... args) throws IOException {
        Validate.isTrue(args.length == 1, "usage: supply path to process");
        String dir = args[0];
        File repo = new File(dir);
        System.out.println("Website,Words,Links,Images,Inputs");
        for(String name:repo.list()) {
            if (new File(repo, name).isDirectory()) {
                oneWebsite(repo, name);
            }
        }
    }

    private static void oneWebsite(File repo, String name) throws IOException {
        File dir = new File(repo, name);
        try {
            File indexFile = getIndexPath(dir);
            Document doc = Jsoup.parse(indexFile, null);
            if (isFrameset(doc)) {
                System.out.printf("%s,Frameset
", name);
            } else {
                HtmlTextExtractor formatter = new HtmlTextExtractor();
                String plainText = formatter.getPlainText(doc);

                System.out.printf("%s,%d,%d,%d,%d
", name,
                        countWords(plainText),
                        countLinks(doc),
                        countImages(doc),
                        countInputFields(doc));
            }
        } catch (FileNotFoundException e) {
            System.out.printf("%s,No httrac index file
", name);
        }
    }

    private static File getIndexPath(File dir) throws IOException, FileNotFoundException {
        File httrackIndex = new File(dir, HTTRACK_INDEX_NAME);
        FileInputStream inputStream = new FileInputStream(httrackIndex);
        try {
            String everything = IOUtils.toString(inputStream);
            int urlIndex = everything.indexOf(HTTRACK_INDEX_START_MARKER) + HTTRACK_INDEX_START_MARKER.length();
            int urlLastIndex = everything.indexOf(HTRACK_INDEX_END_MARKER, urlIndex);
            return new File(dir, everything.substring(urlIndex, urlLastIndex));
        } finally {
            inputStream.close();
        }
    }

    private static boolean isFrameset(Document doc) {
        Elements framesets = doc.select("frameset");
        return framesets.size() > 0;
    }
    
    private static int countWords(String text) {
        boolean inWord = false;
        int count = 0;
        int len = text.length();
        
        for (int i = 0; i < len; i++) {
            final char c = text.charAt(i);
            switch (c) {
                case ' ':
                case '	':
                case '
':
                    if (inWord) {
                        count++;
                    }
                    inWord = false;
                default:
                    if (Character.isLetterOrDigit(c)) {
                        inWord = true;
                    }
            }
        }
        
        return count;
    }
    
    private static int countLinks(Document doc) {
        Elements links = doc.select("a[href]");
        return links.size();
    }
    
    private static int countImages(Document doc) {
        Elements images = doc.select("img[src]");
        return images.size();
    }
    
    private static int countInputFields(Document doc) {
        // we only count inputs of type text and textareas, as they account for
        // 99.9% of what we want
        Elements textInputs = doc.select("input[type=text]");
        Elements textareas = doc.select("textarea");
        return textInputs.size() + textareas.size();
    }
    
    /**
     * Format an Element to plain-text
     * @param element the root element to format
     * @return formatted text
     */
    public String getPlainText(Element element) {
        FormattingVisitor formatter = new FormattingVisitor();
        NodeTraversor traversor = new NodeTraversor(formatter);
        traversor.traverse(element); // walk the DOM, and call .head() and .tail() for each node

        return formatter.toString();
    }

    // the formatting rules, implemented in a breadth-first DOM traverse
    private class FormattingVisitor implements NodeVisitor {
        private static final int maxWidth = 80;
        private int width = 0;
        private StringBuilder accum = new StringBuilder(); // holds the accumulated text

        // hit when the node is first seen
        public void head(Node node, int depth) {
            String name = node.nodeName();
            if (node instanceof TextNode)
                append(((TextNode) node).text()); // TextNodes carry all user-readable text in the DOM.
        }

        // hit when all of the node's children (if any) have been visited
        public void tail(Node node, int depth) {
            String name = node.nodeName();
            if (name.equals("br"))
                append("
");
            else if (StringUtil.in(name, "p", "h1", "h2", "h3", "h4", "h5"))
                append("

");
        }

        // appends text to the string builder with a simple word wrap method
        private void append(String text) {
            accum.append(text + " ");
        }

        public String toString() {
            return accum.toString();
        }
    }
}

Poste deinen Quellcode bitte in Java-Tags

Okay danke war mir auf dieser Seite nicht mehr sicher, wie es funktioniert, java in Brackets wahrscheinlich

*** Edit ***

Ok der Programmierer hat mir jetzt die Quelle und das was er geaendert hat bei Github geschickt, oben das geaenderte (gruen ist hinzugefuegt, rot geloescht) und unten das original, vielleicht kann ja jemand mehr damit anfangen, warum er da einzelne Zeilen des Originals geaendert hat und wofuer das das Original eigentlich gedacht war. Er ueberschaetzt mich da mit meinem Javawissen.

Das ist der Link mit den zwei unterschiedlichen Versionen:

Kann jemand helfen?

Du hast dir deine Hausübung von jemand andern machen lassen und willst jetzt dass wir sie dir erklären?

Was genau ist die Frage? Sowas wie…

private static int countLinks(Document doc) { // Zählt die Links
    Elements links = doc.select("a[href]"); // Holt alle Links aus dem Document
    return links.size(); // Gibt zurück, wie viele es sind
}

…hilft ja vermutlich nicht viel. Die übrigen Änderungen sind eben was, was er an einem Beispielsnippet verändert hat… z.B. das nicht aus einer URL, sondern einer Datei gelesen wird.

Ja doch Marco13, genau so etwas wuerde mir helfen, so etwas fuer die gesamte Javadatei und ich haette auf jeden Fall bereits die Erklaerung dafuer, selbst fuer sein Beispiel waere es hilfreich zu wissen, an welchem Punkt nach den Dateien gesucht wird.

Und Bertor, ich kann die Frage nachvollziehen, da kann ich direkt mit den letzten Teilen meiner Lebensgeschichte anfangen, wenn die Hintergruende interessieren. Ich studiere Informationsmanagement, bin im letzten Semester, es gibt allerdings diesen einen Kurs in dem ich dieser Javagruppe zugeteilt wurde, obwohl es direkt bei grundlegendem bei mir hakt und es wurde zuvor auch nicht wirklich Java an den Mann gebracht. Ich versuchte bereits den Programmierer zu verstehen und habe auch neben meinem ihm gesessen, welcher mir auch waehrenddessen versucht hat zu erklaeren, wie alles umgesetzt wird - allerdings hat dieser natuerlich auch nur begrenzt Zeit gehabt und mich auch in meinem Wissen ueberschaetzt, jetzt stolpere ich ueber meine Notizen und merke, dass ich damit nicht mehr wirklich etwas anfangen kann und wollte hier nur jemanden aus diesem Bereich bitten. Also bei so einer Herangehensweise, wie Marko13 meinte, wuerde ich auch noch etwas lernen.
Vielen Dank.

Du Studierst im letzten Semester und brauchst jemanden, der dir Kommentare an bestehenden Code schreibt um herauszufinden was der Code macht?
Gehört bei Informationsmanagement kein Programmieren zum Studienplan?

Ist richtig, es gehoert nicht unbedingt zum Studienplan!

Aber wenns generell mit java hakt is ein HTML-Parser nicht wirklich das richtige zum lernen und verstehen…