自己动手编写CSDN博客备份工具-blogspider之源码分析(1)

来源:互联网 发布:美国东密西根大学知乎 编辑:程序博客网 时间:2024/05/17 08:34
作者:gzshun. 原创作品,转载请标明出处!
来源:http://blog.csdn.net/gzshun


前一篇博文《自己动手编写CSDN博客备份工具-blogspider》介绍了blogspider的使用,使用方法很简单,blogspider可以将自己的CSDN博客下载到本地,这里也只提供最基本的功能。这两天有很多哥们儿给我发邮件,想要blogspider的源码,该程序是开源的,有需要的可以留下联系方式。
今天就介绍下blogspider的源代码,其实这里面比较核心的东西就是如何向网站服务器申请我们需要的网页文件。在Java语言,有提供一些网络包,已经将HTTP协议的东西都集成在了包里面,那实现起来就比较简单。最近由于春运期间,大家都在12306网站购票,于是网上就出现了一款抢票的软件,那是用Java写的,是一个谷歌插件。其实那个软件是我一个同事以前的同事写出来的,我们都从这里受益,也买到了回家过年的票,在这里感谢那位牛人。
向Java程序员了解了一下,那个软件的实现原理很简单,步骤如下:
1.访问网站获取网站信息
2.接受到网站服务器的响应消息
3.根据用户选择(硬座,硬卧)的消息再提交到网站服务器
4.得到网站的结果

主要是2个操作:一个是GET方法,一个是POST方法。
GET方法: 从网站服务器下载网页消息,比如网页浏览器可以浏览CSDN网站的新闻与图片,这些都是从网站服务器GET下载到本地;
POST方法:从本地将资料提交到网站服务器,比如在CSDN博客写完文章要点击发表博客,这时候是将一篇文章的所有信息给POST到CSDN服务器。
blogspider的主要目的,就是下载功能,这里使用的是GET方法,用C语言写的都比较低级,这些最基本的都需要自己来实现,等有空看看面向对象编程语言的实现。

废话少说,源码说话:

一.贴出代码中的调试宏,汗,太儿戏了

[cpp] view plaincopy
  1. /*Debug program macro*/  
  2. #if 0  
  3. #define SPIDER_DEBUG  
  4. #endif  

二.贴出代码中的一些宏定义,这些涉及到HTML文件的语法,但本代码不需要会html,只需要最基本的字符串处理:

[cpp] view plaincopy
  1. #define BUFSIZE          1024  
  2.   
  3. #define HTML_ARTICLE     ("<span class=\"link_title\">")  
  4. #define HTML_MULPAGE     ("class=\"pagelist\"")  
  5. #define BLOG_NEXT_LIST   ("article/list")  
  6. #define BLOG_TITLE       ("title=\"")  
  7. #define BLOG_HREF        ("<a href=\"")  
  8. #define BLOG_DATE        ("<span class=\"link_postdate\">")  
  9. #define BLOG_READ        ("<span class=\"link_view\"")  
  10. #define BLOG_COMMENT     ("<span class=\"link_comments\"")  
  11. #define BLOG_SPAN_HEAD   ("<span>")  
  12. #define BLOG_SPAN_END    ("</span>")  
  13. #define BLOG_RANK        ("blog_rank")  
  14. #define BLOG_LI          ("<li>")  
  15. #define BLOG_INDEX       ("index.html")  
  16. #define CSDN_BLOG_URL    ("http://blog.csdn.net")  
  17. #define CSDN_BLOG_HOST   ("blog.csdn.net")  
  18. #define CSDN_BLOG_PORT   (80)  
  19.   
  20. #define BLOG_LOCK        (10)  
  21. #define BLOG_UNLOCK      (11)  
  22. #define BLOG_DOWNLOAD    (20)  
  23. #define BLOG_UNDOWNLOAD  (21)  

上面的BLOG_LOCK,BLOG_UNLOCK是爬虫链表的处理锁,这是扩展预留的,现在还没用。本来要用多线程来处理链表,但经过测试,会产生竞争,导致connect超时,这等过完年再试试。

三.这里再给出爬虫链表的结构体与博客存放基本信息的结构体,里面有多一些变量,但没真正的使用,有些只是预留而已:

[cpp] view plaincopy
  1. typedef struct tag_blog_info {  
  2.     char *b_url;           /*网址*/  
  3.     char *b_host;          /*网站服务器主机名*/  
  4.     char *b_page_file;     /*页面文件名称*/  
  5.     char *b_local_file;    /*本地保存的文件名称*/  
  6.     char *b_title;         /*博客主题*/  
  7.     char *b_date;          /*博客发表日期*/  
  8.     int   b_port;          /*网址端口号*/  
  9.     int   b_sockfd;        /*网络套接字*/  
  10.     int   b_reads;         /*阅读次数*/  
  11.     int   b_comments;      /*评论次数*/  
  12.     int   b_download;      /*下载状态*/  
  13.     int   b_lock;          /*处理锁*/  
  14.     int   b_seq_num;       /*序号*/  
  15. }blog_info;  
  16.   
  17. typedef struct tag_blog_spider {  
  18.     blog_info *blog;  
  19.     struct tag_blog_spider *next;  
  20. }blog_spider;  
  21.   
  22. typedef struct tag_blog_rank {  
  23.     int   b_page_total;    /*博客总页数*/  
  24.     char *b_title;         /*博客标题*/  
  25.     char *b_page_view;     /*博客访问量*/  
  26.     char *b_integral;      /*博客积分*/  
  27.     char *b_ranking;       /*博客排名*/  
  28.     char *b_original;      /*博客原创文章数量*/  
  29.     char *b_reship;        /*博客转载文章数量*/  
  30.     char *b_translation;   /*博客译文文章数量*/  
  31.     char *b_comments;      /*博客评论数量*/  
  32. }blog_rank;  

四.在一个程序中,使用全局变量不是最好的方法,但都有优缺点:

使用全局变量:
1.优点:操作简单,不用提供太多的函数形参;
2.缺点:不好维护,代码可读性差;所以该程序只使用了3个全局变量。

[cpp] view plaincopy
  1. /*global variable*/  
  2. static int g_seq_num = 0;  
  3. static char csdn_id[255];  
  4. static struct hostent *web_host;  

web_host变量用来保存"blog.csdn.net"主机信息,在初始化socket的使用会使用到里面的IP地址, web_host->h_addr_list[0];

五.程序中定义了很多函数,如下:

[cpp] view plaincopy
  1. static char *strrstr(const char *s1, const char *s2);//从s1字符串中查找s2字符串,返回最后一次出现的地址  
  2. static char *strfchr(char *s);//过滤掉s字符串中不规则的字符  
  3. static int  init_spider(blog_spider **spider);//初始化博客爬虫节点,必须使用指针的指针,否则达不到预期效果  
  4. static int  init_rank(blog_rank **rank);//初始化博客存放基本信息的结构体  
  5. static void insert_spider(blog_spider *spider_head, blog_spider *spider);//将博客插入爬虫链表  
  6. static int  spider_size(blog_spider *spider_head);//计算爬虫链表的长度  
  7. static void print_spider(blog_spider *spider_head);//打印爬虫链表,保存到当前目录的*.log文件  
  8. static void print_rank(blog_rank *rank);//打印博客基本信息  
  9. static void free_spider(blog_spider *spider_head);//释放爬虫链表的空间  
  10. static void free_rank(blog_rank *rank);//释放博客基本信息的空间  
  11. static int get_blog_info(blog_spider *spider_head, blog_rank *rank);//从博客主页获取博客标题,博客文章的总页数,积分,排名等信息  
  12. static int analyse_index(blog_spider *spider_head);分析每一页博客的信息,并添加进爬虫链表  
  13. static int download_index(blog_spider *spider_head);//下载博客主页  
  14. static int download_blog(blog_spider *spider);//下载每一篇博客  
  15. static int get_web_host(const char *hostname);//得到"blog.csdn.net"网站的主机信息  
  16. static int connect_web(const blog_spider *spider);//初始化socket,并连接网站服务器  
  17. static int send_request(const blog_spider * spider);//给网站服务器发送请求  
  18. static int recv_response(const blog_spider * spider);//接受网站服务器的响应消息  

六.先给出上述2个字符串处理函数,这家伙,有点罗嗦

[cpp] view plaincopy
  1. /************************************************************** 
  2. strrstr  : 查找指定字符串, 返回最后一次出现的地址, 自己实现 
  3. ***************************************************************/  
  4. static char *strrstr(const char *s1, const char *s2)  
  5. {  
  6.     int len2;  
  7.     char *ps1;  
  8.   
  9.     if (!(len2 = strlen(s2))) {  
  10.         return (char *)s1;  
  11.     }  
  12.       
  13.     ps1 = (char *)s1 + strlen(s1) - 1;  
  14.     ps1 = ps1 - len2 + 1;  
  15.   
  16.     while (ps1 >= s1) {  
  17.         if ((*ps1 == *s2) && (strncmp(ps1, s2, len2) == 0)) {  
  18.             return (char *)ps1;  
  19.         }  
  20.         ps1--;  
  21.     }  
  22.   
  23.     return NULL;  
  24. }  
  25.   
  26. /********************************************************* 
  27. strfchr  : 查找指定字符串中不规则的字符, 并赋空 
  28. 若没有删除这些不规则的字符,则创建文件的时候将会出错 
  29. *********************************************************/  
  30. static char *strfchr(char *s)  
  31. {  
  32.     char *p = s;  
  33.       
  34.     while (*p) {  
  35.         if (('/' == *p) || ('?' == *p)) {  
  36.             *p = 0;  
  37.             strcat(s, "xxx");  
  38.               
  39.             return p;  
  40.         }  
  41.         p++;  
  42.     }  
  43.       
  44.     return NULL;  
  45. }  

引用星爷的一句话:"功夫其实绝对是适合男女老幼的,打打杀杀只是大家对它的误解。功夫更加是一种艺术,一种不屈的精神。所以,一直以来我都在找方法想将功夫重新包装起来,使得你们这些升斗小民对功夫能够有更深一层的了解。".
轻松一下,继续:

七.初始化爬虫链表,我把很多处理都给独立到函数了,这样可以增加程序的可读性,不能将所有功能都在main函数实现.

[cpp] view plaincopy
  1. /********************************************************* 
  2. 初始化博客爬虫的链表节点, 申请空间并赋空值 
  3. *********************************************************/  
  4. static int init_spider(blog_spider * * spider)  
  5. {  
  6.     *spider = (blog_spider *)malloc(sizeof(blog_spider));  
  7.     if (NULL == *spider) {  
  8.         #ifdef SPIDER_DEBUG  
  9.         fprintf(stderr, "malloc: %s\n", strerror(errno));  
  10.         #endif  
  11.         return -1;  
  12.     }  
  13.   
  14.     (*spider)->blog = (blog_info *)malloc(sizeof(blog_info));  
  15.     if (NULL == (*spider)->blog) {  
  16.         #ifdef SPIDER_DEBUG  
  17.         fprintf(stderr, "malloc: %s\n", strerror(errno));  
  18.         #endif  
  19.         free(*spider);  
  20.         return -1;  
  21.     }  
  22.   
  23.     (*spider)->blog->b_url           = NULL;  
  24.     (*spider)->blog->b_host          = strdup(CSDN_BLOG_HOST);  
  25.     (*spider)->blog->b_page_file     = NULL;  
  26.     (*spider)->blog->b_local_file    = NULL;  
  27.     (*spider)->blog->b_title         = NULL;  
  28.     (*spider)->blog->b_date          = NULL;  
  29.     (*spider)->blog->b_port          = CSDN_BLOG_PORT;  
  30.     (*spider)->blog->b_sockfd        = 0;  
  31.     (*spider)->blog->b_reads         = 0;  
  32.     (*spider)->blog->b_comments      = 0;  
  33.     (*spider)->blog->b_download      = BLOG_UNDOWNLOAD;  
  34.     (*spider)->blog->b_lock          = BLOG_UNLOCK;  
  35.     (*spider)->blog->b_seq_num       = 0;  
  36.           
  37.     (*spider)->next = NULL;  
  38.   
  39.     return 0;  
  40. }  
  41.   
  42. /********************************************************* 
  43. 初始化博客基本信息结构体,包含以下几个变量: 
  44. 1.博客页面总页数 
  45. 2.博客标题 
  46. 3.博客访问量 
  47. 4.博客积分 
  48. 5.博客排名 
  49. 6.博客原创文章数量 
  50. 7.博客转载文章数量 
  51. 8.博客译文文章数量 
  52. 9.博客评论数量 
  53. *********************************************************/  
  54. static int init_rank(blog_rank **rank)  
  55. {  
  56.     *rank = (blog_rank *)malloc(sizeof(blog_rank));  
  57.     if (NULL == *rank) {  
  58.         #ifdef SPIDER_DEBUG  
  59.         fprintf(stderr, "malloc: %s\n", strerror(errno));  
  60.         #endif  
  61.         return -1;  
  62.     }  
  63.   
  64.     (*rank)->b_page_total      = 0;  
  65.     (*rank)->b_title           = NULL;  
  66.     (*rank)->b_page_view       = NULL;  
  67.     (*rank)->b_integral        = NULL;  
  68.     (*rank)->b_ranking         = NULL;  
  69.     (*rank)->b_original        = NULL;  
  70.     (*rank)->b_reship          = NULL;  
  71.     (*rank)->b_translation     = NULL;  
  72.     (*rank)->b_comments        = NULL;  
  73.   
  74.     return 0;  
  75. }  

八.爬虫链表的一些处理,这些都比较简单,就都贴出来吧

[cpp] view plaincopy
  1. /********************************************************* 
  2. 将博客爬虫节点插入爬虫链表 
  3. *********************************************************/  
  4. static void insert_spider(blog_spider * spider_head, blog_spider * spider)  
  5. {  
  6.     blog_spider *pspider;  
  7.   
  8.     pspider = spider_head;  
  9.   
  10.     while (pspider->next) {  
  11.         pspider = pspider->next;  
  12.     }  
  13.       
  14.     pspider->next = spider;  
  15. }  
  16.   
  17. /********************************************************* 
  18. 返回爬虫链表长度 
  19. *********************************************************/  
  20. static int spider_size(blog_spider * spider_head)  
  21. {  
  22.     int count = 0;  
  23.     blog_spider *pspider;  
  24.   
  25.     pspider = spider_head;  
  26.   
  27.     while (pspider->next) {  
  28.         pspider = pspider->next;  
  29.         count++;  
  30.     }  
  31.       
  32.     return count;  
  33. }  

篇幅有点长,待下篇文章...

周星驰:你来这里干什么?
赵薇:我想帮你们比赛。
周星驰:你怎么帮?你快点回火星吧,地球是很危险的。

原创粉丝点击