写了一个网络爬虫,可以抓取网上的图片。

需要给定初始网站即可。

在vs2010中编译通过。

需要使用多字节字符集进行编译,

vs2010默认的是Unicode字符集。

编译后,运行即可,有惊喜哦!!!

爬虫原理

从最开始的网址开始,在其中找到链接到其他网页的超链接,

放到一个网页队列里面保存起来,找到该网页的所有图片,下载下来。

查看网页队列是否为空,不为空则取出下一个网页,

提取该网页的超链接放入队列的后面,下载该网页所有图片。

如此循环往复。

 
主框架:
void main()
{
//初始化socket,用于tcp网络连接
    WSADATA wsaData;
    if( WSAStartup(MAKEWORD(2,2), &wsaData) != 0 ){
        return;
    }
 
// 创建文件夹,保存图片和网页文本文件
CreateDirectory( "./img",0);
CreateDirectory("./html",0);
 
// 遍历的起始地址
string urlStart = "http://www.wmpic.me/tupian";
 
// 使用广度遍历
// 提取网页中的超链接放入hrefUrl中,提取图片链接,下载图片。
BFS( urlStart );
 
// 访问过的网址保存起来
visitedUrl.insert( urlStart );
 
while( hrefUrl.size()!=0 ){
string url = hrefUrl.front();  // 从队列的最开始取出一个网址
cout << url << endl;
BFS( url );  // 遍历提取出来的那个网页,找它里面的超链接网页放入hrefUrl,下载它里面的文本,图片
hrefUrl.pop();   // 遍历完之后,删除这个网址
}
    WSACleanup();
    return;
}
 
    BFS是最重要的处理:
 
       先是获取网页响应,保存到文本里面,然后找到其中的图片链接HTMLParse,

下载所有图片DownLoadImg。

 
//广度遍历
void BFS( const string & url ){
char * response;
int bytes;
// 获取网页的响应,放入response中。
if( !GetHttpResponse( url, response, bytes ) ){
cout << "The url is wrong! ignore." << endl;
return;
}
string httpResponse=response;
free( response );
string filename = ToFileName( url );
ofstream ofile( "./html/"+filename );
if( ofile.is_open() ){
// 保存该网页的文本内容
ofile << httpResponse << endl;
ofile.close();
}
vector<string> imgurls;
//解析该网页的所有图片链接,放入imgurls里面
HTMLParse( httpResponse,  imgurls, url );
 
//下载所有的图片资源
DownLoadImg( imgurls, url );
}

附上所有代码:

  1. //#include <Windows.h>
  2. #include <string>
  3. #include <iostream>
  4. #include <fstream>
  5. #include <vector>
  6. #include "winsock2.h"
  7. #include <time.h>
  8. #include <queue>
  9. #include <hash_set>
  10. #pragma comment(lib, "ws2_32.lib")
  11. using namespace std;
  12. #define DEFAULT_PAGE_BUF_SIZE 1048576
  13. queue<string> hrefUrl;
  14. hash_set<string> visitedUrl;
  15. hash_set<string> visitedImg;
  16. int depth=0;
  17. int g_ImgCnt=1;
  18. //解析URL,解析出主机名,资源名
  19. bool ParseURL( const string & url, string & host, string & resource){
  20. if ( strlen(url.c_str()) > 2000 ) {
  21. return false;
  22. }
  23. const char * pos = strstr( url.c_str(), "http://" );
  24. if( pos==NULL ) pos = url.c_str();
  25. else pos += strlen("http://");
  26. if( strstr( pos, "/")==0 )
  27. return false;
  28. char pHost[100];
  29. char pResource[2000];
  30. sscanf( pos, "%[^/]%s", pHost, pResource );
  31. host = pHost;
  32. resource = pResource;
  33. return true;
  34. }
  35. //使用Get请求,得到响应
  36. bool GetHttpResponse( const string & url, char * &response, int &bytesRead ){
  37. string host, resource;
  38. if(!ParseURL( url, host, resource )){
  39. cout << "Can not parse the url"<<endl;
  40. return false;
  41. }
  42. //建立socket
  43. struct hostent * hp= gethostbyname( host.c_str() );
  44. if( hp==NULL ){
  45. cout<< "Can not find host address"<<endl;
  46. return false;
  47. }
  48. SOCKET sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP);
  49. if( sock == -1 || sock == -2 ){
  50. cout << "Can not create sock."<<endl;
  51. return false;
  52. }
  53. //建立服务器地址
  54. SOCKADDR_IN sa;
  55. sa.sin_family = AF_INET;
  56. sa.sin_port = htons( 80 );
  57. //char addr[5];
  58. //memcpy( addr, hp->h_addr, 4 );
  59. //sa.sin_addr.s_addr = inet_addr(hp->h_addr);
  60. memcpy( &sa.sin_addr, hp->h_addr, 4 );
  61. //建立连接
  62. if( 0!= connect( sock, (SOCKADDR*)&sa, sizeof(sa) ) ){
  63. cout << "Can not connect: "<< url <<endl;
  64. closesocket(sock);
  65. return false;
  66. };
  67. //准备发送数据
  68. string request = "GET " + resource + " HTTP/1.1\r\nHost:" + host + "\r\nConnection:Close\r\n\r\n";
  69. //发送数据
  70. if( SOCKET_ERROR ==send( sock, request.c_str(), request.size(), 0 ) ){
  71. cout << "send error" <<endl;
  72. closesocket( sock );
  73. return false;
  74. }
  75. //接收数据
  76. int m_nContentLength = DEFAULT_PAGE_BUF_SIZE;
  77. char *pageBuf = (char *)malloc(m_nContentLength);
  78. memset(pageBuf, 0, m_nContentLength);
  79. bytesRead = 0;
  80. int ret = 1;
  81. cout <<"Read: ";
  82. while(ret > 0){
  83. ret = recv(sock, pageBuf + bytesRead, m_nContentLength - bytesRead, 0);
  84. if(ret > 0)
  85. {
  86. bytesRead += ret;
  87. }
  88. if( m_nContentLength - bytesRead<100){
  89. cout << "\nRealloc memorry"<<endl;
  90. m_nContentLength *=2;
  91. pageBuf = (char*)realloc( pageBuf, m_nContentLength);       //重新分配内存
  92. }
  93. cout << ret <<" ";
  94. }
  95. cout <<endl;
  96. pageBuf[bytesRead] = '\0';
  97. response = pageBuf;
  98. closesocket( sock );
  99. return true;
  100. //cout<< response <<endl;
  101. }
  102. //提取所有的URL以及图片URL
  103. void HTMLParse ( string & htmlResponse, vector<string> & imgurls, const string & host ){
  104. //找所有连接,加入queue中
  105. const char *p= htmlResponse.c_str();
  106. char *tag="href=\"";
  107. const char *pos = strstr( p, tag );
  108. ofstream ofile("url.txt", ios::app);
  109. while( pos ){
  110. pos +=strlen(tag);
  111. const char * nextQ = strstr( pos, "\"" );
  112. if( nextQ ){
  113. char * url = new char[ nextQ-pos+1 ];
  114. //char url[100]; //固定大小的会发生缓冲区溢出的危险
  115. sscanf( pos, "%[^\"]", url);
  116. string surl = url;  // 转换成string类型,可以自动释放内存
  117. if( visitedUrl.find( surl ) == visitedUrl.end() ){
  118. visitedUrl.insert( surl );
  119. ofile << surl<<endl;
  120. hrefUrl.push( surl );
  121. }
  122. pos = strstr(pos, tag );
  123. delete [] url;  // 释放掉申请的内存
  124. }
  125. }
  126. ofile << endl << endl;
  127. ofile.close();
  128. tag ="<img ";
  129. const char* att1= "src=\"";
  130. const char* att2="lazy-src=\"";
  131. const char *pos0 = strstr( p, tag );
  132. while( pos0 ){
  133. pos0 += strlen( tag );
  134. const char* pos2 = strstr( pos0, att2 );
  135. if( !pos2 || pos2 > strstr( pos0, ">") ) {
  136. pos = strstr( pos0, att1);
  137. if(!pos) {
  138. pos0 = strstr(att1, tag );
  139. continue;
  140. } else {
  141. pos = pos + strlen(att1);
  142. }
  143. }
  144. else {
  145. pos = pos2 + strlen(att2);
  146. }
  147. const char * nextQ = strstr( pos, "\"");
  148. if( nextQ ){
  149. char * url = new char[nextQ-pos+1];
  150. sscanf( pos, "%[^\"]", url);
  151. cout << url<<endl;
  152. string imgUrl = url;
  153. if( visitedImg.find( imgUrl ) == visitedImg.end() ){
  154. visitedImg.insert( imgUrl );
  155. imgurls.push_back( imgUrl );
  156. }
  157. pos0 = strstr(pos0, tag );
  158. delete [] url;
  159. }
  160. }
  161. cout << "end of Parse this html"<<endl;
  162. }
  163. //把URL转化为文件名
  164. string ToFileName( const string &url ){
  165. string fileName;
  166. fileName.resize( url.size());
  167. int k=0;
  168. for( int i=0; i<(int)url.size(); i++){
  169. char ch = url[i];
  170. if( ch!='\\'&&ch!='/'&&ch!=':'&&ch!='*'&&ch!='?'&&ch!='"'&&ch!='<'&&ch!='>'&&ch!='|')
  171. fileName[k++]=ch;
  172. }
  173. return fileName.substr(0,k) + ".txt";
  174. }
  175. //下载图片到img文件夹
  176. void DownLoadImg( vector<string> & imgurls, const string &url ){
  177. //生成保存该url下图片的文件夹
  178. string foldname = ToFileName( url );
  179. foldname = "./img/"+foldname;
  180. if(!CreateDirectory( foldname.c_str(),NULL ))
  181. cout << "Can not create directory:"<< foldname<<endl;
  182. char *image;
  183. int byteRead;
  184. for( int i=0; i<imgurls.size(); i++){
  185. //判断是否为图片,bmp,jgp,jpeg,gif
  186. string str = imgurls[i];
  187. int pos = str.find_last_of(".");
  188. if( pos == string::npos )
  189. continue;
  190. else{
  191. string ext = str.substr( pos+1, str.size()-pos-1 );
  192. if( ext!="bmp"&& ext!="jpg" && ext!="jpeg"&& ext!="gif"&&ext!="png")
  193. continue;
  194. }
  195. //下载其中的内容
  196. if( GetHttpResponse(imgurls[i], image, byteRead)){
  197. if ( strlen(image) ==0 ) {
  198. continue;
  199. }
  200. const char *p=image;
  201. const char * pos = strstr(p,"\r\n\r\n")+strlen("\r\n\r\n");
  202. int index = imgurls[i].find_last_of("/");
  203. if( index!=string::npos ){
  204. string imgname = imgurls[i].substr( index , imgurls[i].size() );
  205. ofstream ofile( foldname+imgname, ios::binary );
  206. if( !ofile.is_open() )
  207. continue;
  208. cout <<g_ImgCnt++<< foldname+imgname<<endl;
  209. ofile.write( pos, byteRead- (pos-p) );
  210. ofile.close();
  211. }
  212. free(image);
  213. }
  214. }
  215. }
  216. //广度遍历
  217. void BFS( const string & url ){
  218. char * response;
  219. int bytes;
  220. // 获取网页的相应,放入response中。
  221. if( !GetHttpResponse( url, response, bytes ) ){
  222. cout << "The url is wrong! ignore." << endl;
  223. return;
  224. }
  225. string httpResponse=response;
  226. free( response );
  227. string filename = ToFileName( url );
  228. ofstream ofile( "./html/"+filename );
  229. if( ofile.is_open() ){
  230. // 保存该网页的文本内容
  231. ofile << httpResponse << endl;
  232. ofile.close();
  233. }
  234. vector<string> imgurls;
  235. //解析该网页的所有图片链接,放入imgurls里面
  236. HTMLParse( httpResponse,  imgurls, url );
  237. //下载所有的图片资源
  238. DownLoadImg( imgurls, url );
  239. }
  240. void main()
  241. {
  242. //初始化socket,用于tcp网络连接
  243. WSADATA wsaData;
  244. if( WSAStartup(MAKEWORD(2,2), &wsaData) != 0 ){
  245. return;
  246. }
  247. // 创建文件夹,保存图片和网页文本文件
  248. CreateDirectory( "./img",0);
  249. CreateDirectory("./html",0);
  250. //string urlStart = "http://hao.360.cn/meinvdaohang.html";
  251. // 遍历的起始地址
  252. // string urlStart = "http://www.wmpic.me/tupian";
  253. string urlStart = "http://item.taobao.com/item.htm?spm=a230r.1.14.19.sBBNbz&id=36366887850&ns=1#detail";
  254. // 使用广度遍历
  255. // 提取网页中的超链接放入hrefUrl中,提取图片链接,下载图片。
  256. BFS( urlStart );
  257. // 访问过的网址保存起来
  258. visitedUrl.insert( urlStart );
  259. while( hrefUrl.size()!=0 ){
  260. string url = hrefUrl.front();  // 从队列的最开始取出一个网址
  261. cout << url << endl;
  262. BFS( url );                   // 遍历提取出来的那个网页,找它里面的超链接网页放入hrefUrl,下载它里面的文本,图片
  263. hrefUrl.pop();                 // 遍历完之后,删除这个网址
  264. }
  265. WSACleanup();
  266. return;
  267. }

C++网络爬虫的实现——WinSock编程的更多相关文章

  1. 手把手教你写基于C++ Winsock的图片下载的网络爬虫

    手把手教你写基于C++ Winsock的图片下载的网络爬虫 先来说一下主要的技术点: 1. 输入起始网址,使用ssacnf函数解析出主机号和路径(仅处理http协议网址) 2. 使用socket套接字 ...

  2. Socket网络编程--网络爬虫(1)

    我们这个系列准备讲一下--网络爬虫.网络爬虫是搜索引擎系统中十分重要的组成部分,它负责从互联网中搜集网页,采集信息,这些网页信息用于建立索引从而为搜索引擎提供支持,它决定着整个引擎系统的内容是否丰富, ...

  3. Python 网络爬虫 009 (编程) 通过正则表达式来获取一个网页中的所有的URL链接,并下载这些URL链接的源代码

    通过 正则表达式 来获取一个网页中的所有的 URL链接,并下载这些 URL链接 的源代码 使用的系统:Windows 10 64位 Python 语言版本:Python 2.7.10 V 使用的编程 ...

  4. Python 网络爬虫 008 (编程) 通过ID索引号遍历目标网页里链接的所有网页

    通过 ID索引号 遍历目标网页里链接的所有网页 使用的系统:Windows 10 64位 Python 语言版本:Python 2.7.10 V 使用的编程 Python 的集成开发环境:PyChar ...

  5. Python 网络爬虫 007 (编程) 通过网站地图爬取目标站点的所有网页

    通过网站地图爬取目标站点的所有网页 使用的系统:Windows 10 64位 Python 语言版本:Python 2.7.10 V 使用的编程 Python 的集成开发环境:PyCharm 2016 ...

  6. Python 网络爬虫 005 (编程) 如何编写一个可以 下载(或叫:爬取)一个网页 的网络爬虫

    如何编写一个可以 下载(或叫:爬取)一个网页 的网络爬虫 使用的系统:Windows 10 64位 Python 语言版本:Python 2.7.10 V 使用的编程 Python 的集成开发环境:P ...

  7. Python 网络爬虫 004 (编程) 如何编写一个网络爬虫,来下载(或叫:爬取)一个站点里的所有网页

    爬取目标站点里所有的网页 使用的系统:Windows 10 64位 Python语言版本:Python 3.5.0 V 使用的编程Python的集成开发环境:PyCharm 2016 04 一 . 首 ...

  8. 《Python编程》课程报告 python技术在数据分析中的应用之网络爬虫

      摘要:... 2 1       引言 :... 2 1.1课题研究背景和研究现状... 2 1.1.1课题背景和目的... 3 1.1.2研究现状... 4 1.1.2.1语言... 4 1.1 ...

  9. Socket网络编程--网络爬虫(2)

    上一小节,我们实现了下载一个网页.接下来的一步就是使用提取有用的信息.如何提取呢?一个比较好用和常见的方法就是使用正则表达式来提取的.想一想我们要做个什么样的网络爬虫好呢?我记得以前好像博客园里面有人 ...

随机推荐

  1. box-sizing 属性

    box-sizing属性可以为三个值: content-box,border和padding不计算入width之内 padding-box,padding计算入width内 border-box,bo ...

  2. ecmascript 6 的arguments转数组的代码

  3. CentOS7系列--1.6CentOS7配置sudo

    CentOS7配置sudo 如果一些用户共享权限,配置sudo是为了分离用户的职责 1. 将root 的权限传递给所有用户 [root@centos7 ~]# visudo 添加下面的内容到最后一行, ...

  4. vip会员统计表 (vip等级是灵活配置的 非写死1是金卡用户 2是什么 等)

      一个非常常见的报表,分析会员组成比例 以及最新增长情况 和上月同期会员增长情况. 比较特殊一点的是 报表中的 普通会员 和 金卡会员 临时会员 银卡会员 等列 都是根据会员等级配置表动态生成的(即 ...

  5. 网络基础 图解Windows系统下单网卡设置双IP访问不同网段的方法

    图解Windows系统下单网卡设置双IP访问不同网段的方法 by:授客 QQ:1033553122 在Windows系统下即使只有一块网卡,同样可以实现双IP访问不同网段. 例: 外网信息: IP:1 ...

  6. App 图标设计 - 圆角透明效果(0 基础使用 PS)

    App 图标设计 - 圆角透明效果(0 基础使用 PS) 方法: 如果你有些基础,就不必看图文教程了: 1.使用圆角矩形工具选中,设置圆角尺寸[例如:1024*1024 px(圆角:160 px)] ...

  7. 【转】grep -v grep

    1.grep 是查找含有指定文本行的意思,比如grep test 就是查找含有test的文本的行 2.grep -v 是反向查找的意思,比如 grep -v grep 就是查找不含有 grep 字段的 ...

  8. LeetCode题解之Single Number

    1.题目描述 2.分析 3.代码 int singleNumber(vector<int>& nums) { map<int,int> m; for( vector&l ...

  9. leetCode题解之寻找string中最后一个word的长度

    1.题目描述 返回一个 string中最后一个单词的长度.单词定义为没有空格的连续的字符,比如 ‘a’,'akkk'. 2.问题分析 从后向前扫描,如果string是以空格‘  ’结尾的,就不用计数, ...

  10. 显示iOS所有系统字体

    显示iOS所有系统字体 源码地址: https://github.com/YouXianMing/UI-Component-Collection 效果图: 便于你开发中寻找适合自己的字体, demo中 ...