(十一)C++之获取B站用户的粉丝数信息(一)

1.1、写这个例子的初衷

通过一个多星期,将C++基础学习了一遍,想通过一个例子来整合一下自己所学到的东西,刚好我计划这段时间制作一个B站的粉丝信息计数器(主要是拿C语言来写的),但是后面想想,也想通过C++来实现,刚好拿来练练手。

1.2、获取使用到的知识点

这里面使用到网络socket的知识,但是不是很复杂,只需要自行百度C++中如何使用TCP编程即可。

1.3、获取原理

现在我们之讲原理,并不是最终版本,因为最终版本是需要有一定可靠性的,比如断网、网络恢复后又该如何恢复访问,所以这一节先教大家获取到自己想要的信息。

第一步:先打开我们的B站用户界面:
在这里插入图片描述
我们最终获取的就是圈出来的那四个信息。

然后按F12(有一些的小伙伴如果F12被占用其他功能,可以加上Fn+F12),调取网页代码出来。

按照下面两个步骤。
在这里插入图片描述
输入st后,点击一下空白处,就会出现圈出来的内容,然后点一下他,出现右边的代码信息。
在这里插入图片描述

然后获取到这个网址:
在这里插入图片描述

打开新的网址,把蓝色的区域删掉
在这里插入图片描述
最终就得到我们需要的两个信息,就是你关注的人和关注的人。
在这里插入图片描述

剩余两个信息视频播放数和点赞数在另外一个网址那里。
在这里插入图片描述
他的获取方式和上一条获取方式一样。
在这里插入图片描述
在这个网址可以获取到视频播放数和点赞人数,那么到这里,获取这四个信息的操作就已经知道,接下来就是写代码去验证。

1.4、C++获取

因为网页使用的html语言,是基于TCP网络的,所以我们可以通过TCP来模拟访问。

网络上有很多讲解C++的TCP的使用,在这里我不深入进去,只是教大家如何使用。

因为html使用的是域名访问,可是TCP是建立在ip地址的访问,所以我们第一步先解决,将域名转为ip,代码如下:

#include <winsock2.h>
#include <ws2def.h>
#include <stdlib.h>
#include <iostream>

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


#define HOST_NAME "api.bilibili.com"
#define PORT 80

using namespace std;

int main(void)
{
	/*这里主要是加载上面那个库做的初始化*/

	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if (WSAStartup(sockVersion, &data) != 0){
		return -1;
	}

	/*获取IP地址*/

	struct hostent *hptr;
	char   ip[32];


	if ((hptr = gethostbyname(HOST_NAME)) == NULL)
	{
		cout << " gethostbyname error for host:" << HOST_NAME << endl;
		return -1;
	}
	cout << "official hostname:" << hptr->h_name << endl;

	switch (hptr->h_addrtype)
	{
	case AF_INET:
	case AF_INET6:
		memset(ip,0x00,32);
		strcpy(ip, inet_ntoa(*(struct in_addr *)*hptr->h_addr_list));
		cout << ip << endl;
		break;
	default:
		cout << "unknown address type" <<endl;
		return -2;
		break;
	}
	return 0;
}

我们访问的B站信息网址是"api.bilibili.com",所以运行结果如在:
在这里插入图片描述

这样我们就可以得到转化后的ip地址了。

获取到IP地址,就可以尝试连接到服务器上。

#include <winsock2.h>
#include <ws2def.h>
#include <stdlib.h>
#include <iostream>

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


#define HOST_NAME "api.bilibili.com"
#define PORT 80

using namespace std;

int main(void)
{
	/*这里主要是加载上面那个库做的初始化*/

	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if (WSAStartup(sockVersion, &data) != 0){
		return -1;
	}

	/*获取IP地址*/

	struct hostent *hptr;
	char   ip[32];


	if ((hptr = gethostbyname(HOST_NAME)) == NULL)
	{
		cout << " gethostbyname error for host:" << HOST_NAME << endl;
		return -1;
	}
	cout << "official hostname:" << hptr->h_name << endl;

	switch (hptr->h_addrtype)
	{
	case AF_INET:
	case AF_INET6:
		memset(ip,0x00,32);
		strcpy(ip, inet_ntoa(*(struct in_addr *)*hptr->h_addr_list));
		cout << ip << endl;
		break;
	default:
		cout << "unknown address type" << endl;
		return -2;
		break;
	}

	/*连接到服务器*/
	SOCKET socket_fd;
	SOCKADDR_IN seraddr;

	socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	if (socket_fd == INVALID_SOCKET){
		cout << "invalid socket!" << endl;
		return -1;
	}

	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(PORT);
	seraddr.sin_addr.S_un.S_addr = inet_addr(ip);

	if (connect(socket_fd, (SOCKADDR *)&seraddr, sizeof(seraddr)) == SOCKET_ERROR){
		cout << "connect err!" << endl;
		closesocket(socket_fd);
		return -2;
	}
	cout << "tcp connect OK" << endl;

	return 0;
}

这部分的代码增加了连接到服务器的部分代码。
运行结果:
在这里插入图片描述

这里已经连接上服务器,接下来就是发送请求了。

这里的发送请求,就有点门路在里面,我们不能乱发,只能模拟浏览器发送,否则会被服务器返回403之类的错误。

我们打开我们之前的网页,选择我们第一个右键后选择copy然后选择request headers。

在这里插入图片描述

新建一个文本,然后粘贴进去。
在这里插入图片描述
最终我们选择如下内容:
在这里插入图片描述
为了避免服务器禁止我们访问,所以我们需要模拟浏览器的请求头的部分信息,来伪装成为浏览器。

所以定一个字符串:

char req[] = "GET /x/relation/stat?vmid=378528692 HTTP/1.1\r\nAccept: */*\r\nHost: api.bilibili.com\r\nConnection: keep-alive\r\n\r\n";

这里要注意两点,每一行都得添加一个换行"\r\n",最后一行要多加"\r\n"一个换行符,具体请看代码。
之后通过向服务器请求,就可以得到我们想要的数据。

#include <winsock2.h>
#include <ws2def.h>
#include <stdlib.h>
#include <iostream>

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


#define HOST_NAME "api.bilibili.com"
#define PORT 80
#define BUFSIZE 1024

using namespace std;

int main(void)
{
	/*这里主要是加载上面那个库做的初始化*/

	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if (WSAStartup(sockVersion, &data) != 0){
		return -1;
	}

	/*获取IP地址*/

	struct hostent *hptr;
	char   ip[32];


	if ((hptr = gethostbyname(HOST_NAME)) == NULL)
	{
		cout << " gethostbyname error for host:" << HOST_NAME << endl;
		return -1;
	}
	cout << "official hostname:" << hptr->h_name << endl;

	switch (hptr->h_addrtype)
	{
	case AF_INET:
	case AF_INET6:
		memset(ip,0x00,32);
		strcpy(ip, inet_ntoa(*(struct in_addr *)*hptr->h_addr_list));
		cout << ip << endl;
		break;
	default:
		cout << "unknown address type" << endl;
		return -2;
		break;
	}

	/*连接到服务器*/
	SOCKET socket_fd;
	SOCKADDR_IN seraddr;

	socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	if (socket_fd == INVALID_SOCKET){
		cout << "invalid socket!" << endl;
		return -1;
	}

	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(PORT);
	seraddr.sin_addr.S_un.S_addr = inet_addr(ip);

	if (connect(socket_fd, (SOCKADDR *)&seraddr, sizeof(seraddr)) == SOCKET_ERROR){
		cout << "connect err!" << endl;
		closesocket(socket_fd);
		return -2;
	}
	
	cout << "tcp connect OK" << endl;

	/*发送请求*/

	char strResponse[BUFSIZE];

	char req[] = "GET /x/relation/stat?vmid=378528692 HTTP/1.1\r\nAccept: */*\r\nHost: api.bilibili.com\r\nConnection: keep-alive\r\n\r\n";

	int req_len = strlen(req);

	int result = 0;

	req_len = strlen(req);
	result = send(socket_fd, req, req_len, 0);
	if (result == SOCKET_ERROR){
		cout << "send err!" << endl;
		closesocket(socket_fd);
		return -1;
	}
	//开始接受服务器返回来的数据
	result = recv(socket_fd, strResponse, BUFSIZE - 1, 0);

	if (result > 0){

		strResponse[result] = '\0';

		cout << strResponse << endl;

	}
	else{
		cout << "recv err!" << endl;
		closesocket(socket_fd);
		return -2;
	}

	return 0;
}

运行结果:
在这里插入图片描述
这个就是我们最终访问得到服务器的数据,只是这里我们还没解释出来而已。

这里获取到你关注的人数和关注你的人数,接下来就是视频播放量和点赞数了。

同理,也可以得到视频播放量的数据。

1.5、最终代码

#include <winsock2.h>
#include <ws2def.h>
#include <stdlib.h>
#include <iostream>

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


#define HOST_NAME "api.bilibili.com"
#define PORT 80
#define BUFSIZE 1024

using namespace std;

int main(void)
{
	/*这里主要是加载上面那个库做的初始化*/

	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if (WSAStartup(sockVersion, &data) != 0){
		return -1;
	}

	/*获取IP地址*/

	struct hostent *hptr;
	char   ip[32];


	if ((hptr = gethostbyname(HOST_NAME)) == NULL)
	{
		cout << " gethostbyname error for host:" << HOST_NAME << endl;
		return -1;
	}
	cout << "official hostname:" << hptr->h_name << endl;

	switch (hptr->h_addrtype)
	{
	case AF_INET:
	case AF_INET6:
		memset(ip,0x00,32);
		strcpy(ip, inet_ntoa(*(struct in_addr *)*hptr->h_addr_list));
		cout << ip << endl;
		break;
	default:
		cout << "unknown address type" << endl;
		return -2;
		break;
	}

	/*连接到服务器*/
	SOCKET socket_fd;
	SOCKADDR_IN seraddr;

	socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	if (socket_fd == INVALID_SOCKET){
		cout << "invalid socket!" << endl;
		return -1;
	}

	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(PORT);
	seraddr.sin_addr.S_un.S_addr = inet_addr(ip);

	if (connect(socket_fd, (SOCKADDR *)&seraddr, sizeof(seraddr)) == SOCKET_ERROR){
		cout << "connect err!" << endl;
		closesocket(socket_fd);
		return -2;
	}
	
	cout << "tcp connect OK" << endl;

	/*发送请求*/

	char strResponse[BUFSIZE];

	char req[] = "GET /x/relation/stat?vmid=378528692 HTTP/1.1\r\nAccept: */*\r\nHost: api.bilibili.com\r\nConnection: keep-alive\r\n\r\n";

	int req_len = strlen(req);

	int result = 0;

	req_len = strlen(req);
	result = send(socket_fd, req, req_len, 0);
	if (result == SOCKET_ERROR){
		cout << "send err!" << endl;
		closesocket(socket_fd);
		return -1;
	}
	//开始接受服务器返回来的数据
	result = recv(socket_fd, strResponse, BUFSIZE - 1, 0);

	if (result > 0){

		strResponse[result] = '\0';

		cout << strResponse << endl;

	}
	else{
		cout << "recv err!" << endl;
		closesocket(socket_fd);
		return -2;
	}

	char req_like[] = "GET /x/space/upstat?mid=378528692 HTTP/1.1\r\nAccept: */*\r\nHost: api.bilibili.com\r\nConnection: keep-alive\r\n\r\n";

	int req_like_len = strlen(req_like);

	result = send(socket_fd, req_like, req_like_len, 0);
	if (result == SOCKET_ERROR){
		cout << "send err!" << endl;
		closesocket(socket_fd);
		return -1;
	}
	//开始接受服务器返回来的数据
	result = recv(socket_fd, strResponse, BUFSIZE - 1, 0);

	if (result > 0){

		strResponse[result] = '\0';

		cout << strResponse << endl;

	}
	else{
		cout << "recv err!" << endl;
		closesocket(socket_fd);
		return -2;
	}

	return 0;
}

运行结果:
在这里插入图片描述

当然以上只是教大家获取的原理,这样肯定是不可能跑的,第一,因为数据还没提取出来,第二因为他的可靠性太差了,网络一中断,便再也无法恢复过来。

所以下一篇我们来慢慢完善他,不知道大家明白这个原理没,如果那里不明白的可以评论。

发布了29 篇原创文章 · 获赞 0 · 访问量 411

猜你喜欢

转载自blog.csdn.net/weixin_42547950/article/details/104435609