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


  上一小節我們實現了從博客園的首頁獲取一些用戶的用戶名,並保存起來。接下來的這一小節我將對每個用戶名構建一個用戶的博客主頁,然后從這個主頁獲取所有能獲取到的網頁,網頁的格式現在是http://www.cnblogs.com/yourname/p/xxxxxxxx.html以前是http://www.cnblogs.com/youurname/archive/xxxxxxx.html

  我的做法是把所有用戶名處理后得到的一個個url放到一個隊列里去,然后每次在這個隊列中拿一個url進行解析查找看有沒有新的用戶。如果有那么把新的用戶加入到map中,結束后就從隊列中再拿一個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 #include <map>
 12 #include <string>
 13 #include <iostream>
 14 
 15 using namespace std;
 16 #define BUF_SIZE 512
 17 
 18 struct URL
 19 {
 20     char host[64];
 21     char url[128];//除去域名后的url
 22 };
 23 int reptile_regex(char * buf,char *pattern,map<string,int> & user);
 24 int createSocket(char *hostname,int port);
 25 int closeSocket(int sockfd);
 26 int sendHttpRequest(int sockfd,struct URL url);
 27 int recvHttpRespond(int sockfd,char *ch);
 28 
 29 int main(int argc,char *argv[])
 30 {
 31     int sockfd;
 32     char ch[100000];//100k
 33     char pattern[128]={0};
 34     struct URL url;
 35     string str;
 36     map<string,int> user;//第一個是用戶名,第二個保存被加入的次數
 37 
 38     strcpy(url.host,"www.cnblogs.com");
 39     strcpy(url.url,"/");
 40     sockfd=createSocket(url.host,80);
 41     sendHttpRequest(sockfd,url);
 42     recvHttpRespond(sockfd,ch);
 43     strcpy(pattern,"http://www.cnblogs.com/[[:alnum:]\\-\\_]*");
 44     reptile_regex(ch,pattern,user);
 45     map<string,int>::iterator it;
 46     for(it=user.begin();it!=user.end();++it)
 47     {
 48         cout<<it->first<<endl;
 49     }
 50 
 51     closeSocket(sockfd);
 52     return 0;
 53 }
 54 
 55 
 56 //第一個參數是要匹配的字符串,第二個參數是匹配的規則
 57 int reptile_regex(char * buf,char *pattern,map<string,int> & user)
 58 {
 59     size_t nmatch=10;
 60     regmatch_t pm[10];
 61     regex_t reg;//正則表達式指針
 62     char * str;
 63     char ch[32];
 64     int i,j;
 65     str=buf;
 66     regcomp(&reg,pattern,0);//編譯匹配模式
 67     while(regexec(&reg,str,nmatch,pm,0)!=REG_NOMATCH)
 68     {
 69         i=pm[0].rm_so+23;
 70         for(j=i;j<pm[0].rm_eo;++j)
 71         {
 72             ch[j-i]=str[j];
 73         }
 74         ch[j-i]=0;
 75         string st(ch);
 76         user[st]++;
 77         //printf("%d=%s\n",i,substr(buf,pm[i].rm_so,pm[i].rm_eo));
 78         str=str+pm[0].rm_eo;
 79     }
 80     regfree(&reg);
 81     return 0;
 82 }
 83 
 84 int closeSocket(int sockfd)
 85 {
 86     close(sockfd);
 87     return 0;
 88 }
 89 
 90 int createSocket(char *hostname,int port)
 91 {
 92     struct sockaddr_in servAddr;
 93     struct hostent * host;
 94     int sockfd;
 95     host=gethostbyname(hostname);
 96     if(host==NULL)
 97     {
 98         perror("dns 解析失敗");
 99     }
100     servAddr.sin_family=AF_INET;
101     servAddr.sin_addr=*((struct in_addr *)host->h_addr);
102     servAddr.sin_port=htons(port);
103     bzero(&(servAddr.sin_zero),8);
104 
105     sockfd=socket(AF_INET,SOCK_STREAM,0);
106     if(sockfd==-1)
107     {
108         perror("socket 創建失敗");
109     }
110 
111     if(connect(sockfd,(struct sockaddr *)&servAddr,sizeof(struct sockaddr_in))==-1)
112     {
113         perror("connect 失敗");
114     }
115     return sockfd;
116 }
117 
118 int sendHttpRequest(int sockfd,struct URL url)
119 {
120     char sendBuf[BUF_SIZE];
121     int sendSize;
122     //構建一個http請求
123     sprintf(sendBuf,"GET %s HTTP/1.1 \r\nHost: %s \r\nConnection: Close \r\n\r\n",url.url,url.host);
124     if((sendSize=send(sockfd,sendBuf,BUF_SIZE,0))==-1)
125     {
126         perror("send 失敗");
127     }
128     return 0;
129 }
130 
131 int recvHttpRespond(int sockfd,char *ch)
132 {
133     char recvBuf[BUF_SIZE];
134     int recvSize;
135     //獲取http應答信息
136     memset(recvBuf,0,sizeof(recvBuf));
137     memset(ch,0,sizeof(ch));
138     while(recvSize=recv(sockfd,recvBuf,BUF_SIZE,0)>0)
139     {
140         strcat(ch,recvBuf);
141         memset(recvBuf,0,sizeof(recvBuf));
142     }
143     return 0;
144 }

   接下來要做的是創建兩個隊列,一個保存新進來的用戶,一個保存新url用來處理的。然后讓兩個隊列一直循環下去。理論上就可以爬到大多數的用戶名。如果用上面的程序進行匹配的話,有時候會出現匹配錯誤的時候,例如有一個用戶名是wunaozai,但是在匹配的過程中有時候會有wunao這樣的用戶名出現,一開始以為是重名,但是后來看了源碼發現沒有這個用戶了,然后多次獲取,每次都或多或少會有錯誤的用戶名出現。到底是為什么呢?我把用recv獲取到的網頁ch這個都打印出來,然后用grep過濾一下,會發現根本沒有錯,但是就是會匹配錯誤的用戶名。我就接着把網頁重定向到一個文件中,然后用vim打開,然后查找一下,然后真相大白了,原來這個文本中有時候會在用戶名處有這個符號(^A),UNIX中ctrl-v ctrl-a可以打印出來,ascii碼的值是0x01.哎弄了那么久,導致這篇博客那么晚才發布。在正則中可以用[:cntrl:]進行匹配。這一部分的代碼修改比較簡單。

  還有一個問題就是沒進行一次連接,都要創建一次socket連接。因為我的HTTP請求中的Connection是Close而不是keep-alive。

  修改BUG后的網絡爬蟲程序

   ... 
29
int reptile_regex_url(char * buf,char *pattern,map<string,int> & user,queue<string> & qstr); 30 31 32 int main(int argc,char *argv[]) 33 { 34 int sockfd; 35 char ch[100000];//100k 36 char pattern_user[128]={0}; 37 char pattern_url[128]={0}; 38 struct URL url; 39 string str; 40 map<string,int> user;//第一個是用戶名,第二個保存被加入的次數 41 queue<struct URL> qurl; 42 queue<string> qstr; 43 44 strcpy(url.host,"www.cnblogs.com"); 45 strcpy(url.url,"/"); 46 // 47 sockfd=createSocket(url.host,80); 48 //初始化用戶名 49 sendHttpRequest(sockfd,url); 50 recvHttpRespond(sockfd,ch); 51 //printf("%s\n",ch); 52 strcpy(pattern_user,"http://www.cnblogs.com/[[:alnum:][:cntrl:]\\-\\_]*"); 53 reptile_regex_url(ch,pattern_user,user,qstr); 54 map<string,int>::iterator it; 55 for(it=user.begin();it!=user.end();++it) 56 { 57 qstr.push(it->first); 58 strcpy(url.host,"www.cnblogs.com"); 59 strcpy(url.url,"/"); 60 strcat(url.url,it->first.c_str()); 61 strcat(url.url,"/"); 62 qurl.push(url); 63 } 64 //一開始以為是只要創建一次socket然后每次都可以進行send&recv的。但是后來測試好像不行,每次都要進行一次socket的創建 65 closeSocket(sockfd); 66 67 while(1) 68 { 69 while(!qurl.empty()) 70 { 71 url=qurl.front(); 72 qurl.pop(); 73 cout<<"現在正在判斷:"; 74 cout<<url.host<<url.url<<endl; 75 //將獲取到的地址進行再次獲取用戶名 76 strcpy(url.host,"www.cnblogs.com"); 77 strcpy(url.url,"/"); 78 sockfd=createSocket(url.host,80); 79 sendHttpRequest(sockfd,url); 80 recvHttpRespond(sockfd,ch); 81 //printf("\n\n\n%s\n",ch); 82 strcpy(pattern_user,"http://www.cnblogs.com/[[:alnum:][:cntrl:]\\-\\_]*"); 83 reptile_regex_url(ch,pattern_user,user,qstr); 84 closeSocket(sockfd); 85 } 86 while(!qstr.empty()) 87 { 88 qstr.pop(); 89 } 90 } 91 sendHttpRequest(sockfd,url); 92 recvHttpRespond(sockfd,ch); 93 strcpy(pattern_url,"http://www.cnblogs.com/[[:alnum:]\\-\\_]*/[[:alnum:]\\-\\_/]*\\.html"); 94 //reptile_regex(ch,pattern_url,qurl); 95 96 97 98 return 0; 99 } 100 101 102 //第一個參數是要匹配的字符串,第二個參數是匹配的規則 103 int reptile_regex_url(char * buf,char *pattern,map<string,int> & user,queue<string> & qstr) 104 { 105 size_t nmatch=10; 106 regmatch_t pm[10]; 107 regex_t reg;//正則表達式指針 108 char * str; 109 char ch[32]; 110 int i,j,k; 111 str=buf; 112 regcomp(&reg,pattern,REG_EXTENDED);//編譯匹配模式 113 while(regexec(&reg,str,nmatch,pm,0)!=REG_NOMATCH) 114 { 115 i=pm[0].rm_so+23; 116 k=0; 117 memset(ch,0,sizeof(ch)); 118 for(j=i;j<pm[0].rm_eo;++j)//這里修改**** 119 { 120 if(str[j]!=0x01) //ctrl-v ctrl-a 121 { 122 ch[k++]=str[j]; 123 } 124 } 125 string st(ch); 126 if(user[st]==0) 127 { 128 cout<<"新加入的用戶名:"<<st<<endl; 129 qstr.push(st); 130 } 131 user[st]++; 132 str=str+pm[0].rm_eo; 133 } 134 regfree(&reg); 135 return 0; 136 } 137 138 int closeSocket(int sockfd) 139 { 140 close(sockfd); 141 return 0; 142 } 143 144 int createSocket(char *hostname,int port) 145 {       ...
169 return sockfd; 170 } 171 172 int sendHttpRequest(int sockfd,struct URL url) 173 {      ...
183 return 0; 184 } 185 186 int recvHttpRespond(int sockfd,char *ch) 187 {      ...
199 return 0; 200 }

   我們從博客園的首頁中可以看到最新博客有200頁之多。每一頁的格式為http://www.cnblogs.com/sitehome/p/1 到 http://www.cnblogs.com/sitehome/p/200 所以我們可以根據這個格式進行獲取用戶名,一般也是這種方式獲取的比較多。

 1 int main(int argc,char *argv[])
 2 {
 3     int sockfd;
 4     char ch[100000];//100k
 5     char pattern_user[128]={0};
 6     char pattern_url[128]={0};
 7     struct URL url;
 8     string str;
 9     map<string,int> user;//第一個是用戶名,第二個保存被加入的次數
10     queue<struct URL> qurl;
11     queue<string> qstr;
12 
13     //http://www.cnblogs.com/sitehome/p/1 - 200 //最新博客的200篇
14     //初始化用戶名
15     for(int i=1;i<=200;++i)
16     {
17         strcpy(url.host,"www.cnblogs.com");
18         strcpy(url.url,"/sitehome/p/");
19         char pch[8];
20         sprintf(pch,"%d",i);
21         strcat(url.url,pch);
22         strcat(url.url,"/");
23         cout<<"當前正在判斷:"<<url.host<<url.url<<endl;
24         sockfd=createSocket(url.host,80);
25         sendHttpRequest(sockfd,url);
26         recvHttpRespond(sockfd,ch);
27         strcpy(pattern_user,"http://www.cnblogs.com/[[:alnum:][:cntrl:]\\-\\_]*");
28         reptile_regex_url(ch,pattern_user,user,qstr);
29 
30         closeSocket(sockfd);
31     }
32     map<string,int>::iterator it;
33     for(it=user.begin();it!=user.end();++it)
34     {
35         qstr.push(it->first);
36         strcpy(url.host,"www.cnblogs.com");
37         strcpy(url.url,"/");
38         strcat(url.url,it->first.c_str());
39         strcat(url.url,"/");
40         qurl.push(url);
41     }
42     //一開始以為是只要創建一次socket然后每次都可以進行send&recv的。但是后來測試好像不行,每次都要進行一次socket的創建
43 
    ... ...
73     return 0;
74 }

  獲取到的用戶名如下

  這一小節到這里就結束了,可以獲取用戶名了,不過雖然有200頁,不過獲取來還是很快的。下一節我將對這些用戶的關注和粉絲進行用戶名的再次提取。然后得到新的用戶名,然后再次提取,就這樣一直下去。理論上有在博客園活躍過的人都可以爬取的到,想想都激動。(這個是理論,我沒敢試,怕管理員找我談人生和理想。) 

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM