2014-10-17 22 views
5

Więc napisałem program do skrobania witryny sieci Web w języku C# przy użyciu pakietu Agility HTML. To było dość proste. Nawet biorąc pod uwagę niespójności w formatowaniu na stronie internetowej, zajęło mi to tylko kilka godzin, aby pracować.Jak zeskrobać stronę internetową za pomocą C?

Teraz muszę ponownie wdrożyć ten program w C, aby można go było uruchomić w środowisku Linux. To poważny koszmar.

Jestem w stanie cofnąć stronę, ale jeśli chodzi o śledzenie jej w celu wyciągnięcia części, które mnie interesują - rysuję dużo pustych miejsc. Początkowo nie byłem nastawiony na próbę wdrożenia rozwiązania podobnego do mojej opcji HTML Agility w języku C#, z wyjątkiem używania Tidy i innej biblioteki XML, aby zachować logikę mniej więcej taką samą.

To nie wyszło tak dobrze. Biblioteka XML, do której mam dostęp, wydaje się nie obsługiwać xpath i nie mogę zainstalować takiej, która działa. Tak więc uciekam się do próby znalezienia sposobu na odczytanie strony za pomocą dopasowywania ciągów znaków w celu znalezienia żądanych danych. Nie mogę oprzeć się wrażeniu, że musi istnieć lepszy sposób na zrobienie tego.

Oto co mam:

#define HTML_PAGE "codes.html" 

int extract() 
{ 

    FILE *html; 

    int found = 0; 
    char buffer[1000]; 
    char searchFor[80], *cp; 

    html = fopen(HTML_PAGE, "r"); 

    if (html) 
    { 

     // this is too error prone, if the buffer cuts off half way through a section of the string we are looking for, it will fail! 
     while(fgets(buffer, 999, html)) 
     { 
      trim(buffer); 

      if (!found) 
      { 
       sprintf(searchFor, "<strong>"); 
       cp = (char *)strstr(buffer, searchFor); 
       if(!cp)continue; 

       if (strncmp(cp + strlen(searchFor), "CO1", 3) == 0 || strncmp(cp + strlen(searchFor), "CO2", 3) == 0) 
       { 
        got_code(cp + strlen(searchFor)); 
       } 
      } 
     } 
    } 

    fclose(html); 

    return 0; 
} 

got_code(html) 
    char *html; 
{ 
    char code[8]; 
    char *endTag; 
    struct _code_st *currCode; 
    int i; 

    endTag = (char *)strstr(html, "</strong>"); 
    if(!endTag)return; 

    sprintf(code, "%.7s", html); 

    for(i=0 ; i<Data.Codes ; i++) 
     if(strcasecmp(Data.Code[i].Code, code)==0) 
      return; 

    ADD_TO_LIST(currCode, _code_st, Data.Code, Data.Codes); 
    currCode->Code = (char *)strdup(code); 

    printf("Code: %s\n", code); 
} 

Powyższe nie działać prawidłowo. Otrzymuję wiele kodów, które mnie interesują, ale jak wspomnę powyżej, jeśli bufor odcina się w niewłaściwych miejscach, tęsknię za niektórymi.

Próbowałem po prostu przeczytać cały fragment html, który mnie interesuje, ale nie byłem w stanie wymyślić, jak przejść przez to - nie mogłem uzyskać żadnych wyświetlanych kodów.

Czy ktoś wie, jak rozwiązać ten problem?

EDIT: Myślałem o tym więcej. Czy istnieje sposób, w jaki mogę patrzeć do przodu w pliku i szukać końca każdego "bloku" tekstu, który piszę i ustawić rozmiar bufora przed odczytaniem? Czy potrzebowałbym innego wskaźnika pliku do tego samego pliku? To (miejmy nadzieję) zapobiegnie problemowi odcięcia bufora w niewygodnych miejscach.

+0

Może uruchomić go z Mono? –

+5

C jest świetny, ale nie dla tego rodzaju zadania. Zamiast tego użyj czegoś innego, jak Perl lub Python. Do diabła, nawet PHP by to zrobił. –

+0

Tak, muszę się zgodzić. To po prostu jest niewłaściwe narzędzie do pracy. –

Odpowiedz

5

Dobra, więc po wielu uderzeniach głową o ścianę, próbując wymyślić sposób, aby mój powyższy kod zadziałał, postanowiłem spróbować nieco innego podejścia.

Ponieważ wiedziałem, że dane na stronie, którą skrobię, są zawarte w jednej ogromnej linii, zmieniłem kod, aby przeszukać plik, dopóki go nie znajdzie. Potem idę w dół, szukając bloków, które chciałem. To zadziałało zaskakująco dobrze i kiedy miałem kod czytający niektóre bloki, łatwo było wprowadzić drobne modyfikacje, aby wyjaśnić niespójności w kodzie HTML. Najdłużej trwało zastanawianie się, jak wyskoczyć z gry, gdy dotarłem do końca linii i rozwiązałem to, wyprzedzając, aby upewnić się, że jest jeszcze jeden blok do przeczytania.

Tu jest mój kodu (co jest brzydkie, ale funkcjonalny):

#define HTML_PAGE "codes.html" 
#define START_BLOCK "<strong>" 
#define END_BLOCK "</strong>" 

int extract() 
{ 

    FILE *html; 

    int found = 0; 
    char *line = NULL, *endTag, *startTag; 
    size_t len = 0; 
    ssize_t read; 

    char searchFor[80]; 

    html = fopen(HTML_PAGE, "r"); 

    if (html) 
    { 
     while((read = getline(&line, &len, html)) != -1) 
     { 
      if (found) // found line with codes we are interested in 
      { 
       char *ptr = line; 
       size_t nlen = strlen (END_BLOCK); 

       while (ptr != NULL) 
       { 
        sprintf(searchFor, START_BLOCK); 
        startTag = (char *)strstr(ptr, searchFor); 
        if(!startTag) 
        { 
         nlen = strlen (START_BLOCK); 
         ptr += nlen; 
         continue; 
        } 

        if (strncmp(startTag + strlen(searchFor), "CO1", 3) == 0 || strncmp(startTag + strlen(searchFor), "CO2", 3) == 0) 
         got_code(startTag + strlen(searchFor), code); 
        else { 
         nlen = strlen (START_BLOCK); 
         ptr += nlen; 
         continue; 
        } 

        sprintf(searchFor, END_BLOCK); 
        ptr = (char *)strstr(ptr, searchFor); 

        if (!ptr) { found = 0; break; } 

        nlen = strlen (END_BLOCK);     
        ptr += nlen; 

        if (ptr) 
        { 
         // look ahead to make sure we have more to pull out 
         sprintf(searchFor, END_BLOCK); 
         endTag = (char *)strstr(ptr, searchFor); 
         if (!endTag) { break; } 
        } 
       } 

       found = 0; 
       break; 
      } 

      // find the section of the downloaded page we care about 
      // the next line we read will be a blob containing the html we want 
      if (strstr(line, "wiki-content") != NULL) 
      { 
       found = 1; 
      } 
     } 

     fclose(html); 
    } 

    return 0; 
} 

got_code(char *html) 
{ 
    char code[8]; 
    char *endTag; 
    struct _code_st *currCode; 
    int i; 

    endTag = (char *)strstr(html, "</strong>"); 
    if(!endTag)return; 

    sprintf(code, "%.7s", html); 

    for(i=0 ; i<Data.Codes ; i++) 
     if(strcasecmp(Data.Code[i].Code, code)==0) 
      return; 

    ADD_TO_LIST(currCode, _code_st, Data.Code, Data.Codes); 
    currCode->Code = (char *)strdup(code); 

    printf("Code: %s\n", code); 
} 

Nie tak elegancki i solidny jako mojego C# programu, ale przynajmniej to cofa wszystkie informacje chcę.

Powiązane problemy