一个简单的爬虫程序,爬取网站的图片

最简单的爬虫是分析网页,如果要爬取图片,就要将图片在网页中的格式进行分析,取到图片的连接,接着下载图片;

由于网页中还会链接到其他的网页,所以需要将其中的所有网页取出,一般有两种算法:BFS广度优先遍历:和DFS深度优先遍历。

对于一个简单的爬虫程序,需要以下几部分组成

1.解析网页URL得到对应的主机名和资源值

2.需要向服务端发送http请求,得到相应的相应

3.提取所有的url及图片地址

4.文件的下载和存储

5.一个BFS或者DFS算法

用类CrawlerC来实现:

主要的成员函数有:

bool analyUrl(const string&url,string&host,string&resource);//通过对URL的解析得到对应的主机地址和资源

bool getHttpRes(const string&url,char*&response,int &bytesRead);//向对应的主机发送http请求

void getHtmlRes(string &htmlResponse,vector&imgUrl,const string&host);//通过解析响应得到网页上的图片地址

string getFileName(const string&url);//依据URL建立文件名,便于区分哪个网站的图片

void downLoadImg(vector&imgUrls,const string&url);//从远端下载图片

void BFS(const string &url);//广度优先遍历

bool CrawlerC::analyUrl(const string &url, string &host, string &resource)
{
	//url中http是协议,之后的www什么的是域名,之后的/后面是资源
	if (strlen(url.c_str()) > 2000){
		cout << "url长度过长,可能是错误的url" << endl;
		return false;
	}
	//判断字符串2是不是字符串1的子串,如果是,返回首次出现的地址
	const char*pos = strstr(url.c_str(), "http://");//在URL中找到http出现的位置
	if (pos == NULL)
		pos = url.c_str();//没有找到http,则默认是http
	else
		pos += strlen("http://");//有http需要将其跳过
	if (strstr(pos, "/") == 0)//需要取到第一个/之前的值,这部分值是主机的地址
		return false;
	char pHost[100];
	char pResource[2000];
	sscanf(pos, "%[^/]%s", pHost, pResource);//格式化字符串,取到以/为结束的字符串
	host = pHost;//得到主机名
	resource = pResource;//得到资源
	return true;
}

bool CrawlerC::getHttpRes(const string &url, char*&response, int &bytesRead)
{
	string host, resource;
	if (!analyUrl(url, host, resource)){//解析URL得到对应的主机名和资源
		cout <h_addr,4);

	//建立连接
	if (connect(sock, (SOCKADDR*)&sa, sizeof(sa)) != 0){
		cout << "无法连接" << endl;
		return false;
	}

	//准备发送HTTP请求
	string request = "GET " + resource + " HTTP/1.1\r\nHost:" + host + "\r\nConnection:Close\r\n\r\n";
	//发送数据
	if (SOCKET_ERROR == send(sock, request.c_str(), request.size(), 0)){
		cout << "发送失败" << endl;
		closesocket(sock);
		return false;
	}

	//接收数据
	int nContentLeng = 1048576;
	char *pageBuf = (char*)malloc(nContentLeng);
	//char *pageBuf = new char[nContentLeng];
	memset(pageBuf, 0, nContentLeng);

	bytesRead = 0;//已经读取的长度
	int ret = 1;//拷贝完成的长度
	cout << "read:" << endl;
	while (ret > 0){
		//指定接收的缓存及其长度,返回拷贝完成的字节数
		ret = recv(sock, pageBuf + bytesRead, nContentLeng - bytesRead, 0);
		if (ret > 0)
			bytesRead += ret;
		if (nContentLeng - bytesRead < 100){//缓冲区的剩余长度过小
			cout << "重新分配" << endl;
			nContentLeng *= 2;
			pageBuf = (char*)realloc(pageBuf, nContentLeng);
		}
		cout << ret << endl;
	}
	pageBuf[bytesRead] = '\0';//添加结束标志
	response = pageBuf;
	closesocket(sock);
	return true;
}
void CrawlerC::getHtmlRes(string &htmlResponse, vector&imgUrls, const string &host)
{
	//找到所有的连接加入到队列中
	const char* p = htmlResponse.c_str();//需要解析的返回串
	char* tag = "href=\"";//这个匹配href=",找到链接
	const char *pos = strstr(p,tag);
	ofstream ofile("url.txt", ios::app);
	while (pos){//循环取出所有的超链接
		pos += strlen(tag);//pos现在指向了网址的开始
		const char*nextQ = strstr(pos, "\"");//匹配引号
		if (nextQ){//取到网址的结束位
			char *url = new char[nextQ - pos + 1];
			sscanf(pos, "%[^\"]", url);//取到以引号结束的子串
			string surl = url;
			if (visitedUrl.find(surl) == visitedUrl.end()){
				visitedUrl.insert(surl);//不在url中
				ofile << surl << endl;
				hrefUrl.push(surl);
			}
			pos = strstr(pos, tag);
			delete[]url;
		}
	}
	ofile << endl << endl;
	ofile.close();

	tag = " strstr(pos0, ">")){
			pos = strstr(pos0, att1);
			if (!pos){
				continue;
			}
			else
				pos = pos + strlen(att1);
		}
		else{
			pos = pos2 + strlen(att2);
		}
		const char*nextQ = strstr(pos, "\"");//找后引号
		if (nextQ){
			char*url = new char[nextQ - pos + 1];
			sscanf(pos, "%[^\"]", url);
			cout << url << endl;
			string imgUrl = url;
			if (visitedImg.find(imgUrl) == visitedImg.end()){
				visitedImg.insert(imgUrl);
				imgUrls.push_back(imgUrl);
			}
			pos0 = strstr(pos0, tag);
			delete[]url;
		}
	}
}

std::string CrawlerC::getFileName(const string&url)
{
	string fileName;
	fileName.resize(url.size());
	int k = 0;
	for (int i = 0; i < (int)url.size(); i++){
		char ch = url[i];
		//去除特殊符号
		if (ch != '\\'&&ch != '/'&&ch != ':'&&ch != '*'&&ch != '?'&&ch != '"'&&ch != '<'&&ch != '>'&&ch != '|')
			fileName[k++] = ch;
	}
	return fileName.substr(0, k);
}

void CrawlerC::downLoadImg(vector&imgUrl, const string&url)
{
	//生成保存图片的文件夹
	string foldname = getFileName(url);
	foldname = "./img/" + foldname;
	if (!CreateDirectory(foldname.c_str(), NULL))
		cout << "不能创建文件夹" << endl;
	char*image=NULL;
	int byteRead;
	for (int i = 0; i < imgUrl.size(); i++){
		string str = imgUrl[i];
		int pos = str.find_last_of(".");
		if (pos==string::npos)
			continue;
		else{
			string ext = str.substr(pos + 1, str.size() - pos - 1);
			if (ext != "bmp"&& ext != "jpg" && ext != "jpeg"&& ext != "gif"&&ext != "png")
				continue;
		}
		if (getHttpRes(imgUrl[i], image, byteRead)){
			if (strlen(image)==0)
				continue;
			const char*p = image;
			const char * pos = strstr(p, "\r\n\r\n") + strlen("\r\n\r\n");//图片文件的开始位置
			int index = imgUrl[i].find_last_of("/");
			if (index != string::npos){
				string imgname = imgUrl[i].substr(index, imgUrl[i].size());//截取文件名
				ofstream ofile(foldname + imgname, ios::binary);
				if (!ofile.is_open())
					continue;
				cout << g_ImgCnt++ << foldname + imgname << endl;
				ofile.write(pos, byteRead - (pos - p));
				ofile.close();
			}
			free(image);
		}
	}
}

void CrawlerC::BFS(const string &url)
{
	char*response=0;
	int bytes;
	if (!getHttpRes(url, response, bytes)){
		cout << "错误的url" << endl;
		return;
	}
	string httpResponse = response;
	free(response);
	string fileName = getFileName(url);

	ofstream ofile("./html/" + fileName);
	if (ofile.is_open()){
		ofile << httpResponse << endl;
		ofile.close();
	}
	vectorimageUrl;
	getHtmlRes(httpResponse, imageUrl,url);
	downLoadImg(imageUrl, url);
}
int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2, ), &wsaData) != 0)//这个是初始化socket
		return 1;
	CreateDirectory("./img", 0);
	CreateDirectory("./html", 0);
	CrawlerC test;
	string url = "http://www.nipic.com/index.html";
	test.BFS(url);
	test.visitedUrl.insert(url);
	while (test.hrefUrl.size() > 0){
		string url1 = test.hrefUrl.front();
		//cout << url1 << endl;
		test.BFS(url1);//对于每一个url都要进行搜索
		test.hrefUrl.pop();
	}
	WSACleanup();
	return 0;
}

也可以将提取URL和下载图片作为不同的线程来处理

参考链接:http://blog.csdn.net/huangxy10/article/details/8120106

你可能感兴趣的:(爬虫相关)