手把手教你写基于C++ Winsock的图片下载的网络爬虫
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,给出好的建议。
- /*下载图片 C++ Winsock 网络编程*/
- #define _CRT_SECURE_NO_WARNINGS //vs 2013用于忽略c语言安全性警告
- #include <cstdio>
- #include <iostream>
- #include <fstream>
- #include <string>
- #include <cstring>
- #include <regex>
- #include <vector>
- #include <queue>
- #include <algorithm>
- #include <winsock2.h>
- #include <map>
- using namespace std;
- #pragma comment(lib, "ws2_32.lib")
- char host[500];
- int num = 1;
- char othPath[800];
- string allHtml;
- vector <string> photoUrl;
- vector <string> comUrl;
- map <string, int> mp; //防止相同网址重复遍历
- SOCKET sock;
- bool analyUrl(char *url) //仅支持http协议,解析出主机和IP地址
- {
- char *pos = strstr(url, "http://");
- if (pos == NULL)
- return false;
- else
- pos += 7;
- sscanf(pos, "%[^/]%s", host, othPath); //http:// 后一直到/之前的是主机名
- cout << "host: " << host << " repath:" << othPath << endl;
- return true;
- }
- void regexGetimage(string &allHtml) //C++11 正则表达式提取图片url
- {
- smatch mat;
- regex pattern("src=\"(.*?\.jpg)\"");
- string::const_iterator start = allHtml.begin();
- string::const_iterator end = allHtml.end();
- while (regex_search(start, end, mat, pattern))
- {
- string msg(mat[1].first, mat[1].second);
- photoUrl.push_back(msg);
- start = mat[0].second;
- }
- }
- void regexGetcom(string &allHtml) //提取网页中的http://的url
- {
- smatch mat;
- //regex pattern("href=\"(.*?\.html)\"");
- regex pattern("href=\"(http://[^\s'\"]+)\"");
- string::const_iterator start = allHtml.begin();
- string::const_iterator end = allHtml.end();
- while (regex_search(start, end, mat, pattern))
- {
- string msg(mat[1].first, mat[1].second);
- comUrl.push_back(msg);
- start = mat[0].second;
- }
- }
- void preConnect() //socket进行网络连接
- {
- WSADATA wd;
- WSAStartup(MAKEWORD(2, 2), &wd);
- sock = socket(AF_INET, SOCK_STREAM, 0);
- if (sock == INVALID_SOCKET)
- {
- cout << "建立socket失败! 错误码: " << WSAGetLastError() << endl;
- return;
- }
- sockaddr_in sa = { AF_INET };
- int n = bind(sock, (sockaddr*)&sa, sizeof(sa));
- if (n == SOCKET_ERROR)
- {
- cout << "bind函数失败! 错误码: " << WSAGetLastError() << endl;
- return;
- }
- struct hostent *p = gethostbyname(host);
- if (p == NULL)
- {
- cout << "主机无法解析出ip! 错误吗: " << WSAGetLastError() << endl;
- return;
- }
- sa.sin_port = htons(80);
- memcpy(&sa.sin_addr, p->h_addr, 4);// with some problems ???
- //sa.sin_addr.S_un.S_addr = inet_addr(*(p->h_addr_list));
- //cout << *(p->h_addr_list) << endl;
- n = connect(sock, (sockaddr*)&sa, sizeof(sa));
- if (n == SOCKET_ERROR)
- {
- cout << "connect函数失败! 错误码: " << WSAGetLastError() << endl;
- return;
- }
- //像服务器发送GET请求,下载图片
- string reqInfo = "GET " +(string)othPath+ " HTTP/1.1\r\nHost: " + (string)host + "\r\nConnection:Close\r\n\r\n";
- if (SOCKET_ERROR == send(sock, reqInfo.c_str(), reqInfo.size(), 0))
- {
- cout << "send error! 错误码: " << WSAGetLastError() << endl;
- closesocket(sock);
- return;
- }
- //PutImagtoSet();
- }
- void OutIamge(string imageUrl) //将图片命名,保存在目录下
- {
- int n;
- char temp[800];
- strcpy(temp, imageUrl.c_str());
- analyUrl(temp);
- preConnect();
- string photoname;
- photoname.resize(imageUrl.size());
- int k = 0;
- for (int i = 0; i<imageUrl.length(); i++){
- char ch = imageUrl[i];
- if (ch != '\\'&&ch != '/'&&ch != ':'&&ch != '*'&&ch != '?'&&ch != '"'&&ch != '<'&&ch != '>'&&ch != '|')
- photoname[k++] = ch;
- }
- photoname = "./img/"+photoname.substr(0, k) + ".jpg";
- fstream file;
- file.open(photoname, ios::out | ios::binary);
- char buf[1024];
- memset(buf, 0, sizeof(buf));
- n = recv(sock, buf, sizeof(buf)-1, 0);
- char *cpos = strstr(buf, "\r\n\r\n");
- //allHtml = "";
- //allHtml += string(cpos);
- file.write(cpos + strlen("\r\n\r\n"), n - (cpos - buf) - strlen("\r\n\r\n"));
- while ((n = recv(sock, buf, sizeof(buf)-1, 0)) > 0)
- {
- file.write(buf, n);
- //buf[n] = '\0';
- //allHtml += string(buf);
- }
- //file.write(allHtml.c_str(), allHtml.length());
- file.close();
- //closesocket(sock);
- }
- void PutImagtoSet() //解析整个html代码
- {
- int n;
- //preConnect();
- char buf[1024];
- while ((n = recv(sock, buf, sizeof(buf)-1, 0)) > 0)
- {
- buf[n] = '\0';
- allHtml += string(buf);
- }
- regexGetimage(allHtml);
- regexGetcom(allHtml);
- }
- void bfs(string beginUrl) //宽度优先搜索,像爬虫一样遍历网页
- {
- queue<string> q;
- q.push(beginUrl);
- while (!q.empty())
- {
- string cur = q.front();
- mp[cur]++;
- q.pop();
- char tmp[800];
- strcpy(tmp, cur.c_str());
- analyUrl(tmp);
- preConnect();
- PutImagtoSet();
- vector<string>::iterator ita = photoUrl.begin();
- for (ita; ita != photoUrl.end(); ++ita)
- {
- OutIamge(*ita);
- }
- photoUrl.clear();
- vector <string>::iterator it = comUrl.begin();
- for (it; it != comUrl.end(); ++it)
- {
- if (mp[*it]==0)
- q.push(*it);
- }
- comUrl.clear();
- }
- }
- int main()
- {
- CreateDirectoryA("./img", 0); //在工程下创建文件夹
- string beginUrl= "http://news.qq.com/"; //输入起始网址
- bfs(beginUrl);
- return 0;
- }
效果图:
手把手教你写基于C++ Winsock的图片下载的网络爬虫的更多相关文章
- 网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接
本文原作者:“水晶虾饺”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.引言 好多小白初次接触即时通讯(比如:IM或者消息推送应用)时,总是不 ...
- 只有20行Javascript代码!手把手教你写一个页面模板引擎
http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...
- [原创]手把手教你写网络爬虫(4):Scrapy入门
手把手教你写网络爬虫(4) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 上期我们理性的分析了为什么要学习Scrapy,理由只有一个,那就是免费,一分钱都不用花! 咦?怎么有人扔西红柿 ...
- 手把手教你写Kafka Streams程序
本文从以下四个方面手把手教你写Kafka Streams程序: 一. 设置Maven项目 二. 编写第一个Streams应用程序:Pipe 三. 编写第二个Streams应用程序:Line Split ...
- 庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境
庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境 一.介绍 说起微服务架构来,有一个环节是少不了的,那就是CI/CD持续集成的环境.当然,搭建CI/CD环境的工具很多, ...
- 手把手教你写Sublime中的Snippet
手把手教你写Sublime中的Snippet Sublime Text号称最性感的编辑器, 并且越来越多人使用, 美观, 高效 关于如何使用Sublime text可以参考我的另一篇文章, 相信你会喜 ...
- 手把手教你写LKM rookit! 之 第一个lkm程序及模块隐藏(一)
唉,一开始在纠结起个什么名字,感觉名字常常的很装逼,于是起了个这<手把手教你写LKM rookit> 我觉得: 你们觉得:...... 开始之前,我们先来理解一句话:一切的操作都是系统调用 ...
- 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取
版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...
- 手把手教你写电商爬虫-第四课 淘宝网商品爬虫自动JS渲染
版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 手把手教你写电商爬虫-第三课 ...
随机推荐
- 【代码笔记】iOS-JASidePanelsDemo(侧滑)
一,效果图. 二,工程图. 三,代码. AppDelegate.h #import <UIKit/UIKit.h> @class JASidePanelController; @inter ...
- ubuntu将python3设为默认后再安装支持python3.x的包
简介: ubuntu默认python2.7版本,如果想要装python3.x版本,请记住python2.7版本一定不能卸载!!!但是即使我 python3.x版本安装成功,当运行python脚本时,系 ...
- 带你从零学ReactNative开发跨平台App开发(十一)
ReactNative跨平台开发系列教程: 带你从零学ReactNative开发跨平台App开发(一) 带你从零学ReactNative开发跨平台App开发(二) 带你从零学ReactNative开发 ...
- 普通用户查看Oracle参数的值
create or replace function get_param(p_name in varchar2)return varchar2as l_param_type number; l_in ...
- redis mysql验证 redis_mysql_check.py
# coding:utf-8 import pymysql import redis import sys def con_mysql(sql): db = pymysql.connect(host= ...
- Oracle EBS 表空间
-- DATA 这里仅提供查询锁和解锁.有时,锁是正常的,所以杀掉正锁着的进程有一定的风险性. 具体步骤如下: -- 1.0 查看 holder的进程 , 'Holder: ', 'Waiter: ' ...
- MySQL存储引擎之Spider内核深度解析
作者介绍 朱阅岸,中国人民大学博士,现供职于腾讯云数据库团队.研究方向主要为数据库系统理论与实现.新硬件平台下的数据库系统以及TP+AP型混合系统. Spider是为MySQL/MariaDB开发 ...
- 使用CocoaPods
使用CocoaPods 1. 安装CocoaPods 有时候,默认的 https://rubygems.org/ 访问不了,你需要先执行以下命令移除掉sources gem sources -r ht ...
- 解析苹果的官方例子LazyTableImages实现图片懒加载原理
解析苹果的官方例子LazyTableImages实现图片懒加载原理 首先在官网下载源码: https://developer.apple.com/library/ios/navigation/#sec ...
- django中的权限控制(form增删改)
Django默认提供了权限控制,但只能对使用了其自带的登录认证的用户进行权限控制,说白了就是只能对存储在auth_user表中的用户进行权限控制,但不能对未登录过的用户进行权限控制.但如果通过集成LD ...