五、解析网页源代码,得到所需要信息
解析html并不复杂,只是有点麻烦而已。因为页面不可能全部读入内存在解析,只有分开读。分开读就必须要考虑需要查找的标签有可能只读到一部分这种情况。如读1024字节数据,我需要在里面找<div class="article_title">,碰巧的是,1024字节数据最后几位是<div class.在这种情况下,我不能简单的说1024字节里没有我所需要的,那就丢掉这1024字节。这样导致的后果解析出来的文章有遗漏。
需要解析的标签如下:
- #define ARTICLE_TITLE ("<div class=\"article_title\">")
- #define ARTICLE_MANAGE ("<div class=\"article_manage\">")
- #define BLOG_TITLE ("<div class=\"blog_title\">")
- #define URL_LINK ("<a href=")
- #define LINK_TITLE ("<span class=\"link_title\"><a href=")
- #define LINK_TITLE_END ("</a></span>")
- #define LINK_VIEW ("<span class=\"link_view\"")
- #define LINK_COMMENTS ("<span class=\"link_comments\"")
- #define LINK_POSTDATE ("<span class=\"link_postdate\">")
- #define BLOG_RANK ("<ul id=\"blog_rank\">")
- #define BLOG_STATISTICES (" <ul id=\"blog_statistics\">")
- #define URL_END ("</a>")
- #define DIV_END ("</div>")
- #define SPAN ("<span>")
- #define SPAN_END ("</span>")
- #define UL_END ("</ul>")
我的解析流程图如下所示,有待优化。
1、从保存的首页中读1024字节到数组。
2、strcat到2048个字节的数组,多定义一个2048字节的数组,就是为了解决查找标签有可能只读到一部分的情况。
3、判断状态,自定义5个状态
- #define TAG_RESOLV_ARTICLE 0 //解析文章标题、URL
- #define TAG_RESOLV_ARTICLE_STATIS 1//解析文章访问信息
- #define TAG_RESOLV_RANK 2//解析博主博客的访问信息
- #define TAG_RESOLV_STATIS 3//解析博主文章统计
- #define TAG_RESOLV_END 4//解析结束
4、根据状态查找相应的标签位置。
如果状态为TAG_RESOLV_RANK,则查找<ulid="blog_rank">和</ul>是否在数组中。只有两个标签同时在数组中,才解析标签所包含的有用信息。当<ulid="blog_rank">存在,而</ul>不存在时,则丢掉<ulid="blog_rank">之前的数据,在读1024字节数据后在解析。引用我自己写的一段代码来分析一下。
- /*
- *功能:解析博客排名,访问量等。
- */
- int resolvBlogRank(char *buf, int len, struct BloggerInfo *blogger, int *tag) {
- char *blog_rank_start, *blog_rank_end, *span_start, *span_end;
- char tmpbuf[BUFFERLEN * 2];
- int roll_back_loc,buf_len;
- bzero(tmpbuf, sizeof (tmpbuf));
- blog_rank_start = strstr(buf, BLOG_RANK);//找到BLOG_RANK第一次出现的位置(呵呵,只会出现一次)
- if (blog_rank_start == 0) {//如果buf数组里面没有出现BLOG_RANK,则只保留buf数组最后200个字节。为什么是200?主要是因为BLOG_RANK所包括的字符长度目前不会超过200哈。
- buf_len=strlen(buf);
- roll_back_loc=buf_len-200>0?buf_len-200:0;
- strcat(tmpbuf, buf + roll_back_loc);
- bzero(buf, len);
- strcat(buf, tmpbuf);
- } else {//BLOG_RANK出现在buf里面
- blog_rank_end = strstr(blog_rank_start + strlen(BLOG_RANK), UL_END);//找与<span style="white-space: pre-wrap; ">BLOG_RANK对应的结尾标签,这里是</ul></span>
- if (blog_rank_end == 0) {//UL标签没有找到,则保留BLOG_RANK之后的数据
- strcat(tmpbuf, blog_rank_start);
- bzero(buf, len);
- strcat(buf, tmpbuf);
- } else {//UL标签找到
- span_start = strstr(blog_rank_start + strlen(BLOG_RANK), SPAN);
- span_end = strstr(span_start + strlen(SPAN), SPAN_END);
- blogger->visits = myatoi(span_start + strlen(SPAN), span_end);//myatoi函数是我自己写的,用于将字符串转换为整数。如“第10000名”=》10000
- span_start = strstr(span_end + strlen(SPAN_END), SPAN);
- span_end = strstr(span_start + strlen(SPAN), SPAN_END);
- blogger->integral = myatoi(span_start + strlen(SPAN), span_end);
- span_start = strstr(span_end + strlen(SPAN_END), SPAN);
- span_end = strstr(span_start + strlen(SPAN), SPAN_END);
- blogger->ranking = myatoi(span_start + strlen(SPAN), span_end);
- *tag = TAG_RESOLV_STATIS;
- strcat(tmpbuf, blog_rank_end + strlen(UL_END));
- bzero(buf, len);
- strcat(buf, tmpbuf);
- }
- }
- return 0;
- }
5、循环,直到所解析的内容解析完毕
六、根据解析出来的文章URL,发送HTTP请求,保存返回的网页源代码
我是以文章名作为文件的名字,这里需要注意一个问题,当文件名出现/时,必须把文件名中的/替换为别的符号,我是替换为_符号。因为/在linux中属于路径的一部分。
至此,linux网页抓取工具就宣告完毕。
接下来的工作:
1.网页抓取是单线程的,接下来会采用多线程模式。
2.网页抓取没有抓取图片。js,css,所以在有网络的情况下才能呈现该有的样子。
3.代码很多地方需要优化,有的地方有点乱。
4.准备做个windows版本。不考虑做界面。
5.揉和window版本和linux版本,做个通用版。其实通用版还不是不同操作系统调用不同的函数而已。