Pobieranie strony WWW/HTML z sieci za pomocą Qt



  • Pewnie wielu z Was zastanawiało się jak w prosty sposób można pobrać zawartość sieci, konkretnie stronę web za pomocą Qt i wykorzystać ją do własnych celów. Właśnie sam się nad tym zastanawiam.
    Podobno można wykorzystać klasę QNetworkAccessManager.
    Jeśli uda mi się coś napisać to na pewno zamieszczę tu rozwiązanie. Jeśli zaś Wy już macie swoje rozwiązania na ten problem zapraszam do dyskusji. ;)



  • Cześć tutaj na forum masz opisane w jaki sposób powinno być to wykonane: https://forum.qt.io/topic/3352/qnetworkaccessmanager-doesn-t-download-whole-web-page
    W skrócie dość prosto korzystasz z metody QNetworkAccesManager::get() i swoim slotem podpinasz się pod sygnał: QNetworkAccesManager::finished() następnie odczytujesz dane poprzez readAll()



  • Niestety mnie się nie udaje za pomocą sygnałów, QNetworkAccesManager, QNetworkReply i QNetworkRequest. Mój kod wygląda tak... ale nie ma żadnej reakcji.

    qDebug() << "Pobieranie strony WWW";
    QNetworkAccessManager manager;
    QNetworkReply *odpowiedz = NULL;
    QNetworkRequest request;
    //request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
    request.setHeader(QNetworkRequest::UserAgentHeader, "Cos 1.1");
    request.setPriority( QNetworkRequest::HighPriority );
    
    request.setUrl( QUrl( "http://www.theravenlords.za.pl" )); 
            //"http://old.radiopolska.pl/wykaz/galeria.php?l=A#0") );
    odpowiedz = manager.get( request );
    
    connect( odpowiedz, SIGNAL(finished()), this, SLOT( slot_ReplyFinished() ));
    connect( odpowiedz, SIGNAL(readyRead()), this, SLOT(slot_ReadyRead()));
    
    connect( odpowiedz, SIGNAL( error(QNetworkReply::NetworkError) ), this, SLOT( slot_blad() ));
    connect( odpowiedz, SIGNAL( sslErrors(QList<QSslError>)), this, SLOT( slot_SslBlad() ));
    

    Oczywiście slot_ReplyFinished() , slot_ReadyRead(), slot_blad(), slot_SslBlad() są moimi, przeze mnie zaprogramowanymi slotami. Wyglądało by na to, że sygnał finished() emitowany przez QNetworkReply nigdy nie jest emitowany.

    Co Wy na to ? :)



  • Problemem jest czas życia obiektu manager
    Zamień to:
    QNetworkAccessManager manager;
    na:
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    Powinno pomóc :)



  • O właśnie, wielgaśne dzięki. :)
    Dziś w dzień jak siedziałem nad tym zagadnieniem na korytarzu mojej uczelni doszedłem do tego rozwiązania, tylko tyle, że nie wiedziałem, że się to nazywa "czasem życia obiektu".
    Możesz mi wytłumaczyć dla czego ma to wpływ?
    Bo ja zrozumiałem, że jakoś, z tego powodu nie jest wywoływany sygnał finished() przez QNetworkAccessManager.

    W przypadku kiedy pisałem:

      QNetworkAccessManager manager;
      connect(&manager, SIGNAL( finished() ), this, SLOT( slot_zakonczony() ) );
    

    kod zdawał się nie działać, zawartość strony nie była pobierana...
    a w przypadku, kiedy robiłem tak jak zapisałeś poprzez wskaźnik i new kod działał.



  • W telegraficznym skrócie i maksymalnej prostocie to tworząc obiekt w jakiejś metodzie w taki sposób:
    QNetworkAccessManager manager;
    zostaje on niszczony po zakończeniu działania metody w której został stworzony. W związku z tym, że QNetworkAccessManager działa asynchronicznie i jego obiekt zostanie zniszczony przed zakończeniem requesta to sygnał nie zostanie wyemitowany.

    Tworząc obiekt przez operator 'new' nie zostanie on zniszczony po wyjściu z metody, lecz musisz pamiętać by samemu usunąć go w momencie gdy nie będzie Ci już potrzebny używając delete lub (zalecany dla QObjectów) deleteLater().
    Jeżeli tworząc obiekt przez operator new podasz mu w konstruktorze parenta to zostanie on zniszczony wraz z swoim rodzicem (gdybyś go sam nie usunął). Jeżeli nie usuniesz obiektu będziesz miał doczynienia z wyciekiem pamięci.
    W ramach nauki możesz stworzyć sobie dowolny QObject i podpiąć się slotem pod sygnał destroyed() wtedy zobaczysz kiedy jest on niszczony. :)
    Pozdrawiam.



  • Ja nie mowie za bardzo po polsku, jestem z niemec. Ale moze zrozumiece co pisze :) Use QEventLoop:

    QNetworkAccessManager nam;
    
    // Request
    QNetworkRequest request;
    request.setUrl(QUrl("http://www.google.de"));
    QNetworkReply *reply = nam.get(request);
    
    // Wait for server answer
    QEventLoop loop;
    connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
    loop.exec();
    
    // Read resoinse
    QByteArray response = reply->readAll(); // response == website data


  • @turaz
    Dzięki wielkie Turaz, rozjaśniłeś mi sprawę. :) W sumie masz rację, wcześniej nie dostrzegałem problemu długości życia zmiennych i wysyłanych przez nie sygnałów, teraz to widzę. :)
    Żeby connect() zadziałał utworzony prze zemnie obiekt musi istnieć po zakończeniu funkcji, a jak deklaruje go zwyczajnie/niedynamicznie to obiekt przestaje istnieć po zakończeniu funkcji w której jest tworzony. W sumie nawet zmienna - wskaźnik do utworzonego obiektu za pomocą operatora new też przestaje istnieć, więc pewnie wewnątrz funckji connect() robiona jest kopia wskaźnika... hmm... w sumie zawsze to tak działało. :)
    Dzięki wielkie za pomoc, zrobiłem sobie przy okazji praktyczne powtórzenie materiału. :)



  • @cybercatalyst
    Rozumiemy, nie martw się. :)
    Nigdy, jak dotąd nie korzystałem z klasy QEventLoop. Dzięki za podsunięcie pomysłu, kolejnego sposobu na rozwiązanie tego problemu. :)

    [ENG] If you don't understand what I wrote, I can rewrite this at english. :) I suppose you speak english. :)



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.