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

需要给定初始网站即可。

在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. windows 查看端口

    windowsnetstat命令查看进程:netstat -ano查看占用端口进程:netstat -ano|findstr “端口号”,例子netstat -ano|findstr “8080”.t ...

  2. HTML meta标签总结,HTML5 head meta属性整理

    原文链接:http://caibaojian.com/mobile-meta.html <!DOCTYPE html> <!-- 使用 HTML5 doctype,不区分大小写 -- ...

  3. iphone精简教程

    那么教程开始 首先讲一下到底什么是精简 精简,就是把iphone4里面没用的自带软件和一些没用的东西删除 比如说股票,facetime,itunes store这些从来不用的东西,把这些东西删除从而使 ...

  4. 读书笔记week1——涂涵越

    这次读书笔记主要是就<程序员修炼之道>这本书的前半部分做一些总结以及发表一些自己的看法. 本书前面的一部分主要是一些程序员应该在工作中时刻注意的事情,一些关键的信息如下: 1.处理问题的态 ...

  5. Axure中移动端原型设计方法(附IPhoneX和IPhone8最新模板)

    Axure中移动端原型设计方法(附IPhoneX和IPhone8最新模板) 2018年4月16日luodonggan Axure中基于设备模板的移动端原型设计方法(附IPhoneX和IPhone8最新 ...

  6. mysql执行计划常用说明

    MYSQL执行计划顺序原则上是:在所有组中,id值越大,优先级越高,越先执行,id如果相同,可以认为是一组,从上往下顺序执行做执行计划之前,要了解下表统计信息情况:mysql.innodb_table ...

  7. MySQL学习分享--Thread pool实现

    基于<MySQL学习分享--Thread pool>对Thread pool架构设计的详细了解,本文主要对Thread pool的实现进行分析,并根据Mariadb和Percona提供的开 ...

  8. Linux sudo详解

    sudo:控制用户对系统命令的使用权限,root允许的操作.通过sudo可以提高普通用户的操作权限,不过这个权限是需要进行配置才可使用. 常用的命令展示 配置sudo的2种方式 1. visodu 编 ...

  9. Spring MVC Hello World 404

    下面的例子说明了如何使用 Spring MVC 框架来编写一个简单的基于 web 的 Hello World 应用程序.下面让我们使用 Eclipse IDE,然后按照下面的步骤使用 Spring 的 ...

  10. spark-submit提交参数设置

    /apps/app/spark-1.6.1-bin-hadoop2.6/bin/spark-submit --class com.zdhy.zoc2.sparksql.core.JavaSparkSq ...