Socket网络编程--网络爬虫(3)

南笙酒味 提交于 2019-11-27 19:24:25

  上一小节我们实现了从博客园的首页获取一些用户的用户名,并保存起来。接下来的这一小节我将对每个用户名构建一个用户的博客主页,然后从这个主页获取所有能获取到的网页,网页的格式现在是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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!