上一小节,我们实现了下载一个网页。接下来的一步就是使用提取有用的信息。如何提取呢?一个比较好用和常见的方法就是使用正则表达式来提取的。想一想我们要做个什么样的网络爬虫好呢?我记得以前好像博客园里面有人写过一个提取博客园用户名的博客。我这次就实现这个好了。

  第一步我们要分析博客园一个URL的组成,我们每一个用户对应都有这样的一个主目录http://www.cnblogs.com/XXXXXXX 这样的一个主页(现在有了http://XXXXXXX.cnblogs.com这样的主页了,但是不常用)。所以我们判断一个字符串是不是博客园的有效用户,我们的做法就是提取一个像上面一样的URL,然后截取后面的用户名即可。

  带正则表达式的网页下载程序

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. #include <unistd.h>
  7. #include <netdb.h>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10. #include <regex.h>//正则表达式
  11.  
  12. #define BUF_SIZE 512
  13.  
  14. int reptile_regex(char * buf,char *pattern);
  15.  
  16. char ch[];//100k
  17.  
  18. int main(int argc,char *argv[])
  19. {
  20. struct sockaddr_in servAddr;
  21. struct hostent * host;
  22. int sockfd;
  23. char sendBuf[BUF_SIZE],recvBuf[BUF_SIZE];
  24. int sendSize,recvSize;
  25.  
  26. host=gethostbyname(argv[]);
  27. if(host==NULL)
  28. {
  29. perror("dns 解析失败");
  30. }
  31. servAddr.sin_family=AF_INET;
  32. servAddr.sin_addr=*((struct in_addr *)host->h_addr);
  33. servAddr.sin_port=htons(atoi(argv[]));
  34. bzero(&(servAddr.sin_zero),);
  35.  
  36. sockfd=socket(AF_INET,SOCK_STREAM,);
  37. if(sockfd==-)
  38. {
  39. perror("socket 创建失败");
  40. }
  41.  
  42. if(connect(sockfd,(struct sockaddr *)&servAddr,sizeof(struct sockaddr_in))==-)
  43. {
  44. perror("connect 失败");
  45. }
  46.  
  47. //构建一个http请求
  48. sprintf(sendBuf,"GET / HTTP/1.1 \r\nHost: %s \r\nConnection: Close \r\n\r\n",argv[]);
  49. if((sendSize=send(sockfd,sendBuf,BUF_SIZE,))==-)
  50. {
  51. perror("send 失败");
  52. }
  53. //获取http应答信息
  54. memset(recvBuf,,sizeof(recvBuf));
  55. memset(ch,,sizeof(ch));
  56. char pattern[]={};
  57. strcpy(pattern,"http://www.cnblogs.com/[[:alnum:]]*/");
  58. while(recvSize=recv(sockfd,recvBuf,BUF_SIZE,)>)
  59. {
  60. //printf("%s",recvBuf);
  61. strcat(ch,recvBuf);
  62. memset(recvBuf,,sizeof(recvBuf));
  63. }
  64. reptile_regex(ch,pattern);
  65.  
  66. return ;
  67. }
  68.  
  69. //第一个参数是要匹配的字符串,第二个参数是匹配的规则,返回匹配的个数
  70. int reptile_regex(char * buf,char *pattern)
  71. {
  72. size_t nmatch=;//最多匹配100个一次
  73. regmatch_t pm[];//与上面对应
  74. regex_t reg;//正则表达式指针
  75. regcomp(&reg,pattern,);//编译匹配模式
  76. int z=regexec(&reg,buf,nmatch,pm,);
  77. if(z==REG_NOMATCH)
  78. {
  79. ;//本次没有匹配到
  80. }
  81. else
  82. {
  83. for(int i=;i<&&pm[i].rm_so!=-;++i)
  84. {
  85. for(int j=pm[i].rm_so;j<pm[i].rm_eo;++j)
  86. {
  87. printf("%c",buf[j]);
  88. }
  89. //上面的遍历可以用下面函数代替
  90. //printf("%d=%s\n",i,substr(buf,pm[i].rm_so,pm[i].rm_eo));
  91. printf("\n");
  92. }
  93. }
  94. regfree(&reg);
  95. return ;
  96. }

  本来一开始以为这样就可以了,可是没想到每次都是匹配到第一个而已,后面怎么都匹配不到,还以为是正则写错了,但是就那么几个怎么可能错了。最后找到一篇博客,才知道,一次调用regexec是没有办法全部匹配出来的。要进行多次。哎,怎么这么麻烦呀。

  带正则表达式的网页下载程序修改版

  将reptile_regex函数修改如下即可实现多次匹配

  1. int reptile_regex(char * buf,char *pattern)
  2. {
  3. size_t nmatch=;//最多匹配100个一次
  4. regmatch_t pm[];//与上面对应
  5. regex_t reg;//正则表达式指针
  6. char * str;
  7. str=buf;
  8. regcomp(&reg,pattern,);//编译匹配模式
  9. while(regexec(&reg,str,nmatch,pm,)!=REG_NOMATCH)
  10. {
  11. for(int j=pm[].rm_so;j<pm[].rm_eo;++j)
  12. {
  13. printf("%c",str[j]);
  14. }
  15. //printf("%d=%s\n",i,substr(buf,pm[i].rm_so,pm[i].rm_eo));
  16. printf("\n");
  17. str=str+pm[].rm_eo;
  18. }
  19. regfree(&reg);
  20. return ;
  21. }

  好了,现在可以多次匹配了,但是又出现一个问题了,问题就是会有重复的用户名出现。如何避免呢?一个办法是把用户名保存起来,然后来一个用户名就一个一个进行比较,看是否有相同,如果都没有就加入到用户名组里面去。依次类推。不过一般爬虫爬到的用户名都会比较多,如果这样O(N)的比较效率不是很高,可以通过HASH降低为O(1)。但是设计一个hash函数比较麻烦,为了方便,我就使用一个map来处理,效率还好有O(logN)。

  防止重复后的网页下载程序

  1. ...
  2. int main(int argc,char *argv[])
  3. {
  4.     ...
    map<string,int> user;//第一个是用户名,第二个保存被加入的次数
  5.  
  6.     ...
    reptile_regex(ch,pattern,user);
  7. map<string,int>::iterator it;
  8. for(it=user.begin();it!=user.end();++it)
  9. {
  10. cout<<it->first<<endl;
  11. }
  12.  
  13. return ;
  14. }
  15.  
  16. //第一个参数是要匹配的字符串,第二个参数是匹配的规则,返回匹配的个数
  17. int reptile_regex(char * buf,char *pattern,map<string,int> & user)
  18. {
  19. size_t nmatch=;
  20. regmatch_t pm[];
  21. regex_t reg;//正则表达式指针
  22. char * str;
  23. char ch[];
  24. int i,j;
  25. str=buf;
  26. regcomp(&reg,pattern,);//编译匹配模式
  27. while(regexec(&reg,str,nmatch,pm,)!=REG_NOMATCH)
  28. {
  29. //http://www.cnblogs.com/
  30. i=pm[].rm_so+;
  31. for(j=i;j<pm[].rm_eo;++j)
  32. {
  33. //printf("%c",str[j]);
  34. ch[j-i]=str[j];
  35. }
  36. ch[j-i]=;
  37. string st(ch);
  38. user[st]++;
  39. //printf("%s",ch);
  40. //printf("%d=%s\n",i,substr(buf,pm[i].rm_so,pm[i].rm_eo));
  41. //printf("\n");
  42. str=str+pm[].rm_eo;
  43. }
  44. regfree(&reg);
  45. return ;
  46. }

  这样就把所有查询到的用户名都保存在users中了。而且对应的int还保存了查询到的次数。这个还可以在以后用来判断该用户的博客是否经常被人提及到的一个参考值。

  参考资料:

  正则表达式匹配多个问题: http://blog.163.com/lixiangqiu_9202/blog/static/53575037201412311211291/

  本文地址: http://www.cnblogs.com/wunaozai/p/3900169.html

Socket网络编程--网络爬虫(2)的更多相关文章

  1. [转] - Linux网络编程 -- 网络知识介绍

    (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...

  2. JAVA基础知识之网络编程——-网络基础(Java的http get和post请求,多线程下载)

    本文主要介绍java.net下为网络编程提供的一些基础包,InetAddress代表一个IP协议对象,可以用来获取IP地址,Host name之类的信息.URL和URLConnect可以用来访问web ...

  3. UNIX网络编程——网络IPC:套接字

    UNIX网络编程——网络IPC:套接字 Contents 套接字接口 套接字描述符 寻址 字节序 地址格式 地址查询 绑定地址 建立连接 数据传输 套接字选项 带外数据 UNIX域套接字 使用套接字的 ...

  4. Socket网络编程--网络爬虫(1)

    我们这个系列准备讲一下--网络爬虫.网络爬虫是搜索引擎系统中十分重要的组成部分,它负责从互联网中搜集网页,采集信息,这些网页信息用于建立索引从而为搜索引擎提供支持,它决定着整个引擎系统的内容是否丰富, ...

  5. 网络编程—网络基础概览、socket,TCP/UDP协议

    网络基础概览 socket概览 socket模块—TCP/UDP的实现 TCP/UDP总结 网络基础概览 osi七层协议各层主要的协议 # 物理层传输电信号1010101010 # 数据链路层,以太网 ...

  6. linux网络编程--网络编程的基本函数介绍与使用【转】

    本文转载自:http://blog.csdn.net/yusiguyuan/article/details/17538499 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览 ...

  7. python网络编程——网络IO模型

    1 网络IO模型介绍 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:    (1)同步阻塞IO(Blocking IO):即传统的IO模型.    (2)同步非阻塞IO(Non-bl ...

  8. JAVA基础知识之网络编程——-网络通信模型(IO模型)

    <Unix网络编程:卷1>中介绍了5中I/O模型,JAVA作为运行在宿主机上的程序,底层也遵循这5中I/O模型规则.这5中I/O模型分别是: 阻塞式IO 非阻塞式IO I/O复用 信号驱动 ...

  9. UNIX网络编程——网络I/O模型

    在学习UNIX网络编程的时候.一開始分不清 同步 和 异步,所以还是总结一下,理清下他们的差别比較好. IO分类 IO依据对IO的调度方式可分为堵塞IO.非堵塞IO.IO复用.信号驱动IO.异步IO. ...

随机推荐

  1. Evolution ZOJ2853

    矩阵太大导致爆栈 解决方法:矩阵不要开在main函数里面  全部在外面处理  main函数只能处理简单的赋值和加减 损失的要自减!! 最后为  sum+=t[i]*ans.m[i][n-1];   得 ...

  2. 094实战 关于js SDK的程序,java SDK的程序

    一:JS SDK 1.修改配置workspace 2.导入 3.Demo.html <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Trans ...

  3. U盘装机教程

    http://winpe.uqidong.asia/upzxpxt/upzxpxt.html

  4. 分布式系统缓存系列之guava cache

      guava是google的一个开源java框架,其github地址是 https://github.com/google/guava.guava工程包含了若干被Google的 Java项目广泛依赖 ...

  5. HDU 1051 Wooden Sticks 造木棍【贪心】

    题目链接>>> 转载于:https://www.cnblogs.com/Action-/archive/2012/07/03/2574800.html  题目大意: 给n根木棍的长度 ...

  6. BZOJ.4894.天赋(Matrix Tree定理 辗转相除)

    题目链接 有向图生成树个数.矩阵树定理,复习下. 和无向图不同的是,度数矩阵改为入度矩阵/出度矩阵,分别对应外向树/内向树. 删掉第i行第i列表示以i为根节点的生成树个数,所以必须删掉第1行第1列. ...

  7. 洛谷.1782.旅行商的背包(背包DP 单调队列)

    题目链接(卡常背包) 朴素的多重背包是: \(f[i][j] = \max\{ f[i-1][j-k*v[i]]+k*w[i] \}\),复杂度 \(O(nV*\sum num_i)\) 可以发现求\ ...

  8. #1075 : 开锁魔法III

    描述 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.初始时,崔克茜将会随机地选择 k 个盒子用魔法将它 ...

  9. LVS 之ARP

    原理 工作过程 主机A的IP地址为:192.168.1.1  MAC地址为:0A-11-22-33-44-01: 主机B的IP地址为:192.168.1.2  MAC地址为:0A-11-22-33-4 ...

  10. Oracle 11g透明网关连接Sqlserver 2000(转)

    Oracle 11g透明网关连接Sqlserver 2000: http://www.cnblogs.com/lightnear/archive/2013/02/03/2890858.html 透明网 ...