Web Data Extraction

De Wikijoan
Dreceres ràpides: navegació, cerca

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(&reg, pattern, REG_EXTENDED | REG_NOSUB))
        return;
    if (!(d = opendir(dir)))
        return;
    while (entry = readdir(d))
        if (!regexec(&reg, 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

Eines de l'usuari
Espais de noms
Variants
Accions
Navegació
IES Jaume Balmes
Màquines recreatives
CNC
Informàtica musical
joanillo.org Planet
Eines