Web Data Extraction
Contingut
Introducció
Es tracta de fer servir i desenvolupar eines i tècniques per a l'extracció de contingut web, per bolcar-lo a fitxers o a bases de dades. També s'hauria de mirar tota la part de ETL
La primera aplicació pràctica és per fer una base de dades de MAME ROMs.
DEiXTo
He començat a fer servir l'aplicació. El front-end gràfic de només està en Windows, tot i que s'està treballant a portar-lo a GTK. El motor està ja en Linux. El motor necessita un fitxer de projecte (.WPF) fet amb la interfície gràfica. La interfície gràfica costa una mica d'entendre a primera vista fins que li pilles el truco (hi ha un manual).
utilitat wget
Amb wget i bash puc fer fàcilment un script per descarregar el contingut (codi font) d'una URL. wget té bastantes opcions. Més tard puc parsejar/filtrar el contingut per quedar-me amb el que vull, i ficar-ho en un fitxer o una base de dades.
#!/bin/bash #normalment no posaria cometes dobles en la URL, però les poso degut al caràcter especial & wget -b -O lletra0-9.txt "http://www.rom-world.com/dl.php?name=MAME&letter=0-9" sleep 2 wget -b -O lletraA.txt "http://www.rom-world.com/dl.php?name=MAME&letter=A" sleep 2 wget -b -O lletraB.txt "http://www.rom-world.com/dl.php?name=MAME&letter=B" sleep 2 ...
Dins dels fitxers resultants puc veure el contingut que m'interessa. Per exemple,
<tr><td class=b><img src=3s.png></td><td class=a><a href=/file.php?id=31893>Welltris (Japan, 2 players)</a> 1.67 MB</td></tr> <tr><td align=right><img src=4s.png></td><td><a href=/file.php?id=31894>West Story</a> 1.04 MB</td></tr> <tr><td class=b><img src=23s.png></td><td class=a><a href=/file.php?id=31895>Western Express (bootleg set 1)</a> 62.33 KB</td></tr> <tr><td align=right></td><td><a href=/file.php?id=31896>Western Express (bootleg set 2)</a> 62.32 KB</td></tr>
wget des de C amb una crida de system()
//gcc capture_romworld.c -o capture_romworld #include <stdlib.h> int main() { system("wget -b -O 24637.txt -q http://www.rom-world.com/file.php?id=24637"); return 0; }
Ara només cal recórrer un fitxer on estan tots els index i obtenir tots els fitxers de sortida, que serien processats a posteriori.
Utilitzar el full de càlcul CALC
Igual que fan els alumnes amb la pràctica dels municipis, es pot utilitzar CALC per formatar una llista tabulada (o amb diferents origens) i formatar-la per tal d'obtenir una llista d'INSEERTS per a ser inserits a una base de dades. Per exemple, tinc una llista de 6500 ROMS que he obtingut amb Deixto de http://www.rom-world.com/dl.php?name=MAME:
... 24656 1943 - The Battle of Midway (US) 30598 1943 Kai - Midway Kaisen 24633 1944: The Loop Master (Japan 000620) 24626 1944: The Loop Master (US 000620) 35416 1945k III 24632 19XX: The War Against Destiny (Asia 951207) ...
Vull ficar en la taula ROMWORLD aquesta informació (i que després hauré de buscar el nom del zip/rom corresponent)
En el CALC:
=CONCATENA("INSERT INTO ROMWORLD (id_romworld,romname) VALUES('";A1;"','";SUBSTITUEIX(B1;"'";"''");"');")
i així obtinc el script
... INSERT INTO ROMWORLD (id_romworld,romname) VALUES('24637','88 Games'); INSERT INTO ROMWORLD (id_romworld,romname) VALUES('24634','99: The Last War'); ...
que puc ficar dins la BD:
$ mysql -u root -p mamearcade < /home/aaa/MAME/captura/script_romworld.sql mysql> SELECT COUNT(*) FROM ROMWORLD; +----------+ | COUNT(*) | +----------+ | 6589 | +----------+
Cercar informació dins d'una web
L'exemple mínim és el que aquí es mostra. Es tracta de cercar el nom d'una rom dins el fitxer 21536.txt. Com veiem per cercar la rom sonicwi3.zip he de detectar la strin File Name:.
... <td bgcolor="#636363" colspan="2"><font size=3><B> File Information</B></font></td></tr> <tr><td bgcolor="#000000" width="105"><font size=2><b>File Name:</b></font></td><td bgcolor="#000000"><font size=2>sonicwi3.zip (<a href="http://server2.xgd.com/zip/unzip.php?id=21536">view contents</a>)</font></td></tr> ...
Amb C, la manera mínima de fer-ho seria:
llegir_romworld_v1.c
//gcc llegir_romworld_v1.c -o llegir_romworld_v1 //http://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C/Manejo_de_archivos #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { FILE *arxiu; char caracters[200]; char *cadfind="File Name:</b></font></td><td bgcolor=\"#000000\"><font size=2>"; //fixar-se amb el tractament de les cometes dobles char *cad1; char *cad2; char *nomrom; arxiu = fopen("21536.txt","r"); if (arxiu == NULL) exit(1); printf("\nEl contingut de l'arxiu de prova és \n\n"); while (feof(arxiu) == 0) { fgets(caracters,200,arxiu); //printf("%s",caracters); cad1 = strstr(caracters,cadfind); if (cad1!=NULL) { cad2 = strstr(cad1,".zip"); nomrom = (char*)strndup(cad1+strlen(cadfind),strlen(cad1)-strlen(cad2)-strlen(cadfind)); printf("%s\n",nomrom); } } fclose(arxiu); return 0; }
descàrrega de fitxers dins d'una web (amb wget)
El codi capture_romworld_zips.c és un exemple de com es pot descarregar arxius d'una web. En aquest cas amb l'inconvenient de què els links que enllacen els fitxers canvien cada x hores. Per tant, en aquest cas a l'hora que es descarrega la informació de quin és el link s'ha de descarregar el fitxer immediatament, no es pot fer a posteriori. La idea és la mateixa que s'ha comentat, utilitzant wget.
Llenguatge C i MySQL
Les proves estan a:
Approximate string matching. Levenshtein Distance Algorithm.
keywords: distance between strings, Levenshtein distance, approximate string matching
Levenshtein Distance Algorithm: C Implementation
by Lorenzo Seidenari (sixmoney@virgilio.it)
/*****************************************************/ /*Function prototypes and libraries needed to compile*/ /*****************************************************/ #include <stdlib.h> #include <malloc.h> #include <string.h> int levenshtein_distance(char *s,char*t); int minimum(int a,int b,int c); /****************************************/ /*Implementation of Levenshtein distance*/ /****************************************/ int levenshtein_distance(char *s,char*t) /*Compute levenshtein distance between s and t*/ { //Step 1 int k,i,j,n,m,cost,*d,distance; n=strlen(s); m=strlen(t); if(n!=0&&m!=0) { d=malloc((sizeof(int))*(m+1)*(n+1)); m++; n++; //Step 2 for(k=0;k<n;k++) d[k]=k; for(k=0;k<m;k++) d[k*n]=k; //Step 3 and 4 for(i=1;i<n;i++) for(j=1;j<m;j++) { //Step 5 if(s[i-1]==t[j-1]) cost=0; else cost=1; //Step 6 d[j*n+i]=minimum(d[(j-1)*n+i]+1,d[j*n+i-1]+1,d[(j-1)*n+i-1]+cost); } distance=d[n*m-1]; free(d); return distance; } else return -1; //a negative return value means that one or both strings are empty. } int minimum(int a,int b,int c) /*Gets the minimum of three values*/ { int min=a; if(b<min) min=b; if(c<min) min=c; return min; }
I ara es tracta de modificar aquest codi i adaptar-lo a la meva causística. Tinc dos strings, i he de decidir si són iguals o no (entenent que perquè siguin iguals no vol dir que siguin exactament iguals, igual que passava amb les cases rurals).
//gcc levenshtein.c -o levenshtein /*****************************************************/ /*Function prototypes and libraries needed to compile*/ /*****************************************************/ #include <stdlib.h> #include <stdio.h> #include <malloc.h> #include <string.h> int levenshtein_distance(char *s,char*t); int minimum(int a, int b, int c); double minimum_double(double a, double b); /****************************************/ /*Implementation of Levenshtein distance*/ /****************************************/ int levenshtein_distance(char *s,char*t) /*Compute levenshtein distance between s and t*/ { //Step 1 int k,i,j,n,m,cost,*d,distance; n=strlen(s); m=strlen(t); if(n!=0&&m!=0) { d=malloc((sizeof(int))*(m+1)*(n+1)); m++; n++; //Step 2 for(k=0;k<n;k++) d[k]=k; for(k=0;k<m;k++) d[k*n]=k; //Step 3 and 4 for(i=1;i<n;i++) for(j=1;j<m;j++) { //Step 5 if(s[i-1]==t[j-1]) cost=0; else cost=1; //Step 6 d[j*n+i]=minimum(d[(j-1)*n+i]+1,d[j*n+i-1]+1,d[(j-1)*n+i-1]+cost); } distance=d[n*m-1]; free(d); return distance; } else return -1; //a negative return value means that one or both strings are empty. } int minimum(int a,int b,int c) /*Gets the minimum of three values*/ { int min=a; if(b<min) min=b; if(c<min) min=c; return min; } double minimum_double(double a, double b) // mínim de dos valors decimals { double min=a; if(b<min) min=b; return min; } int matching(char *s,char *t) { //ja sé calcular el valor de levenshtein, i sé què significa. I ara he de buscar un compromís de similitud entre dos strings que em digui //si són iguals o no. Això dependrà del número de caràcters dels strings. int n, m, o; int dist; double coef; double tall = 0.071428571; //1/14 char *mes_curta; n=strlen(s); m=strlen(t); if (n < m) { mes_curta = s; o = n; } else { mes_curta = t; o = m; } dist = levenshtein_distance(s,t); printf("dist: %d\n", dist); //el primer que s'haurà de fer és fer trim, passar a min o maj, i eliminar els accents i caràcters especials. //primer criteri: si la longitud de les cadenes és diferent en més del 85.7% (una de 6 car, l'altra de 7 car, per ex), matching = 0 coef = minimum_double ((double) n/(double) m,(double) m/(double) n); if (coef < 0.857) return 0; //segon criteri: relació entre la distància i el número de caràcters. Si dist/strlen > 1/14, matchin = 0 printf("%f\n",(double)dist/(double)o); printf("%f\n",tall); if ((double)dist/(double)o > tall) return 0; return 1; } int main() { char *string1; char *string2; int iguals; string1 = "hola joan que tal"; string2 = "apa adeu siau"; string1 = "la mare de deu"; string2 = "la mare de neu"; iguals = matching(string1,string2); printf("matching (0/1): %d\n", iguals); return 1; }
Llistar fitxers d'un directori no-recursivament
no-recursivament: és a dir, sense ficar-se dins dels subdirectoris
En aquest enllaç explica com fer-ho amb molts llenguatges de programació.
//gcc walker.c -o walker //http://rosettacode.org/wiki/Walk_a_directory/Non-recursively #include <sys/types.h> #include <dirent.h> #include <regex.h> #include <stdio.h> void walker(const char *dir, const char *pattern) { struct dirent *entry; regex_t reg; DIR *d; if (regcomp(®, pattern, REG_EXTENDED | REG_NOSUB)) return; if (!(d = opendir(dir))) return; while (entry = readdir(d)) if (!regexec(®, entry->d_name, 0, NULL, 0)) puts(entry->d_name); closedir(d); } int main() { //walker(".", ".\\.c$"); //fitxers de C (*.c) //walker(".", ".\\.c$"); walker("/home/joan/actes_011009", ""); return 0; }
creat per Joan Quintana Compte, juny 2011