先来说一下主要的技术点:

1. 输入起始网址,使用ssacnf函数解析出主机号和路径(仅处理http协议网址)

2. 使用socket套接字连接服务器,,获取网页html代码(使用http协议的GET请求),然后使用正则表达式解析出图片url和其他的url。

3. 下载图片至创建的文件夹中,同时其他的url push进队列。

4. 为了使爬虫能够连续的工作,这里使用了BFS宽度优先搜索,也就是说一开始输入的网址作为起始网址,push进队列,然后把能解析出来的网址在不重复的情况下都push进队列,每次取队列的top来执行下载操作,直到队列为空时终止。

下面附上技术点的学习资料供参考:

ssanf函数的用法:

http://www.cnblogs.com/mycapple/archive/2012/08/03/2621681.html

C++11正则表达式

http://blog.sina.com.cn/s/blog_ac9fdc0b0101oow9.html

http协议:

http://www.ucai.cn/course/show/234

Socket编程:

http://blog.csdn.net/nk_test/article/details/47756381

http://blog.csdn.net/nk_test/article/details/47733307

另外,这个小爬虫结构简陋,还存在很多不足,例如队列中的url太多会爆内存,正则表达式匹配不够准确等,仅仅适合大家学习的时候练手哈。也欢迎大家发现bug,给出好的建议。

  1. /*下载图片 C++ Winsock 网络编程*/
  2. #define _CRT_SECURE_NO_WARNINGS   //vs 2013用于忽略c语言安全性警告
  3. #include <cstdio>
  4. #include <iostream>
  5. #include <fstream>
  6. #include <string>
  7. #include <cstring>
  8. #include <regex>
  9. #include <vector>
  10. #include <queue>
  11. #include <algorithm>
  12. #include <winsock2.h>
  13. #include <map>
  14. using namespace std;
  15. #pragma comment(lib, "ws2_32.lib")
  16. char  host[500];
  17. int num = 1;
  18. char othPath[800];
  19. string allHtml;
  20. vector <string> photoUrl;
  21. vector <string> comUrl;
  22. map <string, int> mp; //防止相同网址重复遍历
  23. SOCKET sock;
  24. bool analyUrl(char *url) //仅支持http协议,解析出主机和IP地址
  25. {
  26. char *pos = strstr(url, "http://");
  27. if (pos == NULL)
  28. return false;
  29. else
  30. pos += 7;
  31. sscanf(pos, "%[^/]%s", host, othPath);   //http:// 后一直到/之前的是主机名
  32. cout << "host: " << host << "   repath:" << othPath << endl;
  33. return true;
  34. }
  35. void regexGetimage(string &allHtml)  //C++11 正则表达式提取图片url
  36. {
  37. smatch mat;
  38. regex pattern("src=\"(.*?\.jpg)\"");
  39. string::const_iterator start = allHtml.begin();
  40. string::const_iterator end = allHtml.end();
  41. while (regex_search(start, end, mat, pattern))
  42. {
  43. string msg(mat[1].first, mat[1].second);
  44. photoUrl.push_back(msg);
  45. start = mat[0].second;
  46. }
  47. }
  48. void regexGetcom(string &allHtml) //提取网页中的http://的url
  49. {
  50. smatch mat;
  51. //regex pattern("href=\"(.*?\.html)\"");
  52. regex pattern("href=\"(http://[^\s'\"]+)\"");
  53. string::const_iterator start = allHtml.begin();
  54. string::const_iterator end = allHtml.end();
  55. while (regex_search(start, end, mat, pattern))
  56. {
  57. string msg(mat[1].first, mat[1].second);
  58. comUrl.push_back(msg);
  59. start = mat[0].second;
  60. }
  61. }
  62. void preConnect()  //socket进行网络连接
  63. {
  64. WSADATA wd;
  65. WSAStartup(MAKEWORD(2, 2), &wd);
  66. sock = socket(AF_INET, SOCK_STREAM, 0);
  67. if (sock == INVALID_SOCKET)
  68. {
  69. cout << "建立socket失败! 错误码: " << WSAGetLastError() << endl;
  70. return;
  71. }
  72. sockaddr_in sa = { AF_INET };
  73. int n = bind(sock, (sockaddr*)&sa, sizeof(sa));
  74. if (n == SOCKET_ERROR)
  75. {
  76. cout << "bind函数失败! 错误码: " << WSAGetLastError() << endl;
  77. return;
  78. }
  79. struct hostent  *p = gethostbyname(host);
  80. if (p == NULL)
  81. {
  82. cout << "主机无法解析出ip! 错误吗: " << WSAGetLastError() << endl;
  83. return;
  84. }
  85. sa.sin_port = htons(80);
  86. memcpy(&sa.sin_addr, p->h_addr, 4);//   with some problems ???
  87. //sa.sin_addr.S_un.S_addr = inet_addr(*(p->h_addr_list));
  88. //cout << *(p->h_addr_list) << endl;
  89. n = connect(sock, (sockaddr*)&sa, sizeof(sa));
  90. if (n == SOCKET_ERROR)
  91. {
  92. cout << "connect函数失败! 错误码: " << WSAGetLastError() << endl;
  93. return;
  94. }
  95. //像服务器发送GET请求,下载图片
  96. string  reqInfo = "GET " +(string)othPath+ " HTTP/1.1\r\nHost: " + (string)host + "\r\nConnection:Close\r\n\r\n";
  97. if (SOCKET_ERROR == send(sock, reqInfo.c_str(), reqInfo.size(), 0))
  98. {
  99. cout << "send error! 错误码: " << WSAGetLastError() << endl;
  100. closesocket(sock);
  101. return;
  102. }
  103. //PutImagtoSet();
  104. }
  105. void OutIamge(string imageUrl) //将图片命名,保存在目录下
  106. {
  107. int n;
  108. char temp[800];
  109. strcpy(temp, imageUrl.c_str());
  110. analyUrl(temp);
  111. preConnect();
  112. string photoname;
  113. photoname.resize(imageUrl.size());
  114. int k = 0;
  115. for (int i = 0; i<imageUrl.length(); i++){
  116. char ch = imageUrl[i];
  117. if (ch != '\\'&&ch != '/'&&ch != ':'&&ch != '*'&&ch != '?'&&ch != '"'&&ch != '<'&&ch != '>'&&ch != '|')
  118. photoname[k++] = ch;
  119. }
  120. photoname = "./img/"+photoname.substr(0, k) + ".jpg";
  121. fstream file;
  122. file.open(photoname, ios::out | ios::binary);
  123. char buf[1024];
  124. memset(buf, 0, sizeof(buf));
  125. n = recv(sock, buf, sizeof(buf)-1, 0);
  126. char *cpos = strstr(buf, "\r\n\r\n");
  127. //allHtml = "";
  128. //allHtml += string(cpos);
  129. file.write(cpos + strlen("\r\n\r\n"), n - (cpos - buf) - strlen("\r\n\r\n"));
  130. while ((n = recv(sock, buf, sizeof(buf)-1, 0)) > 0)
  131. {
  132. file.write(buf, n);
  133. //buf[n] = '\0';
  134. //allHtml += string(buf);
  135. }
  136. //file.write(allHtml.c_str(), allHtml.length());
  137. file.close();
  138. //closesocket(sock);
  139. }
  140. void PutImagtoSet()  //解析整个html代码
  141. {
  142. int n;
  143. //preConnect();
  144. char buf[1024];
  145. while ((n = recv(sock, buf, sizeof(buf)-1, 0)) > 0)
  146. {
  147. buf[n] = '\0';
  148. allHtml += string(buf);
  149. }
  150. regexGetimage(allHtml);
  151. regexGetcom(allHtml);
  152. }
  153. void bfs(string beginUrl)  //宽度优先搜索,像爬虫一样遍历网页
  154. {
  155. queue<string> q;
  156. q.push(beginUrl);
  157. while (!q.empty())
  158. {
  159. string cur = q.front();
  160. mp[cur]++;
  161. q.pop();
  162. char tmp[800];
  163. strcpy(tmp, cur.c_str());
  164. analyUrl(tmp);
  165. preConnect();
  166. PutImagtoSet();
  167. vector<string>::iterator ita = photoUrl.begin();
  168. for (ita; ita != photoUrl.end(); ++ita)
  169. {
  170. OutIamge(*ita);
  171. }
  172. photoUrl.clear();
  173. vector <string>::iterator it = comUrl.begin();
  174. for (it; it != comUrl.end(); ++it)
  175. {
  176. if (mp[*it]==0)
  177. q.push(*it);
  178. }
  179. comUrl.clear();
  180. }
  181. }
  182. int main()
  183. {
  184. CreateDirectoryA("./img", 0);  //在工程下创建文件夹
  185. string beginUrl= "http://news.qq.com/"; //输入起始网址
  186. bfs(beginUrl);
  187. return 0;
  188. }

效果图:

手把手教你写基于C++ Winsock的图片下载的网络爬虫的更多相关文章

  1. 网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接

    本文原作者:“水晶虾饺”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.引言 好多小白初次接触即时通讯(比如:IM或者消息推送应用)时,总是不 ...

  2. 只有20行Javascript代码!手把手教你写一个页面模板引擎

    http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...

  3. [原创]手把手教你写网络爬虫(4):Scrapy入门

    手把手教你写网络爬虫(4) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 上期我们理性的分析了为什么要学习Scrapy,理由只有一个,那就是免费,一分钱都不用花! 咦?怎么有人扔西红柿 ...

  4. 手把手教你写Kafka Streams程序

    本文从以下四个方面手把手教你写Kafka Streams程序: 一. 设置Maven项目 二. 编写第一个Streams应用程序:Pipe 三. 编写第二个Streams应用程序:Line Split ...

  5. 庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境

    庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境 一.介绍 说起微服务架构来,有一个环节是少不了的,那就是CI/CD持续集成的环境.当然,搭建CI/CD环境的工具很多, ...

  6. 手把手教你写Sublime中的Snippet

    手把手教你写Sublime中的Snippet Sublime Text号称最性感的编辑器, 并且越来越多人使用, 美观, 高效 关于如何使用Sublime text可以参考我的另一篇文章, 相信你会喜 ...

  7. 手把手教你写LKM rookit! 之 第一个lkm程序及模块隐藏(一)

    唉,一开始在纠结起个什么名字,感觉名字常常的很装逼,于是起了个这<手把手教你写LKM rookit> 我觉得: 你们觉得:...... 开始之前,我们先来理解一句话:一切的操作都是系统调用 ...

  8. 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...

  9. 手把手教你写电商爬虫-第四课 淘宝网商品爬虫自动JS渲染

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 手把手教你写电商爬虫-第三课 ...

随机推荐

  1. c#实现内存映射文件共享内存

    原文:http://blog.csdn.net/wangtiewei/article/details/51112668 内存映射文件是利用虚拟内存把文件映射到进程的地址空间中去,在此之后进程操作文件, ...

  2. Android开发使用软件

    开发环境搭建顺序: 1.安装开发工具 1).安装java 2).安装as 3).安装myeclipse 4).安装np++ md 5).安装svn git 2.配置环境变量 1).配置java jav ...

  3. JSON学习笔记-4

    JSON 数组 1.访问数组 1.一次访问一个嵌套内容值var myObj, x; myObj = { "name":"网站", , "sites&q ...

  4. sql 去重关键字 distinct

    单列去重: mysql: drop table test;create table test(id int(4));insert into test values(1),(2),(3),(4),(1) ...

  5. Charles基础

    一.Charles 监控其他设备连接方式 1.XP系统:控制面板——>Internet选项——>连接(tab)——>局域网(LAN)设置——>局域网设置——>代理服务器, ...

  6. CSS 小结笔记之盒子模型

    网页标签可以看成是一个个盒子,页面设计就像垒积木一样,在网页中将盒子摆好显示出来.在浏览器中可以很清楚的去看到一个标签的盒子,具体方法如下: 打开浏览器的开发人员工具,在Elements中选中一个标签 ...

  7. HAproxy simple

    下载地址 start install: wget     http://www.haproxy.org/download/1.7/src/haproxy-1.7.5.tar.gz tar   -xf  ...

  8. 使用ServiceBroker自动激活模拟"秒杀"场景

    1.简介 SQL Server Service Broker 是SQL server 里面比较独特的一个功能.它可帮助开发人员构建异步的松散耦合应用程序 ServiceBroker入门文章:http: ...

  9. phantomJs页面操作

    因为phantomjs能加载和操纵页面,它可以自动化地完美执行页面的各种操作. 操作文档: 脚本的被执行,就像它真的正在web 浏览器上运行一样. 下面的脚本,是读取元素id为myagent的文本内容 ...

  10. [翻译] OCMaskedTextField

    OCMaskedTextField https://github.com/OmerCora/OCMaskedTextField Simple class to display dynamically ...