上一小节我们已经实现了获取博客园最近博客的200页里面的用户名,并保存在一个map中。一开始是想通过这个用户名然后构造一个博客地址。然后在这个地址中查找心得用户名,但是后来发现这个的效率不是很高,虽然部分博客会引用别人的博客,也有机率会留有原博客的地址,这样我们可以爬取的到新的用户名。效率是不高。最后我发现博客园的关注和粉丝这个功能用于查找心得用户名很管用。只要该用户在博客园活跃过一段时间就或多或少会有粉丝或关注过某人。(不是有个说法:通过7个人能认识世界上任意一个人)所以这种做法应该是可行的。下面送上一张一开始就画好的框架。有些可能最后的实现跟图中不是很相同。
理论上这样是可以获取得到,但是在实践的时候才知道,原来没有登录的用户是访问不了别人的粉丝和关注,没想到这个美好的想法就这样没了。这可怎么办呢?(如果有知道的还请告诉我呀。)
由于种种原因,最后是实现不了这种循环爬取的效果了。都怪写之前没有好好试验一下,也可能是可以完成,只是自己太弱的原因了。只能提供几个可以获取的用户名的地址。因为达不到那种迭代的效果我就不写。真是太不好意思了。(http://www.cnblogs.com/cate/all/1 ~ http://www.cnblogs.com/cate/all/200、http://www.cnblogs.com/comment/1 ~ http://www.cnblogs.com/comment/200)
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 <queue> 13 #include <string> 14 #include <iostream> 15 16 using namespace std; 17 #define BUF_SIZE 512 18 19 struct URL 20 { 21 char host[64]; 22 char url[128];//除去域名后的url 23 }; 24 int reptile_regex(char * buf,char *pattern,map<string,int> & user); 25 int createSocket(char *hostname,int port); 26 int closeSocket(int sockfd); 27 int sendHttpRequest(int sockfd,struct URL url); 28 int recvHttpRespond(int sockfd,char *ch); 29 int reptile_regex_url(char * buf,char *pattern,map<string,int> & user,queue<string> & qstr,int start); 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 //http://www.cnblogs.com/sitehome/p/1 - 200 //最新博客的200篇 45 //初始化用户名 46 for(int i=1;i<=200;++i) 47 { 48 strcpy(url.host,"www.cnblogs.com"); 49 strcpy(url.url,"/sitehome/p/"); 50 char pch[8]; 51 sprintf(pch,"%d",i); 52 strcat(url.url,pch); 53 strcat(url.url,"/"); 54 cout<<"当前正在判断:"<<url.host<<url.url<<endl; 55 sockfd=createSocket(url.host,80); 56 sendHttpRequest(sockfd,url); 57 recvHttpRespond(sockfd,ch); 58 strcpy(pattern_user,"http://www.cnblogs.com/[[:alnum:][:cntrl:]\-\_]*"); 59 reptile_regex_url(ch,pattern_user,user,qstr,23); 60 61 closeSocket(sockfd); 62 } 63 //一开始以为是只要创建一次socket然后每次都可以进行send&recv的。但是后来测试好像不行,每次都要进行一次socket的创建 64 while(1) 65 { 66 while(!qstr.empty()) 67 { 68 //构造用户的粉丝地址和关注地址 69 strcpy(url.host,"home.cnblogs.com"); 70 strcpy(url.url,"/u/"); 71 strcat(url.url,qstr.front().c_str()); 72 qurl.push(url);//加入粉丝/关注地址 73 cout<<"粉丝/关注地址:"<<url.host<<url.url<<endl; 74 qstr.pop(); 75 } 76 while(!qurl.empty()) 77 { 78 url=qurl.front(); 79 qurl.pop(); 80 cout<<"现在正在判断:"; 81 cout<<url.host<<url.url<<endl; 82 //将获取到的地址进行再次获取用户名 83 sockfd=createSocket(url.host,80); 84 sendHttpRequest(sockfd,url); 85 recvHttpRespond(sockfd,ch); 86 //查看源代码发现地址居然不是想象中的http://home.cnblogs.com/u/用户名,而是/u/用户名 87 strcpy(pattern_user,"/u/[[:alnum:][:cntrl:]\-\_]*"); 88 reptile_regex_url(ch,pattern_user,user,qstr,3); 89 closeSocket(sockfd); 90 } 91 } 92 return 0; 93 } 94 95 96 //第一个参数是要匹配的字符串,第二个参数是匹配的规则 97 int reptile_regex_url(char * buf,char *pattern,map<string,int> & user,queue<string> & qstr,int start) 98 { 99 size_t nmatch=10; 100 regmatch_t pm[10]; 101 regex_t reg;//正则表达式指针 102 char * str; 103 char ch[32]; 104 int i,j,k; 105 str=buf; 106 regcomp(®,pattern,REG_EXTENDED);//编译匹配模式 107 while(regexec(®,str,nmatch,pm,0)!=REG_NOMATCH) 108 { 109 i=pm[0].rm_so+start; 110 k=0; 111 memset(ch,0,sizeof(ch)); 112 for(j=i;j<pm[0].rm_eo;++j) 113 { 114 if(str[j]!=0x01) //ctrl-v ctrl-a 115 { 116 ch[k++]=str[j]; 117 } 118 } 119 string st(ch); 120 if(user[st]==0) 121 { 122 cout<<"新加入的用户名:"<<st<<endl; 123 qstr.push(st); 124 } 125 user[st]++; 126 str=str+pm[0].rm_eo; 127 } 128 regfree(®); 129 return 0; 130 } 131 132 int closeSocket(int sockfd) 133 { 134 close(sockfd); 135 return 0; 136 } 137 138 int createSocket(char *hostname,int port) 139 { 140 struct sockaddr_in servAddr; 141 struct hostent * host; 142 int sockfd; 143 host=gethostbyname(hostname); 144 if(host==NULL) 145 { 146 perror("dns 解析失败"); 147 } 148 servAddr.sin_family=AF_INET; 149 servAddr.sin_addr=*((struct in_addr *)host->h_addr); 150 servAddr.sin_port=htons(port); 151 bzero(&(servAddr.sin_zero),8); 152 153 sockfd=socket(AF_INET,SOCK_STREAM,0); 154 if(sockfd==-1) 155 { 156 perror("socket 创建失败"); 157 } 158 159 if(connect(sockfd,(struct sockaddr *)&servAddr,sizeof(struct sockaddr_in))==-1) 160 { 161 perror("connect 失败"); 162 } 163 return sockfd; 164 } 165 166 int sendHttpRequest(int sockfd,struct URL url) 167 { 168 char sendBuf[BUF_SIZE]; 169 int sendSize; 170 //构建一个http请求 171 sprintf(sendBuf,"GET %s HTTP/1.1 Host: %s Connection: Close ",url.url,url.host); 172 printf("%s ",sendBuf); 173 if((sendSize=send(sockfd,sendBuf,BUF_SIZE,0))==-1) 174 { 175 perror("send 失败"); 176 } 177 return 0; 178 } 179 180 int recvHttpRespond(int sockfd,char *ch) 181 { 182 char recvBuf[BUF_SIZE]; 183 int recvSize; 184 //获取http应答信息 185 memset(recvBuf,0,sizeof(recvBuf)); 186 memset(ch,0,sizeof(ch)); 187 while(recvSize=recv(sockfd,recvBuf,BUF_SIZE,0)>0) 188 { 189 recvBuf[recvSize-2]=0; 190 strcat(ch,recvBuf); 191 memset(recvBuf,0,sizeof(recvBuf)); 192 } 193 return 0; 194 }
========2014年9月2日更新===========
今天找到一个解决的办法了。原来是使用cookie来解决,就是在一个请求头Header中增加一个Cookie端,具体的实现思路可以参考这篇博客: http://www.cnblogs.com/fengfenggirl/p/zhihu_shenhuifu.html 在这里先记录下来,下次如果要实现就可以参照这个思路了。