先给你一个入口网站,发送http请求头接收返回的内容放入URL txt文件中,然后在加入到搜索过的链表中,放入到搜索url txt文件中,分析html内容,找出其中的超链,把超链放入待搜索队列中,最后循环以上步骤直到待搜索队列没有内容。

编译环境Visual Studio

#include <iostream>
#include <WinSock2.h>
#include <errno.h>
#include <cstring>
#include <iomanip>
#include <fstream>
#include <queue>
#include <algorithm>

#pragma comment(lib,"ws2_32.lib")
using namespace std;

#define DEFUAL_PORT 80
#define DEFUAL_ARRAY_SIZE 1048576
#define URL "http://www.baidu.com/"
#define SAVE_HTML_DATA_DIRECTORY "./html"//存放html文本的文件夹

//截取HostUrl中的内容直到Sign处
char *InterceptString(char *&HostUrl,char Sign)
{
if(HostUrl == NULL)
return NULL;
int size = 100;
int nIndex = 0;
char *string = new char[size];
char *MarkString = string;
char *NewStr = NULL;
while(*HostUrl != Sign && *HostUrl != '\0')
{
*string++=*HostUrl++;
nIndex++;
if(nIndex +1 == size)
{
*string = '\0';
size += 100;
NewStr = new char[size];
strcpy_s(NewStr,size,MarkString);
free(MarkString);
MarkString = string = NewStr;
string += nIndex;
}
}
*string = '\0';
return MarkString;
}
//解析URL 分离出主机 和 资源 分别放入host和resource中
bool ParseUrl(char *HostUrl,char *&resource,char *&host)
{
if(HostUrl == NULL)
return false;
if( strstr(HostUrl,"http://") != NULL)//判定URL是否是标准格式
HostUrl +=7;
if(*HostUrl != 'w')//格式错误 
return false;
//分离 主机 和 资源
if(!(host = InterceptString(HostUrl,'/')))
return false;
if(*HostUrl == '\0'){
resource = new char[2];
resource[0] = '/';
resource[1] = '\0';
}
else
if(!(resource = InterceptString(HostUrl,'\0')))
return false;

//cout<<host<<endl<<resource<<endl;
return true;
}
//发送http请求报文
bool SendHttp(char *HostUrl,char *&Htmlresource,int &ByteRead)
{
char *resource = NULL;
char *host = NULL;
if( !ParseUrl(HostUrl,resource,host) ){
cout<< "Parse Url fail !" << endl;
return false;
}
//创建套接字
SOCKET sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sock == -1 || sock == -2){
cout<<"socket error ! error number === "<<GetLastError()<<endl;
return false;
}
//通过域名得到对应ip地址(此处需要联网)
hostent *p_TargetHost_ip = gethostbyname(host);
if(p_TargetHost_ip == NULL){
cout<<"gethostbyname error ! error number === "<<GetLastError()<<endl;
return false;
}

sockaddr_in TargetHost;
TargetHost.sin_family = AF_INET;
TargetHost.sin_port = htons(DEFUAL_PORT);
TargetHost.sin_addr.s_addr = *(u_long*)p_TargetHost_ip->h_addr_list[0];

if( connect(sock,(sockaddr*)&TargetHost,sizeof(TargetHost))  < 0){
cout<<"connect error ! error number ===  "<<GetLastError()<<endl;;
return false;
}
//定义发送报文内容
char message[1024] = {0};//此处不应该是固定大小--------------------
char *StringInputFormat = "GET %s HTTP/1.1\r\nHost:%s\r\nConnection:Close\r\n\r\n" ;

sprintf_s(message,1024,StringInputFormat,resource,host);

//发送请求包
size_t sent = 0;
int tmpress;
while(sent < strlen(message)){
tmpress = send(sock,message+sent,strlen(message)-sent,0);

if(tmpress == SOCKET_ERROR){
cout<< "send message fail ! error ===" << GetLastError() << endl;
return false;
}
sent += tmpress;
}
//接收回复包
//char arr[DEFUAL_ARRAY_SIZE] = {0};//错误 数组不能定义这么大,所以采用new的方式
//另外直接开固定数组也容易越界
int Message_Byte = 100000;//这里应该变成可变的大小-----------
char *ReplyMsg = new char[Message_Byte];
memset(ReplyMsg , 0 ,Message_Byte);
ByteRead = 0;
int rempress = 1;
cout << "Read :" ;
while(rempress > 0){
rempress = recv(sock,ReplyMsg+ByteRead,Message_Byte-ByteRead,0);
if(rempress > 0){
ByteRead += rempress;
}
cout << rempress << " ";
}
ReplyMsg[ByteRead] = 0;

//cout<<ReplyMsg << endl;
cout<<ByteRead <<endl;

Htmlresource = ReplyMsg;

free(resource);
resource = NULL;
free(host);
host = NULL;
closesocket(sock);
return true;
}
//将URL转化为文件名------此处应该改成附加一个变量的,如果为0 加上txt
char *UrlTranformateFilename(char *Url)
{
if(Url == NULL)
return NULL;
char *string = new char[strlen(Url)+5];//Url长度,加上.txt四个字符
char *MarkString = string;
while(*Url != '\0'){
if(*Url != ':'&&*Url != '/')
*string++=*Url;
Url++;
}
char *str_text = ".txt";
while(*str_text != '\0')
{
*string++=*str_text++;
}
*string = '\0';
return MarkString;
}
//将得到的内容放入txt文件中
bool InputFile(char *&Url,char *&Htmlresource)
{
char *Filename = NULL;
if(!(Filename = UrlTranformateFilename(Url))){//将URL转化为文件名
cout<< "UrlTranformateFilename fail ! Url ====" <<Url<< endl;
return false;
}
//通过文件名加上路径创建txt文件
char *path = SAVE_HTML_DATA_DIRECTORY;
char *Filepath = new char[strlen(Filename) +strlen(path)+2];
strcpy_s(Filepath,strlen(Filename)+strlen(path)+2,path);
strcat_s(Filepath,strlen(Filename)+strlen(path)+2,"/");
strcat_s(Filepath,strlen(Filename)+strlen(path)+2,Filename);

cout<<Filepath<<endl;
//在html文件夹中创建txt文件并将内容写入
ofstream File(Filepath);//创建txt文件
if(File.is_open()){//打开文件,这种打开方式会清空原文件内容,重新写入
File<<Htmlresource<<endl;
File.close();
}
//及时删除,防止内存泄漏
free(Filepath);
Filepath = NULL;
return true;
}
//解析HTML内容
bool ParseHtml(char *Htmlresource,queue<char*> &SearchQueue,queue<char*> &NotSearchQueue)
{
//查找html中body 得到其中的body内容
char *str_body = "<body";
char *Html = NULL;
if(!(Html = strstr(Htmlresource,str_body))){
cout<< "this Html error ! " << endl;
return false;
}
//分析html内容 找到其中的超链 如果不在已搜索队列中,加入待搜索队列
char *str_target = "href=\"";
char *str_hyperlink = NULL;
if(!(str_hyperlink = strstr(Html,str_target))){
cout<< "this html no hyperlink !" << endl;
}
while(str_hyperlink){
str_hyperlink += strlen(str_target);
char *str = strstr(str_hyperlink,"\"");
char *Url = InterceptString(str_hyperlink,'"');
NotSearchQueue.push(Url);//此处应该判断是否遍历过
str_hyperlink = strstr(str_hyperlink,str_target);
}

return true;
}

void BFS(char *Url,queue<char*> &SearchQueue,queue<char*> &NotSearchQueue)
{
//先发送http得到其中资源
char *Htmlresource = NULL;
int Byte = 0;
if(!SendHttp(Url,Htmlresource,Byte)){
cout<< "SendHttp  fail ! return !" <<endl;
return ;
}
if(!InputFile(Url,Htmlresource)){//输入到文件失败不需要返回false 还可以继续爬
cout<< "InputFile fail ! ignore !"<<endl;
}
//cout<<Htmlresource<<endl;

//解析html
if(!ParseHtml(Htmlresource,SearchQueue,NotSearchQueue)){
cout<<"ParseHtml fail ! return "<<endl;
return ;
}

}
bool Init()
{
WSADATA wsaData;
if( WSAStartup( MAKEWORD(2,2),&wsaData) != 0)
{
printf("WSAStartup error ! error number === %d \n",GetLastError());
return false;
}
//创建存储html文本和html中图片的文件夹
CreateDirectory(TEXT(SAVE_HTML_DATA_DIRECTORY),NULL);
return true;
}

void Print(queue<char*> NotSearchQueue)
{
while(!NotSearchQueue.empty()){
cout<<NotSearchQueue.front()<<endl;
NotSearchQueue.pop();
}
}
int main(int argc,char *argv[])
{
if(!Init()){
cout<< "Init() fail ! " <<endl;
return -1;
}
char *UrlStart = URL;
cout<<UrlStart<<endl;
queue<char *> SearchQueue;
queue<char *> NotSearchQueue;
BFS(UrlStart,SearchQueue,NotSearchQueue);
SearchQueue.push(UrlStart);
//Print(NotSearchQueue);
//当未搜索队列不为空时候,继续
while(!NotSearchQueue.empty()){
cout<<NotSearchQueue.front()<<endl;
BFS(NotSearchQueue.front(),SearchQueue,NotSearchQueue);
SearchQueue.push(NotSearchQueue.front());
NotSearchQueue.pop();
}
WSACleanup();
return 0;
}

用C++实现一个小小的爬虫相关推荐

  1. Python玩耍:一个小小的爬虫,在一堆公司列表里筛选出总部位于中国的公司

    这周接了个小活,发个博客庆祝一下 需要从https://www.mwcbarcelona.com/exhibition/2019-exhibitors/这个网站上将所有的公司照下来(24000+),并 ...

  2. list的add方法 ,foreach循环添加map---List.add(map)(通过一个java爬虫案例说明)

    案例:一个java爬虫程序 1.案例说明 做了一个爬取某程的旅游网站的java程序,程序主要爬取安庆酒店的某些相关信息. 材料准备:jsoup-1.8.1.jar(需要此架包的联系博主,有任何问题欢迎 ...

  3. 可能我也没有想到,我能把写文章这件事因为一个小小的念头而坚持下来!

    大家好,我是小一 周末的时光总是很短暂,又一个周末过去了,下一个周末只放一天假.闲聊一下自己这段时间的一些个人感受吧 不知道大家有没有发现,从去年疫情期间开始,突然多了 好多好多 的公众号大佬,特别是 ...

  4. 不会Python爬虫?教你一个通用爬虫思路轻松爬取网页数据

    前言 其实爬虫的思路很简单,但是对于很多初学者而言,看得懂,但是自己写的时候就不知道怎么去分析了!说实话还是写少了,自己不要老是抄代码,多动手! 本人对于Python学习创建了一个小小的学习圈子,为各 ...

  5. 使用Scrapy构建一个网络爬虫

    记得n年前项目需要一个灵活的爬虫工具,就组织了一个小团队用Java实现了一个爬虫框架,可以根据目标网站的结构.地址和需要的内容,做简单的配置开发,即可实现特定网站的爬虫功能.因为要考虑到各种特殊情形, ...

  6. 使用HttpClient实现一个简单爬虫,抓取煎蛋妹子图

    第一篇文章,就从一个简单爬虫开始吧. 这只虫子的功能很简单,抓取到"煎蛋网xxoo"网页(http://jandan.net/ooxx/page-1537),解析出其中的妹子图,保 ...

  7. char转成string_真没想到,一个小小的String居然还有这么多窍门?

    推荐学习 公司来了位阿里P8大神,看完他的手写"Kafka笔记",万分膜拜 牛掰!"基础-中级-高级"Java程序员面试集结,看完献出我的膝盖 真没想到,一个小 ...

  8. 如何构建一个分布式爬虫:实战篇

    本篇文章将是『如何构建一个分布式爬虫』系列文章的最后一篇,拟**从实战角度**来介绍如何构建一个*稳健的分布式微博爬虫*.这里我*没敢谈高效*,抓过微博数据的同学应该都知道微博的反爬虫能力,也知道微博 ...

  9. 如何构建一个分布式爬虫:基础篇

    继上篇(如何构建一个分布式爬虫:理论篇)我们谈论了Celery的基本知识后,本篇继续讲解如何一步步使用Celery构建分布式爬虫.这次我们抓取的对象定为celery官方文档(Celery - Dist ...

最新文章

  1. iOS通过Plist保存离线调试日志
  2. windows 2003服务器不断向外发包解决方法 php程序
  3. 详解神秘Linux内核
  4. android学习之Service
  5. Redis(1) 简介以及linux环境下的安装
  6. Android Studio 智能感知无效
  7. 目标检测(三)--DPM
  8. No.1 - 制作一个简单的菜单动画效果---百度IFE
  9. lua源码分享适用脚本chiji游戏
  10. 学UG编程,首先要学习什么?那些编程都是操机操出来的吗?
  11. 计算机网络自顶向下方法华为路由器IPV6到IPV4到IPV6的隧道及实现两端主机通信
  12. 2018科大讯飞营销广告算法大赛
  13. 北京大学肖臻老师《区块链技术与应用》公开课笔记8——BTC挖矿篇
  14. js 正则替换手机号中间四位为****
  15. Kinect v2.0原理介绍之十三:面部帧获取
  16. 台湾榜首iPhone游戏创作者谈开发成败
  17. 织梦dedecms 仿制目标网站首页
  18. 如何使用html实现在线秒表,js实现简单的秒表
  19. 常见宽带路由器设置方法汇总
  20. 基于PyQt5实现查看本地图片功能

热门文章

  1. C和C++实战教程专栏完整目录
  2. Java线程的同步和异步的区别
  3. b2b2c所有功能整合
  4. 免费python课程-Python零基础免费入门课程
  5. Python+Tensorflow实现检测X光图像中的新冠!
  6. 日程安排html模板,一款基于日历的日程安排应用模板
  7. 替代RTD2171U|CS5266设计电路|TYPEC转HDMI方案|CS5266AN
  8. Flutter 2.10 现已发布
  9. stm32f4架构总结
  10. SQL 随机抽样的总结