实现读取页面功能,用到QWebElement
头文件:
#ifndef ACCESSWEATHER_H
#define ACCESSWEATHER_H
#include <QObject>
class QNetworkReply;
class QNetworkRequest;
class QNetworkAccessManager;
class AccessWeather : public QObject
{
Q_OBJECT
signals:
void sgnFinished();
public:
AccessWeather(QObject *parent =0);
~AccessWeather();
QString getWeather(const QString& city);
private slots:
void slotFinished ( QNetworkReply * );
private:
void populateRequest(const QString& city, QNetworkRequest* req);
QString parseWeather(const QString& htmlData);
void saveToFile(const QString& strData, const QString& filename);
private:
QNetworkAccessManager *m_accessWeather;
QString m_curWeather;
};
#endif // ACCESSWEATHER_H
源文件:
//made by davidsu33
//2014-10-12 22:54
#include "accessweather.h"
#include <QDebug>
#include <QEventLoop>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QWebElement>
#include <QWebFrame>
#include <QWebPage>
#include <QFile>
#include <QPixmap>
#include <boost/config.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/foreach.hpp>
#include <sstream>
namespace
{
//活动指数
struct ActiveIndex
{
//程度
QString m_degree;
//建议
QString m_advices;
};
//天气提示
struct WeatherTooltip
{
//PM2.5
QString m_pm25;
//晨练指数
ActiveIndex m_MorningExerciseIndex;
//洗车指数
ActiveIndex m_WashingCarIndex;
//旅游指数
ActiveIndex m_tourismIndex;
};
struct SimpleWeatherInfo
{
QString m_desc;
QString m_temperature;
QString m_windForce;
QString m_weather;
QPixmap m_temperatureImg;
};
//天气信息
struct WeatherInfo
{
~WeatherInfo()
{
qDeleteAll(m_weekInfo);
}
QString m_title;
QString m_time;
QPixmap m_tqImg;
QString m_temperature;
QString m_cdRed;
QString m_windForce;
QString m_city;
WeatherTooltip m_tooltips;
QVector<SimpleWeatherInfo*> m_weekInfo;
};
//下载图片(同步)
void downloadImg(const QString& imgURL, QPixmap &pix)
{
QScopedPointer<QNetworkAccessManager> accessNetIcon(new QNetworkAccessManager);
QNetworkReply * reply = accessNetIcon->get(QNetworkRequest(QUrl(imgURL)));
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished ()), &loop, SLOT(quit()));
loop.exec();
pix.loadFromData(reply->readAll());
}
//提取今日天气信息
void pickTodayWeather(const QWebElement &e, WeatherInfo& wi)
{
wi.m_title = e.findFirst("h3").toPlainText();
QWebElement elemTime = e.findFirst("li.time");
wi.m_time = elemTime.toPlainText();
QWebElement img = e.findFirst("img");
QString imgURL = img.attribute("src");
//下载图片
downloadImg(imgURL, wi.m_tqImg);
wi.m_temperature = e.findFirst("span").toPlainText();
wi.m_cdRed = e.findFirst("li.cDRed").toPlainText();
BOOST_AUTO(liCollect, e.findAll("li"));
Q_FOREACH(const QWebElement&c, liCollect)
{
if(c.hasAttribute("style") && c.attribute("style")=="height:18px;overflow:hidden")
{
wi.m_windForce = c.toPlainText();
break;
}
}
wi.m_city = e.findFirst("input").attribute("value");
}
void pickTodayTooltips(const QWebElement &e, WeatherTooltip& wt)
{
BOOST_AUTO(liCollect, e.findAll("li"));
Q_FOREACH(const QWebElement&c, liCollect)
{
if(!c.hasAttribute("class"))
{
wt.m_pm25 = c.toPlainText();
}
if (c.toPlainText().contains("晨练指数"))
{
wt.m_MorningExerciseIndex.m_advices= c.findFirst("div").toPlainText();
wt.m_MorningExerciseIndex.m_degree = c.findFirst("span").toPlainText();
}
if (c.toPlainText().contains("洗车指数"))
{
wt.m_WashingCarIndex.m_advices= c.findFirst("div").toPlainText();
wt.m_WashingCarIndex.m_degree = c.findFirst("span").toPlainText();
}
if (c.toPlainText().contains("旅游指数"))
{
wt.m_tourismIndex.m_advices= c.findFirst("div").toPlainText();
wt.m_tourismIndex.m_degree = c.findFirst("span").toPlainText();
}
}
}
void appendNextWeather(const QWebElement &e, QVector<SimpleWeatherInfo*>& wi)
{
SimpleWeatherInfo *swi = NULL;
BOOST_AUTO(liCollect, e.findAll("div"));
Q_FOREACH(const QWebElement&c, liCollect)
{
swi = new SimpleWeatherInfo;
swi->m_desc = c.findFirst("h3").toPlainText();
QStringList strList;
BOOST_AUTO(divCollect, c.findAll("li"));
Q_FOREACH(const QWebElement&cc, divCollect)
{
strList.append(cc.toPlainText());
}
downloadImg(c.findFirst("img").attribute("src"), swi->m_temperatureImg);
if(strList.size() == 4)
{
swi->m_temperature = strList[1];
swi->m_weather = strList[2];
swi->m_windForce = strList[3];
}
wi.append(swi);
}
}
}
AccessWeather::AccessWeather(QObject *parent)
: QObject(parent)
{
m_accessWeather = new QNetworkAccessManager(this);
connect(m_accessWeather,
SIGNAL(finished ( QNetworkReply * )),
this,
SLOT(slotFinished ( QNetworkReply * ))
);
}
AccessWeather::~AccessWeather()
{
}
QString AccessWeather::getWeather( const QString& city )
{
m_curWeather.clear();
QNetworkRequest req;
populateRequest(city, &req);
m_accessWeather->get(req);
//wait for get weather
QEventLoop loop;
connect(this, SIGNAL(sgnFinished ()), &loop, SLOT(quit()));
loop.exec();
QString r = parseWeather(m_curWeather);
return r;
}
void AccessWeather::slotFinished( QNetworkReply * reply)
{
QNetworkReply::NetworkError err = reply->error();
if(QNetworkReply::NoError != err)
{
emit sgnFinished();
return;
}
QUrl newUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if (!newUrl.isEmpty())
{
//读取新的地址
m_accessWeather->get(QNetworkRequest(newUrl));
}
else
{
QByteArray ba = reply->readAll();
m_curWeather = ba;
qDebug()<<ba;
emit sgnFinished();
}
int bp = 0;
}
void AccessWeather::populateRequest( const QString& city, QNetworkRequest* req )
{
QString percentEncode = city.toLocal8Bit().toPercentEncoding();
QString strReqestLine = QString("http://www.tianqi.com/index.php?c=tianqi&a=search&city=%1").arg(percentEncode);
//如果不使用这个,则会导致url.encodedQuery()重新的错误解析而导致错误(编码格式问题)
QUrl url = QUrl::fromEncoded(strReqestLine.toAscii());
QString rurl = url.encodedPath();
QString rurl2 = url.encodedQuery();
req->setUrl(url);
//req->setRawHeader("Host", "http://www.tianqi.com/");//不需要设置Host,默认会有,否则会导致错误的信息提交而获取不到正确信息
//下列信息可有可无,如果没有则会有默认的值
req->setRawHeader("Connection", "keep-alive");
req->setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36");
req->setRawHeader("Referer", "http://www.tianqi.com/");
req->setRawHeader("Accept-Encoding", "gzip,deflate,sdch");
req->setRawHeader("Accept-Language", "zh-CN,zh;q=0.8");
req->setRawHeader("Cookie", "cityPy=baoding; AJSTAT_ok_pages=1; AJSTAT_ok_times=1; bdshare_firstime=1412864742830");
req->setRawHeader("Content-Type", "text/html; charset=GBK");
//GET /index.php?c=tianqi&a=search&city=%E7%9F%B3%E5%AE%B6%E5%BA%84 HTTP/1.1\r\n //直接使用中文'石家庄'而导致的错误
//不使用QUrl::fromEncoded而导致的错误,会将%重新解析为%25
//GET /index.php?c=tianqi&a=search&city=%25%CA%25%AF%25%BC%25%D2%25%D7%25%AF HTTP/1.1\r\n
//正确的提交结果
//GET /index.php?c=tianqi&a=search&city=%CA%AF%BC%D2%D7%AF HTTP/1.1\r\n
}
QString AccessWeather::parseWeather( const QString& htmlData )
{
namespace BoostPT = boost::property_tree;
typedef BoostPT::ptree BoostPTree;
QWebPage page;
QWebFrame &frame = *page.mainFrame();
frame.setHtml(htmlData);
QEventLoop loop;
connect(&frame, SIGNAL(loadFinished(bool)), &loop, SLOT(quit()));
loop.exec();
QWebElement elem = frame.documentElement();
//findAll递归查找根据CSS selector selectorQuery
BOOST_AUTO(collect, elem.findAll("div"));
int sz = collect.count();
QList<QWebElement> d = collect.toList();
WeatherInfo wi;
Q_FOREACH(const QWebElement &e, collect)
{
if(e.hasClass("tqshow"))
{
pickTodayWeather(e, wi);
}
if(e.hasClass("today_data_r01"))
{
pickTodayTooltips(e, wi.m_tooltips);
}
if(e.hasClass("everytqshow"))
{
appendNextWeather(e, wi.m_weekInfo);
}
}
//GBK编码,系统默认是GBK编码
//如果不合适可以通过setCodecForTr来调节
// std::string strData = htmlData.toAscii();
// std::istringstream istrstem(strData);
//回传数据为HTML非标准的XML,不能正确解析,需要使用QWebKit包解析
//BoostPTree pt;
//try
//{
// BoostPT::read_xml<BoostPTree>(istrstem, pt);
//}
//catch (const boost::property_tree::file_parser_error& e)
//{
// qDebug()<<"what:"<<e.what()<<" message:"<<e.message().c_str();
// assert(false);
//}
//
//QString result;
//QString prefix = "html.body.div.div.div.div.div.div.div.div.div.div";
//QString attrClassKey = QString("%1.<xmlattr>.class").arg(prefix);
//QString attrIDKey = QString("%1.<xmlattr>.id").arg(prefix);
//std::string today = pt.get<std::string>(attrClassKey.toStdString());
//std::string todayID = pt.get<std::string>(attrIDKey.toStdString());
return QString::null;
}
void AccessWeather::saveToFile( const QString& strData, const QString& filename )
{
QFile file(filename);
file.open(QFile::WriteOnly);
file.write(strData.toLocal8Bit());
file.close();
}