Récupèrer la source html d'un site web

Voir le sujet précédent Voir le sujet suivant Aller en bas

Récupèrer la source html d'un site web

Message  -ed- le Jeu 29 Mai 2008 - 2:37

Je débute dans le monde des réseaux, et je voudrais réaliser une application qui récupère la source html d'un site web donné.

Je pense qu'il faut que je fasse une requête http sur le serveur du site et que je lui envoie un message pour qu'il m'envoie la source html. Ensuite j'ai plus qu'à l'enregistrer dans un fichier.


Je conseille de commencer par lire un peu de théorie ici :

http://mapage.noos.fr/emdel/reseaux.htm

il y a aussi des exercices pratiques.

une fois qu'on sait faire un client, il suffit de se connecter au serveur, de faire une requête http et de récupérer le fichier html (qui sera précédé d'un en-tête http)...

Je conseille un travail en couche qui respecte les différents niveaux (socket, http, html)

ça n'est pas compliqué, tu créés un petit client TCP qui va se connecter au serveur du site (généralement port 80 mais à vérifier quand même), ensuite il faut envoyer une requête sur ta socket de type :
GET /index.html\n

ensuite tu n'as plus qu'à lire sur ta socket la page html demandé, dans ce cas là, c'est index.html.

Bon j'ai fait un effort pour le faire en C89, la gestion des erreurs est minimale, je laisse le soin à Emmanuel de le corriger si besoin est.
Code:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>

#define HOST "mapage.noos.fr"
#define PORT 80
#define FILE "/emdel/"

int main (void)
{
   struct hostent *host_address = gethostbyname (HOST);

   if (host_address != NULL)
   {
      int socket_id = socket (PF_INET, SOCK_STREAM, 0);

      if (socket_id != -1)
      {
         struct sockaddr_in sockname;
         int optval = 1;

         setsockopt (socket_id, SOL_SOCKET, SO_REUSEADDR, &optval,
                     sizeof optval);

         sockname.sin_family = host_address->h_addrtype;
         sockname.sin_port = htons (PORT);
         memcpy (&sockname.sin_addr.s_addr, host_address->h_addr,
                 host_address->h_length);

         if (connect (socket_id, (struct sockaddr *) &sockname,
                      sizeof (struct sockaddr_in)) != -1)
         {
            char str[1024];
            int l;

            write (socket_id, "GET ", sizeof ("GET ") - 1);
            write (socket_id, FILE, sizeof (FILE) - 1);
            write (socket_id, " HTTP/1.1\r\nHost: ",
                   sizeof (" HTTP/1.1\r\nHost: ") - 1);
            write (socket_id, HOST, sizeof (HOST) - 1);
            write (socket_id, "\r\n\r\n", sizeof ("\r\n\r\n") - 1);

            while ((l = read (socket_id, str, sizeof (str) - 1)))
            {
               str[l] = 0;
               printf ("%s", str);
            }
            shutdown (socket_id, 2);
            close (socket_id);
         }
         else                   /* connect () */
         {
            perror ("connect ()");
         }
      }
      else                      /* socket () */
      {
         perror ("socket ()");
      }
   }
   else                         /* gethostbyname () */
   {
      perror ("gethostbyname ()");
   }

   return 0;
}
Quelques erreurs...

ceci fonctionne sous Windows et devrait fonctionner aussi sous GNU/Linux...
Code:

#include <stdio.h>
#include <string.h>

#if defined (WIN32)
#include <winsock2.h>
#elif defined (linux)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket(s) close (s)
typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;
#endif

#define HOST "mapage.noos.fr"
#define PORT 80
#define FILENAME "/emdel/index.htm"

static void envoyer (SOCKET sock, char const *s)
{
   send (sock, s, strlen (s), 0);
}

int client (void)
{
   struct hostent *host_address = gethostbyname (HOST);

   if (host_address != NULL)
   {
      SOCKET socket_id = socket (PF_INET, SOCK_STREAM, 0);

      if (socket_id != INVALID_SOCKET)
      {
         struct sockaddr_in sockname = { 0 };
#if 0
         int optval = 1;

         setsockopt (socket_id, SOL_SOCKET, SO_REUSEADDR, (void *) &optval,
                     sizeof optval);
#endif

         sockname.sin_family = host_address->h_addrtype;
         sockname.sin_port = htons (PORT);
         memcpy (&sockname.sin_addr.s_addr, host_address->h_addr,
                 host_address->h_length);

         if (connect (socket_id, (struct sockaddr *) &sockname,
                      sizeof (struct sockaddr_in)) != -1)
         {
            char str[1024];
            int l;

            envoyer (socket_id, "GET ");
            envoyer (socket_id, FILENAME);
            envoyer (socket_id, " HTTP/1.1\r\nHost: ");
            envoyer (socket_id, HOST);
            envoyer (socket_id, "\r\n\r\n");

            while ((l = recv (socket_id, str, sizeof (str) - 1, 0)))
            {
               str[l] = 0;
               printf ("%s", str);
            }
            shutdown (socket_id, 2);
            closesocket (socket_id);
         }
         else                   /* connect () */
         {
            perror ("connect ()");
         }
      }
      else                      /* socket () */
      {
         perror ("socket ()");
      }
   }
   else                         /* gethostbyname () */
   {
      perror ("gethostbyname ()");
   }

   return 0;
}

int main (void)
{
#if defined (WIN32)
   WSADATA WSAData;
   int erreur = WSAStartup (MAKEWORD (2, 0), &WSAData);
#else
   int erreur = 0;
#endif
   client ();
#if defined (WIN32)
   WSACleanup ();
#endif
   return EXIT_SUCCESS;
}
Mise à part la portabilité (je n'ai pas Windows désolé) quelles sont les erreurs? (si c'est pour les sizeof, ça n'est pas une erreur, c'était voulu)

OK, pour les sizeof, mais c'est pas une solution générale qui peut prêter à confusion et on se retrouve avec des 0 inutiles en ligne...

Par contre, j'ai pas compris pourquoi tu as appelé setsockopt ().

Le nom du fichier était incorrect.

Mais il y a quelque chose que je n'ai pas très bien compris dans votre code.
Moi j'ai appris à donner une adresse IP à mon socket ainsi :

Code:

sin.sin_addr.s_addr = htonl (INADDR_ANY);
// OU
sin.sin_addr.s_addr = inet_addr "XX.XXX.XXX.XXX");

Mais dans votre code, vous semblez faire :

Code:
memcpy (&sockname.sin_addr.s_addr , host_address->h_addr, host_address->h_length);


Pouvez-vous m'expliquer un petit peu s'il vous plait ?


C'est parce que l'adresse provient d'une récupération par gethostbyname(). On fait une copie directe mais comme les types ne sont pas les mêmes, on force un peu le compilateur à coup de memcpy(). Cette manip est réputée fiable et portable...

est ce que pour remplir HOST et FILENAME, il faut toujours couper l'adresse au premier / ou est-ce qu'il y a des exceptions ?

Je ne sais pas répondre aux questions concernant HTTP, mais sur le plan du langage C c'est identique.

On aurait très bien pu faire ceci :
Code:

envoyer (socket_id,
"GET " FILENAME " HTTP/1.1\r\n"
"Host: " HOST "\r\n\r\n");
vu que ce sont des chaines littérales. Évidemment, avec des variables, ça ne fonctionne plus.

Par contre, j'ai pas compris pourquoi tu as appelé setsockopt ().

Pour setsockopt() je te renvoi vers la norme POSIX.1 :

http://www.opengroup.org/onlinepubs/000095399/functions/setsockopt.html


Dernière édition par -ed- le Lun 1 Sep 2014 - 2:37, édité 1 fois (Raison : typo dans le titre)

-ed-
Admin
Admin

Messages : 289
Date d'inscription : 26/05/2008
Age : 60
Localisation : Paris 6eme arrondissement (75, France)

Voir le profil de l'utilisateur http://bien-programmer.fr

Revenir en haut Aller en bas

Voir le sujet précédent Voir le sujet suivant Revenir en haut

- Sujets similaires

 
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum