初心者です。
よろしくお願いいいたします。
早速ですが、私はC言語でWebスクレイピング・Webクローリングをしたいと思っております。
WebスクレイピングやWebクローリングについて自分自身、調べたりしたところ、PHPやpython、perl、ruby、shellscriptなどのスクリプト言語を用いるのが普通のようでした。
しかし、どうしてもC言語で実現したいと思っています。
どなたかC言語で、HTMLファイルの取得のやり方、Webスクレイピング・Webクローリングのやり方をご存知の方がいらっしゃいましたらご協力いただけませんでしょうか?
普通はスクリプト言語でやるものでC言語でやるメリットが一切分からないと言う意見も確かにあると思いますが、ここには記載することはできませんが、諸事情ありましてどうしてもC言語で実現せねばならなくなってしまいました。
私の知識はWebサイトがWebブラウザで閲覧することが可能なのは、WebブラウザがHTTPを用いて、WebサーバにHTMLファイルの送信を要求して、サーバがそのHTTPを受け取って、サーバがHTTPを用いてパソコンに通信を行ない、受け取ったHTMLファイルをWebブラウザが読み取って表示をしているということです。
ユーザーインタフェースなどは一切気にしません。
例えば、
gcc http.c
./a.out
と実行すると、
実行したパソコンの自分で指定したディレクトリにHTMLファイルが保存されるというものです。
これだけでは分からない部分もたくさんあると思います。
自分でも分からないことがたくさんあるので追記などで対応していきます。
どうかお助けください。
よろしくお願いいたします。
###追記(07/27)【vimdiff syakyou.c othon.cで打ち間違いに気がつき解決いたしました】
皆様のアドバイスをもとにプログラムを書いています。
エラーの意味が分からず困っています。
写し間違いだと思うのですが、どこを間違えているのか、エラーの描いてあることがよく分かりません。
どなたか解説していただけませか。
そもそもコードが公開されているのであれば、それをコピーすればいいのかもしれませんが、少しでも理解して身につけたいと思い、コピペはせず、見ながら打ち込むようにしています。
効率が最悪かもしれませんが、親切な方がいらっしゃいましたら、このひよっこに暖かい手を差し伸べていただけないでしょうか。
また、効率の良いやり方・勉強法等をご存知の方がいらっしゃいましたら、併せて教えていただけましたら幸いです。
よろしくお願いいたします。
参考にさせて頂いたプログラム:[C言語] HTTPクライアントを作ってみる
C
1//syakyou.c 2 1 #include <stdio.h> 3 2 #include <string.h> 4 3 #include <stdlib.h> 5 4 #include <sys/types.h> 6 5 #include <netdb.h> 7 6 #include <netinet/in.h> 8 7 #include <sys/param.h> 9 8 #include <unistd.h> 10 9 11 10 #define BUF_LEN 256 12 11 13 12 struct URL { 14 13 char host[BUF_LEN]; 15 14 char path[BUF_LEN]; 16 15 char query[BUF_LEN]; 17 16 char fragment[BUF_LEN]; 18 17 unsigned short port; 19 18 }; 20 19 21 20 /** 22 21 * @param urlStr URLテキスト 23 22 */ 24 23 void parseURL(const char *urlStr, struct URL *url, char **error); 25 24 26 25 int main(int argc, char **argv) { 27 26 28 27 //ソケットのためのファイルディスクリプタ 29 28 int s; 30 29 31 30 //IPアドレスの解決 32 31 struct addrinfo hints, *res; 33 32 struct in_addr addr; 34 33 int err; 35 34 36 35 //サーバに送るHTTPプロトコル用バッファ 37 36 char send_buf[BUF_LEN]; 38 37 39 38 struct URL url1 = { 40 39 "css-eblog.com","/",80 41 40 }; 42 41 43 42 //URLが指定されていたら 44 43 if(argc>1){ 45 44 char *error = NULL; 46 45 parseURL(argv[1],&url,&error); 47 46 48 47 if(error){ 49 48 printf("%s/n",error); 50 49 return 1; 51 50 } 52 51 } 53 52 54 53 printf("http://%s%s%sを取得します。\n\n", url.host, url1.path, url.query); 55 54 56 55 //0クリア 57 56 memset(&hints, 0, sizeof(hints)); 58 57 hints.ai_socktype = SOCK_STREAM; 59 58 hints.ai_socktyoe = AF_INET; 60 59 61 60 char *serviceType = "http"; 62 61 63 62 if((err = getaddrinfo(url.host, serviceType, &hints, &res)) != 0) { 64 63 printf("error %d\n", err); 65 64 return 1; 66 65 } 67 66 68 67 //ソケット生成 69 68 if((s = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) { 70 69 fprintf(stderr, "ソケットの作成に失敗しました。\n"); 71 70 return 1; 72 71 } 73 72 74 73 //サーバに接続 75 74 if(connect(s, res->ai_addr, res->ai_addrlen) != 0) { 76 75 fprintf(stderr, "connectに失敗しました。\n"); 77 76 return 1; 78 77 } 79 78 80 79 //HTTPプロトコルの開始 &サーバに送信 81 80 sprintf(send_buf, "GET %s%s HTTP/1.0\r\n", url.path, url.query); 82 81 write(s, send_buf, strlen(send_buf)); 83 82 84 83 sprintf(send_buf, "Host: %s:%d\r\n", url.host, url.path); 85 84 write(s, send_buf, strlen(send_buf)); 86 85 87 86 sprintf(send_buf, "\r\n"); 88 87 write(s, send_buf, strlen(send_buf)); 89 88 90 89 //受信が終わるまでループ 91 90 while(1){ 92 91 char buf[BUF_LEN]; 93 92 int read_size; 94 93 read_size = read(s, buf, BUF_LEN); 95 94 96 95 if(read_size > 0) { 97 96 write(1, buf, read_size); 98 97 } 99 98 else{ 100 99 break; 101100 } 102101 } 103102 104103 //ソケットを閉じる 105104 close(s); 106105 107106 return 0; 108107 } 109108 110109 111110 void parseURL(const char *urlStr, struct URL *url, char **error) { 112111 char host_path[BUF_LEN]; 113112 114113 if(strlen(urlStr) > BUF_LEN - 1) { 115114 *error = "URLが長すぎます。\n"; 116115 return; 117116 } 118117 119118 //http://から始まる文字列で 120119 //sscanfが成功して 121120 //http://のあとに何かの文字列が存在するなら 122121 if(strstr(urlStr, "http://") && 123122 sscanf(urlStr, "http://%s", host_path) && 124123 strcmp(urlStr, "http://")){ 125124 126125 char *p = NULL; 127126 128127 p = strchr(host_path, '#'); 129128 if(p != NULL){ 130129 strcpy(url->fragment, p); 131130 *p = '\0'; 132131 } 133132 134133 p = strchr(host_path, '?'); 135134 if(p != NULL){ 136135 strcpy(url->query, p); 137136 *p = '\0'; 138137 } 139138 140139 p = strchr(host_path, '/'); 141140 if(p != NULL){ 142141 strcpy(url->path, p); 143142 *p = '\0'; 144143 } 145144 146145 strcpy(url->host, host_path); 147146 148147 //ホスト名の部分に":"が含まれていたら 149148 p = strchr(url1->host, ':'); 150149 if(p != NULL){ 151150 //ポート番号を取得 152151 url->port = atoi(p + 1); 153152 154153 //数字ではない(atoiが失敗)か、0だったら 155154 //ポート番号は80に決め打ち 156155 if(url->port <= 0) { 157156 url->port = 80; 158157 } 159158 160159 //終端文字で空にする 161160 *p = '\0'; 162161 } 163162 else{ 164163 url->port = 80; 165164 } 166165 } 167166 else{ 168167 *error = "URLはhttp://host/pathの形式で指定してください。\n"; 169168 return; 170169 } 171170 }
###エラーコード【vimdiff syakyou.c othon.cで打ち間違いに気がつき解決いたしました】
$ gcc -o syakyou syakyou.c
syakyou.c: In function ‘main’:
syakyou.c:45:21: error: ‘url’ undeclared (first use in this function)
parseURL(argv[1],&url,&error);
^
syakyou.c:45:21: note: each undeclared identifier is reported only once for each function it appears in
syakyou.c:58:7: error: ‘struct addrinfo’ has no member named ‘ai_socktyoe’
hints.ai_socktyoe = AF_INET;
^
syakyou.c: In function ‘parseURL’:
syakyou.c:148:14: error: ‘url1’ undeclared (first use in this function)
p = strchr(url1->host, ':');
^
###試したこと【vimdiff syakyou.c othon.cで解決いたしました】
otehon.cはコピペしたテキストプログラム
syakyou.cは見ながら写したプログラム
$ diff syakyou.c otehon.c
###結果【vimdiff syakyou.c othon.cで打ち間違いに気がつき解決いたしました】
otehon.cはインデントが空白
syakyou.cはインデントがタブ
のため、差分の量が多過ぎて見分けがつかず、お手上げです。
重ね重ね申し訳ありません。
###追記(08/08)【打ち間違いに気がつき解決いたしました】
###warningが出て困っています。どなたか教えていただけないでしょうか【vimdiff syakyou.c othon.cで打ち間違いに気がつき解決いたしました】
$ gcc syakyou.c
syakyou.c: In function ‘main’:
syakyou.c:83:20: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘char *’ [-Wformat=]
sprintf(send_buf, "Host: %s:%d\r\n", url.host, url.path); ^
です。
これはどういった意味なのでしょうか?
char型なのに %d にしているというということでしょうか?
###試したこと【vimdiff syakyou.c othon.cで打ち間違いに気がつき解決いたしました】
Chironian様のアドバイスを元に
c
1 58 hints.ai_socktyoe = AF_INET; を 258 hints.ai_family = AF_INET; に打ち直しました。 3url1 を 4url に直しました。
よろしくお願いします。
また、コンパイル時に
errorやwarningがあった場合、皆様はどのように解決するときのヒントや初心者が犯しやすいミスがあれば教えていただけましたら幸いです。
###追記(2016/08/23)
実行結果がうまくいっていないような気がします。
参考にさせて頂いたプログラム:[C言語] HTTPクライアントを作ってみるを実行してみたのですが、以下のような実行結果が得られました。
なんだか少し違うような気がするのですが、
実行結果がうまくいっているのかの私では判断ができません。
自信がなく、よくわからず質問してしまって申し訳ありませんが、
この実行結果で正しいのでしょうか?
$ ./a.out http://css-eblog.com/P を取得します。 // http://css-eblog.com/Pを取得しているところが少し疑問です。 // http://css-eblog.comになるのではと思いますがどうなのでしょうか…? HTTP/1.1 400 Bad Request Date: Sat, 27 Aug 2016 15:23:02 GMT Server: Apache/2.2.31 Content-Length: 226 Connection: close Content-Type: text/html; charset=iso-8859-1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>400 Bad Request</title> // http://css-eblog.com/Pだからそんなページはないと言われてしまう? // http://css-eblog.comだったら検索できましたが // http://css-eblog.com/Pでは検索できませんでした。 </head><body> <h1>Bad Request</h1> <p>Your browser sent a request that this server could not understand.<br /> </p> </body></html>
自信がなく、よくわからず質問してしまい、恐縮ですが、
http://css-eblog.com/P になってしまう原因は何でしょうか?
どうすれば http://css-eblog.com になるでしょうか?
何卒、よろしくお願いします。

バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/07/19 09:03
2016/07/22 05:52