Qt — получение исходного кода (код HTML) веб-страницы, размещенной в Интернете

Я хочу получить исходный код (HTML) веб-страницы, например домашнюю страницу StackOverflow.

Это то, что я закодировал до сих пор:

QNetworkAccessManager manager;
QNetworkReply *response = manager.get(QNetworkRequest(QUrl(url)));

QString html = response->readAll(); // Source should be stored here

Но ничего не происходит! Когда я пытаюсь получить значение строки html, она пуста ("").

Так что делать? Я использую Qt 5.3.1.


person Alaa Elrifaie    schedule 25.07.2014    source источник


Ответы (3)


Вы должны добавить QEventLoop между ними.

QNetworkAccessManager manager;
QNetworkReply *response = manager.get(QNetworkRequest(QUrl(url)));
QEventLoop event;
connect(response,SIGNAL(finished()),&event,SLOT(quit()));
event.exec();
QString html = response->readAll(); // Source should be stored here
person MKAROL    schedule 25.07.2014
comment
Это плохой совет, поскольку вы пишете асинхронный код так, как если бы он был синхронным. Это не так. Если бы вы не забыли на самом деле exec() цикл обработки событий, вы подвергли бы спрашивающего произвольным последствиям event.exec() возможного повторного входа в этот метод или любые другие методы. Поскольку большинство людей не разрабатывают свой код с учетом таких сложностей, я считаю это источником неопределенного поведения, способного отформатировать ваш жесткий диск или нанести ядерный удар. Явно асинхронное кодирование с помощью C++11 и Qt 5 — более мирная альтернатива. - person Kuba hasn't forgotten Monica; 26.07.2014
comment
Ну, спрашивающий пытался получить HTML с помощью синхронного кода, поэтому я показал ему это решение. И иногда это проще сделать так. И спасибо, что указали мне на ошибку event.exec(). - person MKAROL; 26.07.2014
comment
Да, я согласен с тем, что внедрить неопределенное поведение в ваше приложение легко. Это не значит, что вы должны это делать. Qt делает асинхронное кодирование относительно простым благодаря сигналам/слотам даже в Qt 4. С C++11 и Qt 5 нет никаких оправданий тому, чтобы предлагать вращающиеся локальные циклы событий и тому подобное сумасшествие. - person Kuba hasn't forgotten Monica; 26.07.2014
comment
Большое спасибо. Людям не понравилось ваше решение, и они сказали, что оно действительно плохое, но на самом деле это единственное, что сработало для меня! Спасибо еще раз. - person Alaa Elrifaie; 27.07.2014

QNetworkAccessManager работает асинхронно. Вы звоните readAll() сразу после get(), но в этот момент запрос еще не сделан. Вам нужно использовать сигнал QNetworkAccessManager::finished, как показано в документации, и переместить readAll() в слот, подключенный к этому сигналу.

person Pavel Strakhov    schedule 25.07.2014

Вам нужно закодировать его асинхронным способом. На помощь приходят C++11 и Qt. Просто помните, что тело лямбды будет выполняться позже из цикла событий.

// https://github.com/KubaO/stackoverflown/tree/master/questions/html-get-24965972
#include <QtNetwork>
#include <functional>

void htmlGet(const QUrl &url, const std::function<void(const QString&)> &fun) {
   QScopedPointer<QNetworkAccessManager> manager(new QNetworkAccessManager);
   QNetworkReply *response = manager->get(QNetworkRequest(QUrl(url)));
   QObject::connect(response, &QNetworkReply::finished, [response, fun]{
      response->deleteLater();
      response->manager()->deleteLater();
      if (response->error() != QNetworkReply::NoError) return;
      auto const contentType =
            response->header(QNetworkRequest::ContentTypeHeader).toString();
      static QRegularExpression re("charset=([!-~]+)");
      auto const match = re.match(contentType);
      if (!match.hasMatch() || 0 != match.captured(1).compare("utf-8", Qt::CaseInsensitive)) {
         qWarning() << "Content charsets other than utf-8 are not implemented yet:" << contentType;
         return;
      }
      auto const html = QString::fromUtf8(response->readAll());
      fun(html); // do something with the data
   }) && manager.take();
}

int main(int argc, char *argv[])
{
   QCoreApplication app(argc, argv);
   htmlGet({"http://www.google.com"}, [](const QString &body){ qDebug() << body; qApp->quit(); });
   return app.exec();
}

Если вы не используете этот код только один раз, вы должны поместить экземпляр QNetworkManager в качестве члена вашего класса контроллера или в main и т. д.

person Kuba hasn't forgotten Monica    schedule 26.07.2014
comment
Просто спросите в своем первом операторе if, почему вы используете return, если нет NoError? - person reggie; 26.07.2014
comment
@reggie_jimac Я возвращаюсь, если есть ошибка (состояние ошибки отличное NoError). Если есть ошибка, скорее всего, достоверных данных нет, и дальнейшая обработка бессмысленна. - person Kuba hasn't forgotten Monica; 26.07.2014
comment
Использование объектов после вызова deleteLater можно считать плохим стилем. Если кто-то добавит операцию, вызывающую обработку событий в середине, код станет неявно недействительным. - person Pavel Strakhov; 27.07.2014
comment
@PavelStrakhov Вложенные циклы событий не обрабатывают события deleteLater именно по той причине, которую вы цитируете. Это еще одна причина, по которой притворно-синхронное программирование — это плохо. - person Kuba hasn't forgotten Monica; 27.07.2014