Speicherüberlauf



  • ist mir auch ein raetsel wieso man fuer sowas ausgerechnet c++ verwendet, wenn man mit der sprache sowieso schon nicht zurecht kommt.

    pythons beautifulsoap ist sehr stabil auch wenn es darum geht html trees abzulaufen, und der einstieg vermutlich um ein vielfaches simpler.


  • Mod

    Ich habe es zwar noch nicht gemacht, aber es wird ja wohl auch für C++ HTML-Parser ohne Ende geben. Selbst einen zu schreiben kann auch nicht zu schwer sein, wenn man sich auf die Grundfunktion beschränkt, ein XML-Dokument in seine Elemente zu zerlegen. Dürfte sogar einigermaßen interessant sein. Aber beides (Nutzen von fremden Libraries und Schreiben eines schönen Parsers) geht halt nicht, wenn man sich beständig weigert, die Grundlagen zu lernen.



  • @SeppJ sagte in Speicherüberlauf:

    Nachdem im letzten Thread zu dem Thema darauf hingewiesen wurde, dass man HTML nicht mit regulären Ausdrücken parsen kann, muss man eben eine plain-Text Suche machen. Ist doch logisch.

    Für den Tokenizer kann man IMHO durchaus Reguläre Ausdrücke verwenden, aber das ist natürlich nicht der ganze Parser. Den braucht man aber eventuell gar nicht, wenn man nur irgendwelche Artikeldaten rausfischen will und nicht das komplette Dokument verstehen muss. Wenn der HTML-Code nicht zu chaotisch strukturiert und übel verschachtelt ist, könnte man bei sowas hier z.B. durchaus auch mit std::regex was reißen:

    ... 
    <img class="Artikelbild" src="[url]" /> ... 
    <div class="Artikelname">Original Hammond-Orgel</div> 
    ... 
    <div class="Artikelbeschreibung">blabla</div>
    ...
    

    ... auf jeden Fall mehr als mit std::string::find().

    Ansonsten möchte ich nochmal das Kommandozeilen-Tool Xidel erwähnen, damit kann man Abfragen auf HTML-Dokumenten in XPath/XQuery formulieren und auch komplexere Strukturen handhaben. Das Tool kann ein (gar nicht bis wenig strukturiertes) HTML-Dokument auf Basis der Abfrage z.B. in ein sauber strukturiertes XML- oder JSON-Dokument überführen, mit dem man deutlich einfacher weiterarbeiten kann, als mit dem HTML-Chaos, das Websites meist ausspucken.

    ... oder eben andere Tools/Bibliotheken, die mit XPath/XQuery umgehen können. Die Sprachen wurden genau für sowas gemacht.

    Die Abfragen lassen sich damit auch etwas robuster formulieren, so dass sie nicht bei jeder kleinen Änderung vor die Wand laufen, z.B. wenn das Artikelbild zuerst so aussah:

    <img class="Artikelbild" src="[url]" />
    

    und einen Tag später so:

    <img class="Artikelbild" alt="Alt Text" src="[url]" />
    

    ... oder wenn sich die Reihenfolge der Attribute ändert. Wenn ich mit XPath z.B. img[@class='Artikelbild'] referenziere, dann ist es egal, wo das class-Attribut steht, oder ob das Tag noch andere Attribute hat. Nur mit find/regex kommt man da wahrscheinlich irgendwann nicht mehr hinterher.

    @Cardiac sagte in Speicherüberlauf:

    pythons beautifulsoap ist sehr stabil auch wenn es darum geht html trees abzulaufen, und der einstieg vermutlich um ein vielfaches simpler.

    Das sieht mir auf den ersten Blick auch nach einer guten Lösung aus. Für solche Sachen würde ich auch erstmal sowas wie Python nehmen (oder eben irgendwelche Kommandozeilen-Tools und Shell-Skripte). Erst wenn ich wirklich die Performance brauche (z.B. weil der Code auf ner Suchmaschinen-Serverfarm 100k Seiten pro Sekunde abarbeiten soll), überlege ich mir, das eventuell mit C++ zu erschlagen.



  • @DirkB sagte in Speicherüberlauf:

    Wo wird der denn gelöscht?

    /* oac (Organ Ads Compressor) 
     Programm zur Reduzierung von archivierten eBay-Elektroorgel-Anzeigen auf das zur Erfassung in Datenbanken erforderliche Minimum */
    
    // DANKSAGUNGEN:
    // Lasha Khintibidze für Hilfe beim Erzeugen von Verzeichnissen mittels create_directory (https://www.delftstack.com/de/howto/cpp/cpp-create-directory/)
    // "Finnegan" vom C++-Community-Forum für den Hinweis auf -std=c++17 als zur fehlerfreien Einbindung von <filesystem> notwendigen gcc-Compileroption
    
    #include <iostream>
    #include <fstream>
    #include <string>
    #include <vector>
    #include <dirent.h>
    #include <filesystem>
    #include <iterator>
    #include <map>
    using namespace std;
    namespace fs = std::filesystem;
    
    int main()
    {
      DIR *dir, *subdir;
      dirent *entry;
      const char *altpfad="/home/Musik/Sonstiges/WWW-Seiten, gescannte Texte/Orgeln/eBay-Kleinanzeigen/2019-01/";
      const char *neupfad="/home/Musik/Sonstiges/WWW-Seiten, gescannte Texte/Orgeln/eBay-Kleinanzeigen_neu/2019-01/";
      char *name; // Dateinamen
      ifstream datei, bilddatei;
      ofstream datei_neu;
      string source; // eingelesene Original-HTML-Textdatei als String
      string beschreibung; // Anzeigentext, zu finden nach 'itemprop="description">'
      string benutzer; // Benutzername, zu finden nach '<div class="usercard--info--collumn">'
      string ort; // Postleitzahl und Ort, zu finden nach 'itemprop="locality">'
      string erstdatum; // Erstellungsdatum, zu finden nach "Erstellungsdatum"
      string preis; // Preis, zu finden nach "Preis:"
      string filedate; // Anzeigendatum als Namens- und Titelbestandteil für zu erzeugende neue HTML-Datei
      int occ, occ2; // Variable für Stringpositionen
      char c; // eingelesenes Zeichen aus Original-HTML-Textdatei oder Datenbyte aus Bilddatei
      string dn; // Anzahl der erfassten Anzeigen eines Datums als String (Teil des Dateinamens und Titels der zu erzeugenden neuen HTML-Datei)
      string pn; // Bilddatei-Nummer als String
      /* Array, Zähler und Ergebnisvariable für Suche nach Ziffern - ich bin leider zu dumm, um die Implementation regulärer Ausdrücke in <regex> zu verstehen! */
      unsigned int pw[10]; // Array für Suchergebnisse für die Ziffern 0 bis 9
      unsigned short int i;
      unsigned int pos;
      map<string, int> Daten; // "Array" für Datums-Strings und deren Häufigkeiten
      unsigned int ad=0; // Anzeigen-Zähler
    
      dir = opendir(altpfad);
      while (dir)
      {
        entry = readdir(dir);
        if (!entry) break;
        name = entry->d_name;
      
        // cout << name << endl; // Testausgabe des Dateinamens
        string vollpfad_alt = string(altpfad);
        vollpfad_alt.append(string(name));
        if (vollpfad_alt.find("html") != -1) // nur Dateien mit Endung html öffnen!
        {
          datei.open(vollpfad_alt.c_str(), ios_base::in);
          string pfad_alt = string(altpfad);    
          // string dateiname =  "(919) Technics U50 Orgel Top Zustand in Innenstadt - Köln Altstadt | Künstler- und Musikbedarf gebraucht | eBay Kleinanzeigen.html";
          // string dateiname = "(919) Yamaha Electone HX-1 _ Orgel sehr gut erhalten! mit MDR-4 _ MIDI in Nordrhein-Westfalen - Bergisch Gladbach | Musikinstrumente und Zubehör gebraucht kaufen | eBay Kleinanzeigen.html";
          
          // datei.open("/home/Musik/Sonstiges/WWW-Seiten, gescannte Texte/Orgeln/eBay-Kleinanzeigen_neu/(919) Technics U50 Orgel Top Zustand in Innenstadt - Köln Altstadt | Künstler- und Musikbedarf gebraucht | eBay Kleinanzeigen.html", ios_base::in);
          // datei.open("/home/Musik/Sonstiges/WWW-Seiten, gescannte Texte/Orgeln/eBay-Kleinanzeigen/2019-01/Zu verschenken Orgel,alt Bontempi Master in Nordrhein-Westfalen - Borken | Zu verschenken | eBay Kleinanzeigen.html", ios_base::in);
          // datei.open (pfad_alt.append(dateiname), ios_base::in);
          if (datei)
          {
            while (datei.get(c))
            {
              source.push_back(c); // html-Datei wird eingelesen
            }
          }
          else
          {
            cerr << "Die Datei kann nicht geöffnet werden!" << endl;
          }
          
          datei.close();
          
          // Auslesen der Beschreibung
          occ = source.find("itemprop=\"description\">");
          occ2 = source.find ("</p>", occ);
          beschreibung = source.substr(occ+23, occ2-(occ+23));
    
          // Auslesen des Benutzernamens
          occ = source.find("<div class=\"usercard--info--collumn\">");
          occ = source.find("<h2>", occ);
          occ2 = source.find("</h2>", occ);
          benutzer = source.substr(occ+4, occ2-(occ+4));
             
          // Auslesen der Ortsinformationen
          occ = source.find("itemprop=\"locality\">");
          for (i=0; i<10; i++)
          {
            pw[i] = source.find((char)'0'+i, occ);
            if (i == 0)
              pos = pw[i];
            else
              if (pw[i] < pos)
                pos = pw[i];
          }
        
          occ = source.find("</span>", pos);
          ort = source.substr(pos, occ-pos);
          
          // Auslesen des Erstellungsdatums
          occ = source.find("Erstellungsdatum");
          for (i=0; i<10; i++)
          {
            pw[i] = source.find((char)'0'+i, occ);
            if (i == 0)
              pos = pw[i];
            else
              if (pw[i] < pos)
                pos = pw[i];
          }
          erstdatum = source.substr(pos, 10);
          
          
          
          // Auslesen des Preises
          occ = source.find("category: 'Zu verschenken'");
          if (occ > -1)
            preis = "0 € (zu verschenken)";
          else
          {
            occ = source.find("Preis:");
            for (i=0; i<10; i++)
            {
              pw[i] = source.find((char)'0'+i, occ);
              if (i == 0)
                pos = pw[i];
              else
                if (pw[i] < pos)
                  pos = pw[i];
            }
            occ = source.find("</h2>", pos);
            preis = source.substr(pos, occ-pos);
          }
          
          source.clear();
         
          filedate = erstdatum.substr(6, 4) + erstdatum.substr(3, 2) + erstdatum.substr(0, 2);
          
          if (Daten.insert(make_pair(filedate, 1)).second == false)
            Daten[filedate]++;
          
          dn = to_string(Daten[filedate]);
          if (Daten[filedate] < 10)  // Einfügung führender Nullen
            dn = "00" + dn;
          else if (Daten[filedate] < 100)
            dn = "0" + dn;
          string filedatestring;
          filedatestring = filedate + "_" + dn;  
         
          // Einrichtung des Verzeichnisses für die aktuelle neue HTML-Datei
          string dateiname_neu = filedatestring + ".html";
          string vollpfad_neu;
          vollpfad_neu = string(neupfad);
          vollpfad_neu.append(filedatestring + "/");
          fs::create_directory(vollpfad_neu);
          string vollpfad_neu2 = vollpfad_neu;
          vollpfad_neu.append(dateiname_neu);
     
         // Öffnen und Schreiben der Ausgabe-HTML-Datei
          datei_neu.open(vollpfad_neu.c_str(), ios_base::out);
          cout << "vollpfad_neu: " << vollpfad_neu << endl;
          
          if (!datei_neu)
          {
            cerr << "Ziel-HTML-Datei kann nicht geöffnet werden!" << endl;
            return 1;
          }
          else
          {
            datei_neu << "<!DOCTYPE html>\n"
                      << "<html>\n"
                      << "<head>\n"
                      << "  <meta charset=\"UTF-8\">\n"
                      << "  <title>Anzeige " << filedate << "</title>\n"
                      << "</head>\n"
                      << "<body>\n"
                      << "  <h2>Anzeige " << filedate << "</h2>\n"
                      << "  <p>\n"
                      << "    " << beschreibung << "\n"
                      << "  </p>\n"
                      << "  <table>\n"
                      << "    <tr>\n"
                      << "      <td>Benutzer:</td>\n" 
                      << "      <td>" << benutzer << "</td>\n"
                      << "    </tr>\n"
                      << "    <tr>\n"
                      << "      <td>Datum:</td>\n" 
                      << "      <td>" << erstdatum << "</td>\n"
                      << "    </tr>\n"
                      << "    <tr>\n"                  
                      << "      <td>Ort:</td>\n" 
                      << "      <td>" << ort << "</td>\n"
                      << "    </tr>\n"
                      << "    <tr>\n" 
                      << "      <td>Preis:</td>\n" 
                      << "      <td>" << preis << "</td>\n"
                      << "    </tr>\n"
                      << "  </table>\n" << endl;
                      
                      
          }   
          datei_neu.close();  
          
          
            // Auslesen und Kopieren der Bilddateien aus dem jeweiligen Datenverzeichnis der Original-HTML-Datei
          
          vollpfad_alt.erase(vollpfad_alt.find(".html"));
          vollpfad_alt.append("-Dateien/");
          cout << vollpfad_alt << endl;
          
          subdir = opendir(vollpfad_alt.c_str());
          
          i=0; // Zähler für Bilddateien
          
          string vollpfad_neu_bilddateien; // vollständiger Dateiname (mit Pfad) jeder Bilddatei
          
          while(subdir)
          {
            entry = readdir(subdir);
            if (!entry) break;
            name = entry->d_name;
            string sname = string(name);
            if (sname.find("_72") != -1) // zu kopierende Bilddateien haben das Namensschema _72*JPG
            {
              cout << sname << endl;
              i++; // Bilddateien-Zähler wird um 1 erhöht
              pn = to_string(i);
              vollpfad_neu_bilddateien = vollpfad_neu2 + filedatestring + "_Bild_" + pn + ".jpg";
              
              
              bilddatei.open(vollpfad_alt + sname, ios_base::in);
              datei_neu.open(vollpfad_neu_bilddateien, ios_base::out);
              
                    
              /* cout << "vollpfad_neu2: " << vollpfad_neu2 << endl;
              cout << "filedatestring: " << filedatestring << endl;
              cout << "\"_Bild_\" + pn + \".jpg\": " << "_Bild_" + pn + ".jpg" << endl; */
              
              if (!bilddatei)
              {
                cerr << "Quell-Bilddatei kann nicht geladen werden!" << endl;
                return -1; // Programmabbruch
              }
              else if (!datei_neu)
              {
                cerr << "Ziel-Bilddatei kann nicht geöffnet werden!" << endl;
                return -1; // Programmabbruch
              }
              else 
              {
                while (bilddatei.get(c))
                {
                  datei_neu.put(c);
                }
              }
              
              bilddatei.close();
              datei_neu.close();
            }
          }
         cout << "Anzahl der Bilddateien: " << i << endl;
         
         
         // Einbindung der kopierten Bilddateien in Code der HTML-Ausgabedatei
         datei_neu.open(vollpfad_neu, ios_base::app);
         
         cout << vollpfad_neu << endl;
         cout << vollpfad_neu2 << endl;
         cout << filedatestring << endl;
         cout << vollpfad_neu_bilddateien << endl;
         
         unsigned int j; // Zähler für Schleife
         
         string bilddateiname_rumpf = filedatestring + "_Bild_";
         
         cout << bilddateiname_rumpf << endl;
         
         for (j=1; j<=i; j++)
         {
           datei_neu << "  <p>\n"
                     << "    <img src=\"" << bilddateiname_rumpf << j << ".jpg\">\n"
                     << "  </p>\n";
         }
         
           datei_neu          << "</body>\n"
                     << "</html>" << endl;
         
         cout << "Anzeige Nr. " << ad++ << " reduziert!" << endl;            
                     
        }    
        datei_neu.close();
      }  
      return 0;
    }
      
    
    

    Zeile 141!

    Bis bald im Khyberspace!

    Yadgar



  • Hi(gh)!

    @Cardiac sagte in Speicherüberlauf:

    ist mir auch ein raetsel wieso man fuer sowas ausgerechnet c++ verwendet, wenn man mit der sprache sowieso schon nicht zurecht kommt.

    pythons beautifulsoap ist sehr stabil auch wenn es darum geht html trees abzulaufen, und der einstieg vermutlich um ein vielfaches simpler.

    Ich komme mit keiner Programmiersprache der Welt zurecht, überall knalle ich früher oder später gegen die Wand... in Python allerdings noch viel früher als in C++, weil ich davon nun überhaupt keine Ahnung habe!

    Bis bald im Khyberspace!

    Yadgar



  • @Yadgar sagte in Speicherüberlauf:

    Python allerdings noch viel früher als in C++, weil ich davon nun überhaupt keine Ahnung habe!

    Dann nimm doch Perl (habe ich früher viel gemacht, aber die Sprache ist ja nicht mehr "in"...). Eignet sich für sowas aber auch heute noch, z.B. mit Mojo::DOM) - wenn du dir das Beispiel ansiehst: ist doch viel leichter als das von Hand zu machen? (und Scrapy für Python hatte ich, wenn ich mich recht entsinne, auch schon mal vorgeschlagen - genau wie beautifulsoup).



  • Hi(gh)!

    @wob sagte in Speicherüberlauf:

    @Yadgar sagte in Speicherüberlauf:

    Python allerdings noch viel früher als in C++, weil ich davon nun überhaupt keine Ahnung habe!

    Dann nimm doch Perl (habe ich früher viel gemacht, aber die Sprache ist ja nicht mehr "in"...). Eignet sich für sowas aber auch heute noch, z.B. mit Mojo::DOM) - wenn du dir das Beispiel ansiehst: ist doch viel leichter als das von Hand zu machen? (und Scrapy für Python hatte ich, wenn ich mich recht entsinne, auch schon mal vorgeschlagen - genau wie beautifulsoup).

    Muss man alles mühsam selbst kompilieren, traue ich mir auch nicht zu - ist viel zu oft schief gegangen! Gibt es keine Lösung, die man einfach mit apt-get install <soundso> (bin Debian-User) installieren kann?

    Und Perl kenne ich auch nur vom Hörensagen...

    Bis bald im Khyberspace!

    Yadgar



  • Hi(gh)!

    Ich konnte das Problem lösen - es lag zum Einen daran, dass ich bei der Extraktion der Preisangaben nicht alle vorkommenden Fälle (regulärer Preis, Verhandlungsbasis, zu verschenken, zu tauschen) berücksichtigt hatte, zum Anderen an seinerzeit (Januar 2019) versehentlich abgespeicherten eBay-Auktionen, deren Seiten (zu erkennen am Datennamen-Ende "eBay.html" statt "eBay Kleinanzeigen.html") natürlich eine ganz andere Struktur haben als die Kleinanzeigenseiten!

    Jetzt werden wirklich nur Kleinanzeigen-Seiten geöffnet:

    if (vollpfad_alt.find("eBay Kleinanzeigen.html") != -1)
    

    und alle Möglichkeiten von Preisangaben berücksichtigt:

          // Auslesen des Preises
          occ = source.find("category: 'Zu verschenken'");
          if (occ > -1)
            preis = "0 € (zu verschenken)";
          else
          {
            occ = source.find("category: 'Tauschen'");
            if (occ > -1)
              preis = "Zu tauschen";
            else
            {
              occ = source.find("Preis:");
              occ2 = source.find("Zu verschenken", occ);
              if (occ2 != -1)
                preis = "0 € (zu verschenken)";
              else
              {  
                occ2 = source.find("VB", occ);
                if (occ2 != -1)
                  preis = "VB";          
                else
                {  
                  for (i=0; i<10; i++)
                  {
                    pw[i] = source.find((char)'0'+i, occ);
                    if (i == 0)
                      pos = pw[i];
                    else
                      if (pw[i] < pos)
                        pos = pw[i];
                  }
                  occ = source.find("</h2>", pos);
                  preis = source.substr(pos, occ-pos);
                }
              }
            }  
          }
    
    

    ...und jetzt werden tatsächlich alle relevanten HTML-Dateien reduziert und die zugehörigen Bilddateien kopiert! Ich scheine also irgendwie doch C++ zu können... zumindest irgendwie... 😉

    Bis bald im Khyberspace!

    Yadgar



  • @Yadgar sagte in Speicherüberlauf:

    occ = source.find("</h2>", pos);
    preis = source.substr(pos, occ-pos);

    Du solltest etwas sorgfältiger arbeiten. Du suchst einen String, berücksichtigst aber nicht, dass die Suche auch fehlschlagen kann.

    Der Rückgabewert der find Funktion ist folgendermaßen beschrieben:

    Position of the first character of the found substring or npos if no such substring is found.

    Quelle: https://en.cppreference.com/w/cpp/string/basic_string/find

    Das bedeutet du musst bei allen find Aufrufen das Ergebnis auf string::npos prüfen.



  • @Yadgar https://de.wikipedia.org/wiki/Strukturierte_Programmierung

    Und eine Funktion hat genau eine verf* Aufgabe. Genau eine. Nicht mehr.

    Zudem wie @Quiche-Lorraine schon sagte, prüfe verda* nochmal auf Fehlerzustände. Überall.


Anmelden zum Antworten