自己做了一个简单的爬虫,目标是爬取网页上的图片。
爬虫实现的核心思想是一个广度优先搜索的过程。首先设定一个源网页作为搜索的起始点,然后下载其中所包含的图片,再将源网页中包含的链接加入搜索队列中,完成对源网页的浏览。接下来不停地取出搜索队列中的队首元素,下载其中的图片并将其中的链接加入队列中,完成后将该元素出队,即可实现爬虫在网页间的跳转。
主要的过程可以大致描述成这个样子:
q.push(s);
while (!q.empty())
{
v=q.front();
q.pop();
GetImg(v);
GetUrl(v);
}
搞清楚了主要框架,接下来就是具体实现的问题了。比较重要的有三个点,第一是如何获得网页的源码,第二是如何寻找网页里的图片和链接,第三是如何下载指定的图片。
获取网页的源代码可以用HINTERNET来实现。
寻找图片和链接就比较容易了,图片的前面会有<img,链接前面会有<a href=”,顺藤摸瓜就好了。当然这个判断并不完全可靠,不过我对html的格式就知道这么多了,暂且先这么写着。
下载指定的图片可以用URLDownloadToFile函数实现。
写程序的过程中遇到的几个困扰:
1.有的函数要求字符串是_TCHAR,而这个字符串在别处使用时又需要是char。
解决办法:
char转_TCHAR
MultiByteToWideChar(CP_ACP, 0, char字符串, -1, (LPWSTR)_TCHAR字符串, 长度);
_TCHAR转char
WideCharToMultiByte(CP_ACP, 0, (LPWSTR)_TCHAR字符串, -1, char字符串, 长度, NULL, NULL);
2.内存使用。
在保存取出的图片和链接的url时,我使用的时char [1024],结果一直蜜汁崩溃。反复运行多次后才发现原来有的url特别的长,超过了这个1024。于是,我把长度设置成了2048,并且加上长度超过2000时自动过掉这个点的限制才解决了这个问题。
这个程序之前的版本中,字符串都是用字符指针配合new,malloc等来定义的,指针用的时候总是心慌慌,怕一不小心就内存爆炸,干脆改成了全局数组变量。
介绍一下我的程序吧:
ShowMemoryInfo显示本程序内存使用情况,跟功能没太大关系,调试的时候用的。
GetPageBuf获得网页的源代码,保存在pageBuf里
GetImg下载当前网页里的图片
ImgExist判断图片是否下载成功
GetHref获得当前网页里的链接并加入队列
BFS搜索
源网页选取的是http://www.baidu.com/
下载的图片保存在img文件夹下。
运行时的详细信息输出在ExecLog.txt中。
MAX_NUDE_NUM为总的搜索的网页数。
运行结果:
程序:
#include "stdafx.h"
#define MAX_NODE_NUM 20
#pragma comment(lib, "urlmon.lib")//Download
#pragma comment(lib, "Wininet.lib")//Web page
#pragma comment(lib,"psapi.lib")//Memory
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <time.h>
#include <queue>
#include <windows.h>//Web page
#include <wininet.h>//
#include <tchar.h>
#include <urlmon.h>//Download
#include <psapi.h>//Memory
using namespace std;
queue<_TCHAR *> q;
vector<_TCHAR *> imgList;
vector<_TCHAR *> fileName;
char pageBufMemory[5000000];
_TCHAR hrefMemory[5000000];
_TCHAR * currentHrefPtr = hrefMemory;
_TCHAR imgMemory[5000000];
_TCHAR * currentImgPtr = imgMemory;
_TCHAR fileNameMemory[5000000];
_TCHAR * currentFNPtr = fileNameMemory;
char imgSrc[2048];
char href[2048];
char imgFile[2048];
//Show the Memory that is used
void ShowMemoryInfo() {
HANDLE handle = GetCurrentProcess();
PROCESS_MEMORY_COUNTERS pmc;
GetProcessMemoryInfo(handle, &pmc, sizeof(pmc));
cout << "内存使用:"
<< pmc.WorkingSetSize / 1000
<< "K/"
<< pmc.PeakWorkingSetSize / 1000
<< "K + "
<< pmc.PagefileUsage / 1000
<< "K/"
<< pmc.PeakPagefileUsage / 1000
<< "K"
<< endl;
}
//The Detail when program executing
FILE * execLog;
errno_t errExecLog = fopen_s(&execLog, "ExecLog.txt", "w");
//Get the source code of web page
bool GetPageBuf(_TCHAR * url, char * & pageBuf, int & byteLen) {
/*FILE * debuging;
errno_t errDebug = fopen_s(&debuging, "debuging.txt", "w");*/
byteLen = 0;
HINTERNET hSession = InternetOpen(_T("UrlTest"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hSession != NULL) {
HINTERNET hHttp = InternetOpenUrl(hSession, url, NULL, 0, INTERNET_FLAG_DONT_CACHE, 0);
if (hHttp != NULL) {
char Temp[1024];
DWORD recv = 0;
do {
InternetReadFile(hHttp, Temp, 1023, &recv);
for (int i = 0; i < recv; i++) {
pageBuf[byteLen++] = Temp[i];
}
Temp[recv] = '