一个C++解析HTML的库

2023-05-16

HTTP协议使用广泛,相应的,C++在这块需求也开始增加。一个好的解析库可以达到事半功倍的效果,在此贴出我的解析库的代码,方便新手朋友们使用。

hHttpParse.h

#ifndef __H_HTML_PARSE_H__
#define __H_HTML_PARSE_H__

#pragma once

#include <Windows.h>
#include <WinInet.h>
#include <string>
#include <cstdio>

class hHtmlParse {
	std::string data;
	int p;
public:
	//构造函数,传入HTML代码
	hHtmlParse (std::string& data);
	//获取网页的编码方式
	bool GetCharset (std::string& s);
	//设置当前解析位置
	bool SetPos (const char* find);
	//设置当前解析位置(反向查找目标位置)
	bool SetPos_LastOf (const char* find);
	//查找是否存在目标位置,不会更新当前位置
	bool find (const char* find);
	//匹配一串字符串,使用sscanf_s获取
	bool MatchString (const char* match, std::string& s);
	//获取当前电脑IP地址
	static bool GetLocalIp (std::string& ip);
	//查询某地址或某域名信息
	static bool GetAddrMessage (const wchar_t* addr, std::string& data);
	//关键函数,获取lpURL指向的地址的HTML代码,并存入data中
	static bool UrlGetHtml (LPCWSTR lpURL, std::string& data);
};

#endif //__H_HTML_PARSE_H__


hHttpParse.cpp

#include "hHtmlParse.h"

#pragma comment(lib, "WinInet.lib")

hHtmlParse::hHtmlParse (std::string& data) {
	this->data = data;
	this->p = 0;
}

bool hHtmlParse::GetCharset (std::string& s) {
	this->SetPos ("charset=");
	return this->MatchString ("%*[\"]%[^\"]", s);
}

bool hHtmlParse::SetPos (const char* find) {
	int t = this->data.find (find, p);
	if (-1 == t) return false;
	this->p = t + strlen (find);
	return true;
}

bool hHtmlParse::SetPos_LastOf (const char* find) {
	int t = this->data.rfind (find);
	if (-1 == t) return false;
	this->p = t + strlen (find);
	return true;
}

bool hHtmlParse::find (const char* find) {
	int t = this->data.find (find, p);
	return t != -1;
}

bool hHtmlParse::MatchString (const char* match, std::string& s) {
	return sscanf_s (&data.c_str () [p], match, const_cast<char*>(s.c_str ()), s.capacity ()) > 0;
}

bool hHtmlParse::GetLocalIp (std::string& ip) {
	std::string page, match;
	page.resize (512);
	match.resize(16);
	if (!hHtmlParse::UrlGetHtml (L"http://1111.ip138.com/ic.asp", page)) return false;
	hHtmlParse hp (page);
	hp.SetPos ("<center>");
	hp.MatchString ("%*[^0-9]%[0-9.]", match);
	ip.clear ();
	ip = match.c_str ();
	return true;
}

bool hHtmlParse::GetAddrMessage (const wchar_t* addr, std::string& data) {
	data.clear ();
	std::wstring link = L"http://www.ip138.com/ips138.asp?ip=";
	std::string page, match;
	link += addr;
	page.resize (16384);
	match.resize (64);
	if (!hHtmlParse::UrlGetHtml (link.c_str (), page)) return false;
	hHtmlParse hp (page);
	hp.SetPos_LastOf ("<table");
	hp.SetPos ("<td");
	hp.SetPos ("<td");
	bool b = true;
	if (hp.find (">>")) {
		b = false;
		hp.SetPos (">>");
		hp.MatchString ("%*[^0-9]%[0-9.]", match);
		data = match.c_str ();
	}
	hp.SetPos ("<ul");
	while (hp.find ("<li")) {
		hp.SetPos (":");
		hp.MatchString ("%[^<]", match);
		if (b) b = false; else data += "\n";
		data += match.c_str ();
	}
	return true;
}

bool hHtmlParse::UrlGetHtml (LPCWSTR lpURL, std::string& data) {
	HINTERNET hSession = InternetOpenW (L"EmotionSniffer", NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE);
	if (!hSession) return FALSE;
	HINTERNET hFile = InternetOpenUrlW (hSession, lpURL, NULL, NULL, INTERNET_FLAG_RELOAD, NULL);
	if (!hFile) {
		InternetCloseHandle (hSession); return FALSE;
	}
	DWORD dwW = 0, dwR = 0;
	int capacity = data.capacity ();
	do {
		dwW += dwR;
		if (dwW != 0 && dwR == 0) break;
		if (dwW + 1024 >= capacity) data.resize (capacity *= 2);
	} while (InternetReadFile (hFile, (LPVOID) (data.c_str () + dwW), 1024, &dwR));
	const_cast<char*>(data.c_str ()) [dwW] = '\0';
	InternetCloseHandle (hFile);
	InternetCloseHandle (hSession);
	return TRUE;
}

简要说明下使用方法。首先是封装的三个静态函数, GetLocalIp 和 GetAddrMessage 这俩是通过调用 www.ip138.com 动态查询获取的结果,调用这个库实现。使用这个库时可以参照上面两个函数的代码; UrlGetHtml 是通过 Windows 的 Internet API 实现从 URL 指定的地址下载网页。

重点说说这个库的使用方法,我就说说 hHtmlParse::GetLocalIp 则函数的实现,方法大多类似。

1、字符串定义

std::string page, match;
page.resize (512);
match.resize (16);

其中 page 用作保存 HTML 代码, match 用作保存匹配的字符串,也就是网页中需要获取的数据。由于访问的数据不大,所以 page 设置 512 字节足够。这儿也可以不用设置大小的, UrlGetHtml 实现的比较智能,可以自动扩展大小。设置一个大小只是可以减少内存 I/O ,提高执行速度。另外这儿也给 match 设置一个大小。对于IP地址来说,16字节足够了。

2、获取网页HTML代码

if (!hHtmlParse::UrlGetHtml (L"http://1111.ip138.com/ic.asp", page)) return false;
这句话的意思就是下载网页并将网页代码保存在 page 中,如果执行失败则返回。

3、创建解析对象

hHtmlParse hp (page);
这句代码用于创建一个解析对象,传入的数据为网页HTML字符串。
这里我们看看ip138的网页代码:

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=gb2312">
<title> 您的IP地址 </title>
</head>
<body style="margin:0px"><center>您的IP是:[123.144.*.*] 来自:XX市 联通</center></body></html>
地址和位置打码了,大家看得懂就行了。我们先来分析分析,需要获取地址的代码前面有一个 <center> 是吧?那就把位置设置在这儿吧。。。
4、设置当前解析位置

hp.SetPos ("<center>");
在这个解析库内部维护着一个字符指针,假如说网页前面的都解析过了,需要解析后面的,那就在解析时给字符指针赋值,然后下次解析时从字符指针位置开始解析,既方便网页处理,也提高解析速度,一举两得。这行代码的意思就是将内部维护的地址放在 <center> 这儿,下次调用时直接从这儿开始了。

5、获取匹配字符串,也就是IP地址

hp.MatchString ("%*[^0-9]%[0-9.]", match);
这儿就是获取匹配字符串的代码了 ,第一个参数为sscanf函数需要的那个字符串,match返回匹配的结果。简要说说这个字符串的意思,需要深究的自行bing。

%*[^0-9]   %*表示跳过,不匹配,[]表示匹配的内容,^表示非,0-9表示那十个数字。连起来的意思就是跳过所有不是0-9的字符。

%[0-9.]   %表示匹配,0-9.表示匹配的内容为那十个数字和小数点,一直到非0-9或小数点为止。

代码到这儿就已经获取了匹配的ip了,接下来的内容就是简单的处理了。

6、简单处理并返回

ip.clear ();
ip = match.c_str ();
return true;

由于匹配是通过 const_cast<char*>(s.c_str()) 赋值(这行代码可以在 hHtmlParse::MatchString 函数中找到),所以,在执行步骤6这段代码前,调用 match.length() 实际上返回的是不固定值,虽然不固定但是有数据。所以,假如非得在 const_cast<char*>(s.c_str()) 之后获取字符串长度,只能用lstrlen。

这几行代码大家应该都懂,简要说说第二行,意思是调用 std::string 的重载函数 operator=(char*) ,这样可以刷新 std::string 的长度,执行第二行代码之后,ip中的数据可以直接调用 length() 来获取长度了。

使用步骤描述完毕,同学们如果需要解析其他网页在相应地方修改就行了。另外, SetPos 和 MatchString 并不是只能调用一次的,只要找网页方便,并且保证对象内部维护的指针还没到末尾,就可以重复调用这些函数了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一个C++解析HTML的库 的相关文章

  • 如何禁用 mui 文本字段自动完成?

    我正在使用最新版本的 mui 我有一个包含邮政编码字段的用户联系信息表单 如果值为空 我不希望此字段自动完成 但它会随着浏览器中保存的电子邮件自动完成 这是我到目前为止所尝试过的 自动完成 关闭 自动完成 关闭 自动完成 不 这是我的文本字
  • 折叠和展开选项卡 jquery / 简单的手风琴

    我对手风琴选项卡有疑问 我用过手风琴菜单插件 下面的代码我用于页面中的选项卡 accordions accordion title about Content 1 accordion accordion title Home Content
  • TypeError:req.checkBody 不是包含 bodyparser 和expressvalidator 模块的函数

    我收到错误 req checkBody 不是一个函数 我认为我已经包含了express validator和body parser 这是我的代码 var express require express var bodyParser requ
  • 如何从模板脚本访问 AngularJS 变量

    我的控制器 scope totals totals 我的模板 按 html 注入的预期工作 totals 但我需要的是访问变量totals在模板的脚本中 如下所示 我试过了 scope totals totals totals 等 均无济于
  • 如何在 div 标签上添加带边框的三角形

    我有一个 div 标签 我想在它上面添加一个小三角形 注意 我希望我的 div 标签具有某种颜色的边框 以及另一种颜色的 div 主体 假设我的 div 背景为白色 边框为蓝色 请看这个 http fiddle jshell net pau
  • 如何在
    CSS 中正确定位箭头

    我想更改详细信息中出现的箭头的位置 我尝试过 float left 但如果线条太大 如上面示例中的线条 则当我调整窗口大小时 箭头将移动到下方线条的开头 我希望它保留在第一行的第一个字母之外 我怎样才能做到这一点 Example
  • 对于没有固定/相对/绝对位置的元素,是否有 z-index 替代方案?

    我需要在更高的位置显示一个元素 z level 问题是 该元素位于带有 a 的 div 内 display flex and justify content space around 正常的z index属性不起作用 我认为这是因为该元素没
  • 防止IndexedDB请求错误取消事务

    我的意图 循环localStorage并将数据放入IndexedDB 如果发生某些已知错误 例如当键已存在时出现 ConstraintError 我想忽略这些特定错误 以便事务不会中止 当请求触发错误时 中止事务是默认行为 问题 我以为使用
  • 定位分离的 DOM 树内存泄漏

    我在诊断主要使用 Knockout 构建的非常大的单页 Web 应用程序中的分离 DOM 树内存泄漏时遇到问题 我已经调整了应用程序以附加一个假人FooBar对象特定的 HTML 按钮元素 当用户移动到应用程序的不同 页面 时 该元素应该被
  • 自定义 github 页面的预览图像

    是否可以自定义在将链接发布到 github 页面时看到的预览图像 我觉得他们专门解决了 github 存储库的问题here https docs github com en github administering a repository
  • 印地语或其他非英语语言的 HTML 输入(主要是印度语)

    我想以 HTML 形式获取印地语的用户输入 我该怎么办 我尝试设置字体系列
  • Contenteditable 显示最后插入的 html 元素

    我使用 contenteditable div 作为输入字段来输入一些文本 并通过该文本中的按钮 小 html 图片 插入图标 只要文本比 contenteditable 字段窄 就可以了 一旦文本比字段长 因此它被部分隐藏 当我输入文本字
  • 将图像从 JQuery 上传到 Node JS

    我需要从我的网站上传图像文件HTML页 但是 我不会使用form标签 因为还有其他form稍后将用于将数据传递到服务器的字段 文本字段 复选框等 我的后端在Node JS 我想要的只是从Node Js结尾 我怎样才能做到这一点 HTML d
  • 制作单行带有水平滚动条的 div

    好吧 我想我已经使用空白修复了这个问题 无换行 它在 Chrome 中工作 但在其他方面不起作用 我有这样的事情 div class outer ul li div class inner a href img src eg1 jpg a
  • 如何阻止 HTML 输入字段中的特殊字符? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我只是想问一下如何在html输入字段中阻止 等特殊字符 为什么不使用html5呢
  • 有没有办法在 Blazor 中隐藏 div?

    我正在使用 Blazor 并且想在按下导航栏切换器图标时隐藏侧边栏 列表项崩溃了 但问题是 div 仍然存在 div class page div class sidebar div class nav top row pl 4 navba
  • Angular 8 - 删除 ng-component 标签 - 表行模板

    我有一个灵活的表格组件 有两种模式 普通表 有效 自定义行模板 这不是因为角度添加
  • (jQuery) 在 cookie 中单击时保存复选框状态

    关于此功能有很多主题 但我似乎无法让它工作 我在谷歌上搜索了这个具体案例 有一堆链接让我来到这里 但奇怪的是我似乎无法让它们工作 我所做的唯一工作如下 http dl dropbox com u 2238080 a old z htm ht
  • 脚本和链接标签的简写 http:// 为 // ?有人以前看过/用过这个吗?

    问题如下 如果您使用 addthis 共享按钮 查看任何网站 一旦您浮动在 addthis 按钮上 并且加载了所有必需的资源 请使用 firebug 或 chrome 检查器查看文档的正文 不是源代码 而是屏幕上的实际文档 对象检查器 你会
  • @media查询和图像交换[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我希望在调整浏览器大小时网站中的图像完全改变 我一直在使用媒体查询 但我似乎无法正确使用它 有什么想法 建议吗 将来请添加您尝试过的代

随机推荐

  • ROS rplidar_ros package使用相关说明

    rplidar ros wiki http wiki ros org rplidar ros SDK详解 xff0c 参考blog xff1a https blog csdn net qq 16775293 article details
  • wireshark能抓到数据,调试工具却收不到数据问题

    网上查找原因 xff0c 有各种说法 xff0c 其中有说关闭防火墙的 xff0c 抱着试一试的心态 xff0c 结果成功了 wireshark有数据 xff1a 关闭防火墙 xff1a NetAssist收到数据 xff1a
  • RTKLIB之RTKRCV

    1 options file option file can be saved from rtknavi exe modify the options file to fit for ubuntu system remember to ch
  • 连接跟踪子系统之AF_INET协议族钩子函数

    如笔记连接跟踪子系统之AF INET初始化所述 xff0c 为了支持连接跟踪子系统 xff0c AF INET协议族在4个HOOK点共注册了8个钩子函数 xff0c 每个HOOK点有两个 这些钩子函数可分为入口 xff08 PRE ROUT
  • UML建模详解(9)—Rose将C++代码自动生成UML类图详解

    一 类图 nbsp class diagram nbsp 即 c 中的 class 聚合 nbsp Aggregation nbsp 即我们c 中的引用 表现为 class 头文件中的一个或多个指针成员 组合 nbsp Compositio
  • ROS2学习笔记(十)-- ROS2 launch启动文件

    简介 xff1a 接触过ROS1的同学对launch肯定不陌生 xff0c 在ROS1中 xff0c 我们常用launch实现node和master同时启动 多节点同时启动配置等功能 xff0c ROS2中的launch也是用于多节点启动
  • C++vector的使用总结及常用vector操作

    一 C vector类为内置数组提供了一种替代表示 与string类一样 vector 类是随标准 C 引入的标准库的一部分 使用时需包含头文件 include lt vector gt 二 C vector类有两种使用方式 第一种 STL
  • cc2640 基于官方从机修改的通过手机实现蓝牙点灯例程

    在TI官方从机例程中的simpleBLEPeripheral c进行代码修改 添加引脚驱动头文件 xff1a include lt ti drivers pin PINCC26XX h gt PIN driver 添加全局变量 xff1a
  • cadence 17.0 生成钻孔文件 cam350打不开的问题解决

    17 0生成钻孔文件后 xff0c cam350打不开 这时用记事本打开钻孔文件 对比发现相较于16 6的版本 xff0c 多了以下一行红色高亮的的代码 将其删除就可以用cam350打开了 LEADER 12 HEADER CODE ASC
  • matlab 串口读取设置

    本例代码运行于 MATLAB2015b xff0c 参照网上的代码修改而成 xff0c 可以自定义一次读取的数据 xff0c 并做简单处理 xff0c 随后绘图 实测可用 实用 读串口 clear clc obj 61 serial 39
  • 室内定位综述

    本篇文章是作者研究生毕设的前稿 xff0c 每周更新补充 xff0c 主要是中外关于室内定位的论文综述 xff0c 偏向于TDOA方向 xff0c 也会更新些其他的方向 有不当的地方欢迎有人指正 61 2016 7 20 1 Contrib
  • CC2630 TIMAC协议栈低功耗问题

    项目功能 采集5s数据发送 休眠5s 采集5s数据发送 xff0c 循环往复 平台 CC2630 协议栈 xff1a timac 1 05 02 43299 问题描述 在休眠5s的过程中 xff0c 整体电流在7 8ma xff0c 只比数
  • c++ 大小写转换&&字符转数字

    大小写转换 amp amp 字符转数字 xff0c 实验笔记 int main 其实就是对ASCii表的操作 string s char a 61 39 a 39 int b 61 a 39 0 39 字符转成数字 int c 61 int
  • 数组方式赋值字符串及字面值常量赋值字符串的区别

    c 43 43 实验笔记 数组赋值字符串时需要显示 39 0 39 xff0c 否则在某些时候会有问题 int main const char a 61 39 a 39 39 v 39 39 b 39 39 b 39 39 0 39 数组需
  • STL:: allocator之deallocate & destory的区别与联系

    c 43 43 中的allocator是标准库中的一个类 xff0c 负责内存分配管理 下面是 STL源码剖析 中一个简单allocator实现的部分源代码 xff1a deallocate xff1a template lt class
  • allocator简单实现

    allocator是c 43 43 标准库中用于管理内存的一个类 主要包括以下类方法 xff1a 代码如下 xff08 来源于 STL源码剖析 xff09 xff1a ifndef JJALLOC define JJALLOC includ
  • ROS2学习笔记(十一)-- ROS2 bag数据记录与回放

    简介 xff1a ROS2提供了ros2 bag命令 xff0c 可以记录指定主题的数据到文件中 xff0c 也可以将记录下的内容再发布出来 xff0c 相当于是数据的回放 xff0c 除了通过命令行的方式实现数据记录以外 xff0c 也可
  • Altium Designer 10 导出文件(PDF,gerber,BOM)

    作者 xff1a 卢老师 华清远见嵌入式学院讲师 5 导出原理图文档 GERBER 文件 xff0c BOM对于导出 PDF 文档 xff0c 多人分析时 xff0c 不能保证所有的电脑都安装有 AD10 软件 xff0c 这个也很有必要
  • 纯C++的Socket访问Http封装类

    纯C 43 43 的Socket访问Http封装类 1 项目中要使用c 43 43 43 43 来访问Web服务器 xff0c 从网上找了个C 43 43 的封装类 xff0c 其中调用了MFC xff0c 在VC2005上用能用 xff0
  • 一个C++解析HTML的库

    HTTP协议使用广泛 xff0c 相应的 xff0c C 43 43 在这块需求也开始增加 一个好的解析库可以达到事半功倍的效果 xff0c 在此贴出我的解析库的代码 xff0c 方便新手朋友们使用 hHttpParse h ifndef