早期写的一份课表抓取工具,原本是直接解析字符串数据来获取课程信息,代码比较粗糙,这里把当时核心的 Qt 登录控件实现整理出来作为记录。

实现思路

整体流程基于 Qt 的 QNetworkAccessManager 模拟浏览器请求教务系统:先抓取登录页拿到 __VIEWSTATE 和 Set-Cookie 中的 jsessionid,再拉取验证码图片让用户肉眼识别,提交表单完成登录,随后依据请求 URL 分发到课表页面或成绩页面的处理分支。课表页面使用字符串的 indexOf / mid 逐段切分 HTML,把表格内容还原成二维结构填入内部的 dataTable。

核心代码

login_widget::login_widget(QWidget *parent) ://QWidget(parent),
                ui(new Ui::login_widget)
                {
                
                	ui->setupUi(this);
                	p = (MainWindow*)parent;
                
                	//loading = new loading_widget(this);
                
                
                #ifndef Q_OS_WIN32
                	FixScreen();
                #endif
                
                
                	timer_timeout = new QTimer(this);
                
                	connect(timer_timeout, SIGNAL(timeout()), this, SLOT(slot_timer()));
                
                	manager = new QNetworkAccessManager(this);
                
                
                	connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(doManagerFinished(QNetworkReply*)));
                
                	connect(ui->pushButton_2, SIGNAL(clicked()), this, SLOT(slot_verfy()));
                	connect(ui->pushButton_3, SIGNAL(clicked()), this, SLOT(slot_close()));
                	connect(ui->pushButton_4, SIGNAL(clicked()), this, SLOT(slot_shuom()));
                
                	QMovie *pm = new QMovie(":/loading.gif");
                
                	ui->label_7->setMovie(pm);
                
                	pm->setScaledSize(QSize(100 * PublicFunc::getFactory(), 30 * PublicFunc::getFactory()));
                
                	pm->start(); //才会开始动
                
                	QPixmap pix;
                	pix.load(":/1.png");
                
                	ui->label_6->setPixmap(pix);
                
                	setHide();
                
                
                }
                
                
                
                void login_widget::slot_timer()
                {
                	timer_timeout->stop();
                	setHide();
                
                	QMessageBox::warning(this, char_net_error, char_net_error_center);
                
                	slot_close();
                
                
                }
                
                
                
                
                void login_widget::setShow()
                {
                	ui->label_6->setVisible(true);
                	ui->label_7->setVisible(true);
                
                }
                
                
                void login_widget::setStart(int time)/*超时15 S*/
                {
                	timer_timeout->start(time);
                
                }
                
                
                void   login_widget::setHide()
                {
                
                	ui->label_6->setVisible(false);
                	ui->label_7->setVisible(false);
                
                }
                
                
                
                
                
                void login_widget::setLoginVisiable(bool is)
                {
                	ui->pushButton->setVisible(is);
                
                	ui->pushButton_2->setVisible(is);
                
                	ui->lineEdit->setVisible(is);
                
                	ui->lineEdit_2->setVisible(is);
                
                	ui->lineEdit_3->setVisible(is);
                
                	ui->label->setVisible(is);
                
                	ui->label_2->setVisible(is);
                
                	ui->label_3->setVisible(is);
                
                	ui->label_4->setVisible(is);
                
                }
                
                
                
                
                
                void login_widget::doManagerFinished(QNetworkReply *reply)
                {
                	timer_timeout->stop();
                
                	QByteArray binary = reply->readAll();
                	if (binary.size() <= 0)
                	{
                		setHide();
                		QMessageBox::warning(this, "\347\275\221\347\273\234\351\224\231\350\257\257", "\350\257\267\346\243\200\346\237\245\347\275\221\347\273\234\350\277\236\346\216\245");
                		
                	//reply->deleteLater();
                		delete reply;
                
                		return;
                
                	}
                
                
                
                	QString result = QTextCodec::codecForName("gb2312")->toUnicode(binary);
                	if (reply->url() == homepageUrl)
                	{
                		/*登陆*/
                
                		if (reply->operation() == QNetworkAccessManager::GetOperation)
                		{
                			QRegExp reg("name=\"__VIEWSTATE\" value=\"([^\"]+)\"");
                			if (reg.indexIn(result) != -1)
                			{
                				viewstate = reg.cap(1);
                			}
                
                			if (reply->rawHeader("Set-Cookie").length())
                			{
                				jsessionid = reply->rawHeader("Set-Cookie");
                				jsessionid = jsessionid.left(jsessionid.indexOf(';'));
                			}
                			else
                			{
                
                			}
                
                			slot_verfy();
                		}
                		else
                		{
                
                			setStart();
                			QNetworkRequest request;
                			request.setRawHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.3; WOW64; Trident/8.0)");
                			request.setRawHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
                			request.setRawHeader("Accept-Encoding", "gzip, deflate");
                			request.setRawHeader("Accept-Language", "zh-CN,zh;q=0.8");
                			request.setRawHeader("Cache-Control", "max-age=0");
                			request.setRawHeader("Cookie", jsessionid.toLatin1());
                			request.setRawHeader("Connection", "Keep-Alive");
                
                			//     qDebug() << mainpageUrl;
                			request.setUrl(QUrl(mainpageUrl));
                			manager->get(request);
                
                
                			setShow();
                		}
                	}
                
                
                	else if (reply->url() == captchaUrl)
                	{
                		/*验证码*/
                		QPixmap pixmap;
                
                		if (pixmap.loadFromData(binary))
                		{
                
                			setHide();
                
                
                			ui->label->setVisible(true);
                			ui->pushButton->setVisible(true);
                
                
                #ifndef Q_OS_WIN32
                			ui->label->setPixmap(pixmap.scaled(72 * 2.2 * PublicFunc::getFactory(), 27 * 2.2*PublicFunc::getFactory(), Qt::KeepAspectRatio));
                #else
                			ui->label->setPixmap(pixmap);
                #endif
                
                		}
                	}
                
                	else if (reply->url() == url_kebiao)
                	{
                		/*课表*/
                
                		dataTable.clear();
                
                		//ui->textEdit->append(QString::fromLocal8Bit(binary.data()));
                		
                		QString data = QString::fromLocal8Bit(binary.data());
                
                		/**/
                		int x = data.indexOf("trbg1") + 10;
                		int y = data.indexOf("datelist");
                		data = data.mid(x, y - x);
                
                		x = data.indexOf("trbg1") + 10;
                
                
                		info = (data.mid(x, data.indexOf("labTip") - x));
                
                
                		x = data.indexOf("Table1") - 11;
                		y = data.indexOf("</table>");
                
                		data = data.mid(x, data.size() - x);
                		data = data.mid(0, data.indexOf("</table>"));
                
                
                		x = data.indexOf("</tr>") + 5;
                		data = data.mid(x, -1);
                
                		x = data.indexOf("</tr>") + 5;
                		data = data.mid(x, -1);
                
                		QString tm;
                
                		x = info.indexOf("&nbsp");
                		info = info.mid(0, x);
                
                		x = info.indexOf(">");
                		info = info.mid(x + 1, -1);
                
                		info.remove("|");
                
                		info.remove("</span>");
                
                		info.remove(info.indexOf("<"), info.indexOf(">") - info.indexOf("<") + 1);
                		info.remove(info.indexOf("<"), info.indexOf(">") - info.indexOf("<") + 1);
                		info.remove(info.indexOf("<"), info.indexOf(">") - info.indexOf("<") + 1);
                		info.remove(info.indexOf("<"), info.indexOf(">") - info.indexOf("<") + 1);
                		info.remove(info.indexOf("<"), info.indexOf(">") - info.indexOf("<") + 1);
                
                		info.remove("\t");
                
                		setHide();
                
                
                		info.insert(0, "\n");
                
                		QMessageBox::warning(this, "\350\257\276\350\241\250\344\277\241\346\201\257", info);/*课表信息*/
                
                		QSettings set("kebiao", "1");
                		set.setValue("info", info);
                
                		/*分割完成 接下来 提取每一行数据 一共14行*/
                
                
                		for (int i = 0; i < 14; i++)
                		{
                			x = data.indexOf("<tr>");
                			y = data.indexOf("</tr>") + 5;
                			class_arry[i] = data.mid(x, y - x);
                			data = data.mid(y, -1);
                
                			//	data.remove(class_arry[i]);
                
                		}
                
                		/*提取每一行完毕 开始分割列*/
                		QString day[15];
                
                		QString tmp;
                
                		for (int xx = 0; xx < 14; xx += 2)
                		{
                
                			class_arry[xx].remove("</tr>");
                			class_arry[xx].remove("<tr>");
                
                
                			for (int i = 0; i < 7; i++)
                			{
                				x = class_arry[xx].lastIndexOf("<td");
                				y = class_arry[xx].lastIndexOf("</td>") + 5;
                				day[i] = class_arry[xx].mid(x, y - x);
                
                				class_arry[xx] = class_arry[xx].mid(0, x);
                				day[i].remove(day[i].mid(0, day[i].indexOf(">") + 1));
                
                				day[i].remove(" </td>");
                
                				day[i].remove("</td>");
                				day[i].replace("<br>", ".");
                
                
                				/*取出每一节课完成 开始取出每一节课信息*/
                
                				for (int iii = 0; iii < 3; iii++)
                				{
                
                					/*取出名字*/
                					y = day[i].indexOf(".");
                					tmp = day[i].mid(0, y);
                					dataTable.data[6 - i][xx][iii].class_name = tmp;
                					//	day[i].remove(tmp + ".");
                
                					day[i] = day[i].mid(y + 1, -1);
                
                					/*课程性质*/
                					y = day[i].indexOf(".");
                					tmp = day[i].mid(0, y);
                					dataTable.data[6 - i][xx][iii].class_pro = tmp;
                					tmp = tmp + ".";
                					//	day[i].remove(tmp);
                
                					day[i] = day[i].mid(y + 1, -1);
                
                
                					/*时间*/
                					y = day[i].indexOf(".");
                					tmp = day[i].mid(0, y);
                					dataTable.data[6 - i][xx][iii].class_time = tmp;
                					tmp = tmp + ".";
                					//day[i].remove(tmp);
                
                					day[i] = day[i].mid(y + 1, -1);
                
                
                					/*老师*/
                					y = day[i].indexOf(".");
                					tmp = day[i].mid(0, y);
                					dataTable.data[6 - i][xx][iii].class_techer = tmp;
                					tmp = tmp + ".";
                					//day[i].remove(tmp);
                					day[i] = day[i].mid(y + 1, -1);
                
                
                
                					/*地点*/
                					y = day[i].indexOf(".");
                					if (y <= 0)
                					{
                						dataTable.data[6 - i][xx][iii].class_posi = day[i];
                						day[i] = "";
                					}
                					else
                					{
                
                						tmp = day[i].mid(0, y);
                						dataTable.data[6 - i][xx][iii].class_posi = tmp;
                						tmp = tmp + "..";
                						//day[i].remove(tmp);
                
                						day[i] = day[i].mid(y + 2, -1);
                
                
                					}
                					if (day[i].size() < 1)break;
                
                				}
                
                				/*	QTableWidgetItem* z = new QTableWidgetItem(day[i]);
                					z->setTextAlignment(Qt::AlignCenter);
                
                
                					p->ui->tableWidget->setItem(xx, 6 - i, z);
                					p->ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); //设置不可编辑
                
                					p->ui->tableWidget->horizontalHeader()->resizeSection(6 - i, 80 * PublicFunc::getFactory()); //设置表头第一列的宽度为150
                
                					p->ui->tableWidget->setRowHeight(xx, PublicFunc::getFactory() * 100);
                					p->ui->tableWidget->setRowHeight(xx + 1, 0);
                
                					QFont font;
                					font.setPointSize(12);
                
                
                					font.setFamily("simsun");
                
                					z->setFont(font);*/
                
                			}
                
                
                		}
                		p->syncTable(setCenter::current_week);
                
                
                		QMessageBox::information(this, "\346\217\220\347\244\272", "\345\257\274\345\205\245\350\257\276\350\241\250\346\210\220\345\212\237");/*错误*/
                
                		/*提示 导入课表成功*/
                
                
                		ui->pushButton->setVisible(true);
                
                		slot_close();
                
                	}
                
                
                	else if (reply->url() == url_chenji)
                	{
                
                		if (reply->operation() == QNetworkAccessManager::GetOperation)
                		{
                			/*get*/
                			QRegExp reg("name=\"__VIEWSTATE\" value=\"([^\"]+)\"");
                			if (reg.indexIn(result) != -1)
                			{
                				viewstate = reg.cap(1);
                			}
                
                			if (reply->rawHeader("Set-Cookie").length())
                			{
                				jsessionid = reply->rawHeader("Set-Cookie");
                				jsessionid = jsessionid.left(jsessionid.indexOf(';'));
                			}
                
                			setStart();
                		
                			QNetworkRequest request;
                			request.setRawHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.3; WOW64; Trident/8.0)");
                			request.setRawHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
                			request.setRawHeader("Accept-Encoding", "gzip, deflate");
                			request.setRawHeader("Accept-Language", "zh-CN,zh;q=0.8");
                			request.setRawHeader("Cache-Control", "max-age=0");
                			request.setRawHeader("Cookie", jsessionid.toLatin1());
                			request.setRawHeader("Connection", "Keep-Alive");
                		
                			request.setRawHeader("Referer", ss.toStdString().c_str());
                
                			QUrl mainpageUrl = QUrl(ss);
                			request.setUrl(QUrl(url_chenji));
                
                			QUrlQuery query;
                			query.addQueryItem("__EVENTTARGET", "ddlxn");
                			query.addQueryItem("__EVENTARGUMENT", "");
                			query.addQueryItem("__VIEWSTATE", viewstate);
                			QString xueqi;
                			char xuen[30];
                
                
                			QDate date = QDate::currentDate();
                
                			if (date.month() >= 6 && date.month() <= 10)
                			{
                				/*第二学期*/
                				xueqi = "2";
                				sprintf(xuen, "%d-%d", date.year() - 1, date.year());
                			}
                			else 	if (date.month() > 10)
                			{
                				/*第1学期*/
                				xueqi = "1";
                				sprintf(xuen, "%d-%d", date.year(), date.year() + 1);
                			}
                
                			else
                			{
                				/*第1学期*/
                				xueqi = "1";
                				sprintf(xuen, "%d-%d", date.year() - 1, date.year());
                
                			}
                			query.addQueryItem("ddlxn", xuen);
                			query.addQueryItem("ddlxq", xueqi);
                
                			query.addQueryItem("btnCx", "+%B2%E9++%D1%AF+");
                
                			QByteArray postData = query.toString(QUrl::FullyEncoded).replace("+", "%2B").toLatin1();
                
                			manager->post(request, postData);
                
                			setShow();
                
                
                		}
                		else if (reply->operation() == QNetworkAccessManager::PostOperation)
                		{
                			/*post*/
                
                
                			ui->pushButton->setVisible(true);
                			setHide();
                
                			do_chengji(binary);
                
                		}
                
                	}
                	else
                	{
                
                		setHide();
                
                		/*登陆后*/
                		QString s = binary.data();
                
                		if (s.indexOf("main_html") <= 0)
                		{
                			/*登陆失败*/
                			slot_verfy();
                			QMessageBox::warning(this, "\351\224\231\350\257\257", "\345\257\206\347\240\201\346\210\226\351\252\214\350\257\201\347\240\201");/*错误*/
                
                			/*登陆失败*/
                		}
                		else
                		{
                			/*登陆成功 开始获取课表*/
                
                			if (is_kebiao)
                			{
                				slot_getKebiao();
                			}
                			else
                				slot_getChenJi();
                
                			//	setLoginVisiable(false);
                
                		}
                	}
                
                
                	//reply->deleteLater();
                	delete reply;
                
                }
                
                
                
                void login_widget::on_pushButton_clicked()
                {
                
                
                	/*输入规则判断*/
                	if (ui->lineEdit_2->text().length() < 11)
                	{
                		QMessageBox::warning(this, char_tixing, "\350\257\267\346\255\243\347\241\256\350\276\223\345\205\245\345\255\246\345\217\267");
                
                		return;
                	}
                
                
                	if (ui->lineEdit_3->text().length() == 0)
                	{
                		QMessageBox::warning(this, char_tixing, "\345\257\206\347\240\201\344\270\215\350\203\275\344\270\272\347\251\272");
                
                		return;
                	}
                
                	if (ui->lineEdit->text().length() == 0)
                	{
                		QMessageBox::warning(this, char_tixing, "\350\257\267\350\276\223\345\205\245\351\252\214\350\257\201\347\240\201");
                
                		return;
                	}
                
                
                	if (ui->lineEdit->text().length() < 4)
                	{
                		QMessageBox::warning(this, char_tixing, "\351\252\214\350\257\201\347\240\201\351\224\231\350\257\257");
                
                		return;
                	}
                
                	ui->pushButton->setVisible(false);
                
                
                	/*登陆*/
                
                	setStart();
                	setShow();
                
                	QNetworkRequest request;
                	request.setUrl(homepageUrl);
                	request.setRawHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.3; WOW64; Trident/8.0)");
                	request.setRawHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
                	request.setRawHeader("Accept-Encoding", "gzip, deflate");
                	request.setRawHeader("Accept-Language", "zh-CN,zh;q=0.8");
                	request.setRawHeader("Cache-Control", "max-age=0");
                	request.setRawHeader("Cookie", jsessionid.toLatin1());
                	request.setRawHeader("Connection", "Keep-Alive");
                
                	query.addQueryItem("txtUserName", ui->lineEdit_2->text());
                	query.addQueryItem("TextBox2", ui->lineEdit_3->text());
                	query.addQueryItem("txtSecretCode", ui->lineEdit->text());
                	query.addQueryItem("RadioButtonList1", "%D1%A7%C9%FA");
                	QByteArray postData = query.toString(QUrl::FullyEncoded).replace("+", "%2B").toLatin1();
                
                	//  qDebug() << postData;
                
                	request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
                	request.setHeader(QNetworkRequest::ContentLengthHeader, postData.length());
                	manager->post(request, postData);
                
                
                }
                
                
                
                void login_widget::slot_getChenJi()
                {
                	setShow();
                
                	setStart();
                
                
                	QNetworkRequest request;
                	request.setRawHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.3; WOW64; Trident/8.0)");
                	request.setRawHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
                	request.setRawHeader("Accept-Encoding", "gzip, deflate");
                	request.setRawHeader("Accept-Language", "zh-CN,zh;q=0.8");
                	request.setRawHeader("Cache-Control", "max-age=0");
                	request.setRawHeader("Cookie", jsessionid.toLatin1());
                	request.setRawHeader("Connection", "Keep-Alive");
                
                	request.setRawHeader("Referer", ss.toStdString().c_str());
                
                
                	QUrl mainpageUrl = QUrl(ss);
                	request.setUrl(QUrl(url_chenji));
                	manager->get(request);
                
                
                }
                
                
                
                
                
                
                void login_widget::slot_close()
                {
                
                	manager->clearAccessCache();
                	
                	timer_timeout->stop();
                	setHide();
                
                	this->hide();
                }