作者:gzshun. 原创作品,转载请标明出处!
来源:http://blog.csdn.net/gzshun
周星驰:剪头发不应该看别人怎么剪就发神经跟流行,要配合啊!你看你的发型,完全不配合你的脸型脸型又不配合身型,身型又和发型完全不搭,而且极度不配合啊!!欢哥!你究竟要怎么样啊? 《算死草》
在开篇,先happy下,新年到,开开心心过好年!
已经写了几篇文章,把代码贡献给有需要的人,这里列出前几篇文章,需要的马上跳转,麻利的。。
《自己动手编写CSDN博客备份工具-blogspider》
《自己动手编写CSDN博客备份工具-blogspider之源码分析(1)》
《自己动手编写CSDN博客备份工具-blogspider之源码分析(2)》
本文是blogspider最重要的部分,开始要下载并分析CSDN博客,把博文的URL分析出来,添加进链表,GO!
一.先下载博客主页到本地的index.html
下载网页到本地的步骤:
建立连接 -> 连接网站服务器 -> 发送请求 -> 接收响应 -> 保存到本地
connect_web -> send_request -> recv_response
源码说话:
二.建立连接,并连接网站服务器
先从"blog.csdn.net"主机名获取到IP地址,如下:
/********************************************************** 根据主机名获取到主机信息,主要是获取到IP地址. **********************************************************/ static int get_web_host(const char * hostname) { /*get host ip*/ web_host = gethostbyname(hostname); if (NULL == web_host) { #ifdef SPIDER_DEBUG fprintf(stderr, "gethostbyname: %s\n", strerror(errno)); #endif return -1; } #ifdef SPIDER_DEBUG printf("IP: %s\n", inet_ntoa(*((struct in_addr *)web_host->h_addr_list[0]))); #endif return 0; }
开始初始化套接字,连接网站服务器:
三.发送请求到网站服务器
HTTP协议里面比较重要的有俩方法:GET和POST
向网站服务器发送请求:
GET %s HTTP/1.1\r\n
Accept: */*\r\n
Accept-Language: zh-cn\r\n
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\n
Host: %s:%d\r\n
Connection: Close\r\n\r\n
GET后面跟的是请求的文件,剩下的是一些基本信息,该协议头的结束标志是一个空行,所以程序可以通过判断"\r\n\r\n"为结束标志。具体HTTP协议可以上网搜索一些资料,这里不做介绍。
源码说话:
/********************************************************** 向网站服务器发送请求 **********************************************************/ static int send_request(const blog_spider * spider) { int ret; char request[BUFSIZE]; memset(request, 0, sizeof(request)); sprintf(request, "GET %s HTTP/1.1\r\n" "Accept: */*\r\n" "Accept-Language: zh-cn\r\n" "User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\n" "Host: %s:%d\r\n" "Connection: Close\r\n" "\r\n", spider->blog->b_page_file, spider->blog->b_host, spider->blog->b_port); ret = send(spider->blog->b_sockfd, request, sizeof(request), 0); if (ret < 0) { #ifdef SPIDER_DEBUG fprintf(stderr, "send: %s\n", strerror(errno)); #endif return -1; } #ifdef SPIDER_DEBUG printf("request:\n%s\n", request); #endif return 0; }
周星驰:扫地只不过是我的表面工作,我真正地身份是一位研究僧(生)。《少林足球》
轻松一下,继续。。。
四.接收响应消息
向网站服务器发送了请求,当然必须在本地开始接收。由于可能是网速慢的原因,接收响应消息与消息正体速度有点慢。这里使用了select函数与FD_SET集合来处理,当监听到socket可读,才开始读取消息并保存到本地。
/*************************************************************************************** 接受网站服务器的反馈信息,得到请求的文件内容 向服务器发送请求信息或者服务器的响应消息,以空行结束,所以可以用"\r\n\r\n"来判断结束标志 select: int select (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval * timeout); >0: 正确 -1: 出错 0 : 超时 void FD_ZERO(fd_set *fdset); // clear all bits in fdset void FD_SET(int fd, fd_set *fdset); // turn on the bit for fd in fdset void FD_CLR(int fd, fd_set *fdset); // turn off the bit for fd in fdset int FD_ISSET(int fd, fd_set *fdset); // is the bit for fd on in fdset ***************************************************************************************/ static int recv_response(const blog_spider * spider) { int ret, end, recvsize, count; char recvbuf[BUFSIZE]; fd_set read_fds; struct timeval timeout; FILE *fp; /*建议时间要长点, select失败可能的原因是收到网站的响应消息超时*/ timeout.tv_sec = 30; timeout.tv_usec = 0; while (1) { FD_ZERO(&read_fds); FD_SET(spider->blog->b_sockfd, &read_fds); ret = select(spider->blog->b_sockfd+1, &read_fds, NULL, NULL, &timeout); if (-1 == ret) { /*出错,直接返回错误*/ #ifdef SPIDER_DEBUG fprintf(stderr, "select: %s\n", strerror(errno)); #endif return -1; } else if (0 == ret) { /*超时, 继续轮询*/ #ifdef SPIDER_DEBUG fprintf(stderr, "select timeout: %s\n", spider->blog->b_title); #endif goto fail_recv_response; } /*接受到数据*/ if (FD_ISSET(spider->blog->b_sockfd, &read_fds)) { end = 0; count = 0; /*这里出错可能是文件名不规则,比如"3/5",'/'在Linux是代表目录*/ fp = fopen(spider->blog->b_local_file, "w+"); if (NULL == fp) { goto fail_recv_response; } spider->blog->b_download = BLOG_DOWNLOAD; while (read(spider->blog->b_sockfd, recvbuf, 1) == 1) { if(end< 4) { if(recvbuf[0] == '\r' || recvbuf[0] == '\n') { end++; } else { end = 0; } /*这里是http服务器反馈的消息头,若需要,则可以保存下来*/ } else { fputc(recvbuf[0], fp); count++; if (1024 == count) { fflush(fp); } } } fclose(fp); break; } } return 0; fail_recv_response: spider->blog->b_download = BLOG_UNDOWNLOAD; return -1; }
五.获取CSDN博客的URL,与博客的发表日期,阅读次数,评论次数,并添加进爬虫链表
代码本身已经注释得很清楚了,看注释就够了。HTTP协议涉及到很多知识点,有空可以写写程序来练练手,blogspider效率上还是不够高,有空添加线程处理,同时下载多个博客,这样才能提高效率。
需要blogspider的源代码,可以留下E-mail。