Socket網絡編程--網絡爬蟲(2)

  上一小節,咱們實現了下載一個網頁。接下來的一步就是使用提取有用的信息。如何提取呢?一個比較好用和常見的方法就是使用正則表達式來提取的。想想咱們要作個什麼樣的網絡爬蟲好呢?我記得之前好像博客園裏面有人寫過一個提取博客園用戶名的博客。我此次就實現這個好了。html

  第一步咱們要分析博客園一個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[100000];//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[1]);
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[2]));
34     bzero(&(servAddr.sin_zero),8);
35 
36     sockfd=socket(AF_INET,SOCK_STREAM,0);
37     if(sockfd==-1)
38     {
39         perror("socket 建立失敗");
40     }
41 
42     if(connect(sockfd,(struct sockaddr *)&servAddr,sizeof(struct sockaddr_in))==-1)
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[1]);
49     if((sendSize=send(sockfd,sendBuf,BUF_SIZE,0))==-1)
50     {
51         perror("send 失敗");
52     }
53     //獲取http應答信息
54     memset(recvBuf,0,sizeof(recvBuf));
55     memset(ch,0,sizeof(ch));
56     char pattern[128]={0};
57     strcpy(pattern,"http://www.cnblogs.com/[[:alnum:]]*/");
58     while(recvSize=recv(sockfd,recvBuf,BUF_SIZE,0)>0)
59     {
60         //printf("%s",recvBuf);
61         strcat(ch,recvBuf);
62         memset(recvBuf,0,sizeof(recvBuf));
63     }
64     reptile_regex(ch,pattern);
65 
66     return 0;
67 }
68 
69 
70 //第一個參數是要匹配的字符串,第二個參數是匹配的規則,返回匹配的個數
71 int reptile_regex(char * buf,char *pattern)
72 {
73     size_t nmatch=100;//最多匹配100個一次
74     regmatch_t pm[100];//與上面對應
75     regex_t reg;//正則表達式指針
76     regcomp(&reg,pattern,0);//編譯匹配模式
77     int z=regexec(&reg,buf,nmatch,pm,0);
78     if(z==REG_NOMATCH)
79     {
80         ;//本次沒有匹配到
81     }
82     else
83     {
84         for(int i=0;i<100&&pm[i].rm_so!=-1;++i)
85         {
86             for(int j=pm[i].rm_so;j<pm[i].rm_eo;++j)
87             {
88                 printf("%c",buf[j]);
89             }
90             //上面的遍歷能夠用下面函數代替
91             //printf("%d=%s\n",i,substr(buf,pm[i].rm_so,pm[i].rm_eo));
92             printf("\n");
93         }
94     }
95     regfree(&reg);
96     return 0;
97 }

  原本一開始覺得這樣就能夠了,但是沒想到每次都是匹配到第一個而已,後面怎麼都匹配不到,還覺得是正則寫錯了,可是就那麼幾個怎麼可能錯了。最後找到一篇博客,才知道,一次調用regexec是沒有辦法所有匹配出來的。要進行屢次。哎,怎麼這麼麻煩呀。socket

  帶正則表達式的網頁下載程序修改版函數

  將reptile_regex函數修改以下便可實現屢次匹配spa

 1 int reptile_regex(char * buf,char *pattern)
 2 {
 3     size_t nmatch=10;//最多匹配100個一次
 4     regmatch_t pm[10];//與上面對應
 5     regex_t reg;//正則表達式指針
 6     char * str;
 7     str=buf;
 8     regcomp(&reg,pattern,0);//編譯匹配模式
 9     while(regexec(&reg,str,nmatch,pm,0)!=REG_NOMATCH)
10     {
11         for(int j=pm[0].rm_so;j<pm[0].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[0].rm_eo;
18     }
19     regfree(&reg);
20     return 0;
21 }

  好了,如今能夠屢次匹配了,可是又出現一個問題了,問題就是會有重複的用戶名出現。如何避免呢?一個辦法是把用戶名保存起來,而後來一個用戶名就一個一個進行比較,看是否有相同,若是都沒有就加入到用戶名組裏面去。依次類推。不過通常爬蟲爬到的用戶名都會比較多,若是這樣O(N)的比較效率不是很高,能夠經過HASH下降爲O(1)。可是設計一個hash函數比較麻煩,爲了方便,我就使用一個map來處理,效率還好有O(logN)。設計

  防止重複後的網頁下載程序指針

  ...
 22 int main(int argc,char *argv[])
 23 {
     ...
30 map<string,int> user;//第一個是用戶名,第二個保存被加入的次數 31     ...
70 reptile_regex(ch,pattern,user); 71 map<string,int>::iterator it; 72 for(it=user.begin();it!=user.end();++it) 73 { 74 cout<<it->first<<endl; 75 } 76 77 return 0; 78 } 79 80 81 //第一個參數是要匹配的字符串,第二個參數是匹配的規則,返回匹配的個數 82 int reptile_regex(char * buf,char *pattern,map<string,int> & user) 83 { 84 size_t nmatch=10; 85 regmatch_t pm[10]; 86 regex_t reg;//正則表達式指針 87 char * str; 88 char ch[32]; 89 int i,j; 90 str=buf; 91 regcomp(&reg,pattern,0);//編譯匹配模式 92 while(regexec(&reg,str,nmatch,pm,0)!=REG_NOMATCH) 93 { 94 //http://www.cnblogs.com/ 95 i=pm[0].rm_so+23; 96 for(j=i;j<pm[0].rm_eo;++j) 97 { 98 //printf("%c",str[j]); 99 ch[j-i]=str[j]; 100 } 101 ch[j-i]=0; 102 string st(ch); 103 user[st]++; 104 //printf("%s",ch); 105 //printf("%d=%s\n",i,substr(buf,pm[i].rm_so,pm[i].rm_eo)); 106 //printf("\n"); 107 str=str+pm[0].rm_eo; 108 } 109 regfree(&reg); 110 return 0; 111 }

  這樣就把全部查詢到的用戶名都保存在users中了。並且對應的int還保存了查詢到的次數。這個還能夠在之後用來判斷該用戶的博客是否常常被人說起到的一個參考值。code

 

  參考資料:htm

  正則表達式匹配多個問題: http://blog.163.com/lixiangqiu_9202/blog/static/53575037201412311211291/

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

相關文章
相關標籤/搜索