Verständnissfrage zu QNetworkAccessManager



  • Guten Abend, ich verstehe leider die Verwendung von QNetworkAccessManager nicht. Ich würde sehr gerne ein Programm schreiben, welches ein paar Websites nach Daten durchsucht und auch mit ihnen interagiert (also post).
    Allerdings verwirren mich die Slots.
    (Beispiel aus der Doku)

    QNetworkRequest request;
     request.setUrl(QUrl("http://qt.nokia.com"));
     request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
    
     QNetworkReply *reply = manager->get(request);
     connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
     connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
             this, SLOT(slotError(QNetworkReply::NetworkError)));
     connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
             this, SLOT(slotSslErrors(QList<QSslError>)));
    

    Nun verstehe ich nicht, wie ich daraus ein Programm zusammenfriemeln soll. Theoretisch müsste ich ja für jeden speziellen Request (z.B: Startseite, Login, Logs) eine einzelne Slot-Funktion schreiben. Aber das kann es doch irgendwie nicht sein.



  • Jain. Du musst natürlich für jede Seite eine eigene Funktion schreiben, da kommst nicht drum rum. Das sind nun mal unterschiedliche Seiten, oder willst die alle in einer Funktion mit if..else Blöcken parsen? Aber dafür brauchst du noch keine Slots, weil du kannst das Abfragen der Seite in einer Funktion wie

    QString getHtmlCode(const QUrl& url);

    kapseln. Dann hast du nur eine Funktion, die mit dem QNetworkAccessManager haniert und da brauchst auch nur einen Slot.



  • Auf die Idee bin ich auch schon gekommen. Allerdings wusste ich nicht, wie ich bis zum vollständigem Empfang die Funktion blockieren sollte. getHtmlCode() müsste logischerweise warten, aber wie realisiere ich sowas? Ich könnte natürlich eine boolesche Variable dafür missbrauchen, welche von der Slot-Funktion auf true gesetzt wird, aber das wirkt irgendwie sehr unprofessionell.



  • "Blockieren" kannst du sowas, indem du eine lokale QEventLoop erstellst und das finished Signal mit dem quit Slot (keine Ahnung, wie die Signals/Slots wirklich heißen, habs grad nicht im Kopf) der Event Loop verbindest.
    Aber du musst natürlich nicht wirklich blockieren, war ja nur ein Beispiel. Es kann ja durchaus asynchron ablaufen. Du machst z.B. eine Klasse, die das verarbeitet, die merkt sich intern den Status, z.B. "Verarbeite Login Seite", und wenn der Slot aufgerufen wird, ruft sie in Abhängigkeit vom Status eine entsprechende Unterfunktion auf. Oder du schreibst die Funktionen wirklich gleich als Slots und übergibst sie deiner download Funktion als Parameter, z.B.

    void processNextPage(const QUrl& url, const char * slot)

    Und die verbindet dann das Signal mit dem nächsten Slot... Oder du definierst eine Schnittstelle (muss nicht mal eine Schnittstelle sein, kann auch eine std::function sein), die du als Callback reingibst. Gibt viele Möglichkeiten, such dir was aus.


  • Mod

    Ist ja gerade sinn der Sache, das das Programm nicht blockiert.
    Frage mich allerdings ob QNetworkAccessManager das richtige für dich ist, du bekommst hier mehr oder weniger nur das rohe HTML geliefert, oder auch alles andere was dir der Webserver zurück sendet.

    Evtl. ist QWebKit hier der bessere Ansatz, damit kannst du auch das HTML durchsuchen etc.
    Das ist gerade dann praktisch, wenn eine Seite erst effektiv durch JS benutzbar wird.



  • Das ist natürlich eine gute Fragestellung. QWebKit scheint an sich eine ganz gute Lösung zu sein. Besonders gefällt mir der Umstand, dass ich per DOM auf die Elemente zugreifen kann, was mir ein Gemetzel mit RegEx erspart. Allerdings ist mir noch nicht ganz klar, ob ich mit den QWebKit-Klassen einen Browser simulieren kann. Beim Login müsste ich logischerweise erst die Login-Daten eingeben und danach die Form bestätigen. Leider habe ich dazu bis jetzt noch nichts Brauchbares gefunden.

    Zudem weiss ich auch nicht, ob dieses Vorgehen möglicherweise unnötig Ressourcen verbraucht. Schliesslich muss mein Programmen nichts anzeigen, es muss nur automatisiert Daten auslesen, verarbeiten und eingeben.

    Danke schonmal für die vielen schnellen und konstruktiven Beiträge.



  • Sorry für den Doppelpost. Ich habe vergessen etwas wichtiges zu erwähnen.

    Ist ja gerade sinn der Sache, das das Programm nicht blockiert.

    Das Blockieren wäre an sich kein Problem, da ich das Programm in einen Worker-Thread auslagern wollte, welcher per Signals Daten an den Main-Thread sendet. Wenn jemand eine bessere Lösung hat, kann er die natürlich gerne posten.



  • Qute schrieb:

    Allerdings ist mir noch nicht ganz klar, ob ich mit den QWebKit-Klassen einen Browser simulieren kann.

    Es IST ein Browser. Allerdings ist so eine Lösung alles andere als schön. Genauso wie Regex, vergiss es gleich wieder. Es gibt genug Html Parser Bibliotheken für alle Sprachen im Internet, wo du auch einen DOM bekommst.



  • Hallo, ich habe in der Dokumentation dieses Beispiel gefunden:
    http://doc.qt.digia.com/4.6/network-blockingfortuneclient.html

    Bis jetzt funktioniert das Multi-Threading ausreichend gut(ich kenne aber schon die Lösungen für diese Probleme), doch ich habe blöderweise ein Problem ein QByteArray in einen QString zu konvertieren:

    ...
    qDebug() << "begin request";
    reply = manager.get(QNetworkRequest(QUrl("http://www.google.de/")));
    connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
    loop.exec();
    qDebug() << "finished request";
    mutex.lock();
    a = reply->readAll();
    QString answer(a);
    answer.remove("\0");
    qDebug() << answer;
    mutex.unlock();
    reply->deleteLater();
    ...
    

    Leider wird das QByteArray 'a' nicht in den QString 'answer' konvertiert. Praktisch gesehen, passiert einfach nichts;es steht nichts in der der Debug-Konsole. Auch Methoden wie fromAscii(a, a.count()-1) helfen nicht. Allerdings stehen die richtigen Werte im QByteArray. Die Null-Bytes bereinige ich auch schon ===> ich habe ehrlich gesagt keine Ahnung mehr was ich falsch mache.



  • Schaut irgendwie komisch aus. answer.remove("\0"); brauchst du sicher nicht. Warum deleteLater und nicht gleich? Ich würds mal mit UTF8 und nicht ascii probieren, bzw. gleich das richtige Encoding auslesen.
    So auf den ersten Blick seh ich keinen Fehler. Lass die Debug Ausgaben doch weg und debugs einfach durch, dann siehst du sofort, was in den Variablen steht und wo was schiefgeht.



  • Schaut irgendwie komisch aus. answer.remove("\0"); brauchst du sicher nicht.

    Das Entfernen änder nichts.

    Ich würds mal mit UTF8 und nicht ascii probieren

    UTF8 funktioniert auch nicht.

    Warum deleteLater und nicht gleich?

    Da war ich mir nicht ganz sicher. Wenn man QNetworkReply als Argument im Slot übergeben kriegt, muss man es mit deleteLater() löschen. Da ich QNetworkReply aber als Rückgabewert speichere, wusste ich nicht genau, was ich damit machen soll. deleteLater() hat sich erstmal am sichersten angehört.

    Lass die Debug Ausgaben doch weg und debugs einfach durch, dann siehst du sofort, was in den Variablen steht und wo was schiefgeht.

    Mein Debugger funktionierte nicht richtig. Ich habe ihn jetzt anhand von Google (hoffentlich langfrisitg) richtig eingerichtet. Beim debuggen wird der richtige Inhalt angezeigt. Warum qDebug den String nicht ausgeben will, weiss ich allerdings nicht.
    Danke nochmal für eure Antworten!



  • Ich hatte auch irgendwann mal das Problem, dass qDebug nicht funktioniert hat. Konnte das damals auch nicht nachvollziehen, war mir auch nicht wichtig.
    Aber dass dein Debugger nicht richtig funktioniert hat, find ich schon viel kritischer 😉 Ohne Debugger kann man überhaupt nicht sinnvoll arbeiten.


  • Mod

    Lass dir mal mit qDebug die Größe des Strings anzeigen, oder die ersten n Zeichen.
    Wenn du im Debugger siehst, das es da ist, kannst du ja schon mal den nächsten Schritt versuchen.

    Und mal so neben bei gefragt, wieso verbindest du finished() mit quit()?
    Dir ist schon klar, das dies eine Asynchrone API ist, und dein Code mit mutex in den Handler gehört?
    Hier findest du ein Beispiel für HTTP mit Qt:
    http://www.developer.nokia.com/Community/Wiki/Creating_an_HTTP_network_request_in_Qt



  • Ich habe mich nochmal eingelesen und einen erneuten [bis jetzt erfolgreichen Versuch] gestartet. Das Problem war wahrscheinlich, dass Google mich redirected hat. Deswegen hatte das Paket auch keinen Inhalt.
    Die redirection-Url kann man über

    if (!(sRedirection = _reply->attribute(QNetworkRequest::RedirectionTargetAttribute)).isNull()) 
        qDebug() << sRedirection.toString();
    

    abfragen.

    Und mal so neben bei gefragt, wieso verbindest du finished() mit quit()?
    Dir ist schon klar, das dies eine Asynchrone API ist

    Ja, und genau deswegen benutze ich eine EventLoop. Ich möchte einen synchronen Wrapper erstellen.


Anmelden zum Antworten