[ Foro de Python ]

Web Crawler - No puedo insertar datos

14-Jun-2015 07:03
Invitado (Lizsags)
3 Respuestas

Estoy utilizando GNU/LINUX:  Xubuntu 14.04
Python 2.7.9
MySQL 5.5.43

E creado un Script en python el cual es un crawler y su función debe ser buscar e indexar todas las url's de una dirección semilla. También este script es capaz de crear tablas en una BD previamente creada. Lo que hasta hoy día no e podido es realizar la inserción de los datos que obtengo de la url semilla y que estas sean guardadas en sus respectivas tablas dentro de la BD creada tengo todo correcto según yo espero puedan ayudarme les dejo el codigo:


# -*- coding: utf-8 *-*
# lizsags S.R.L de C.V
# sags@Lizsags.com.mx
# 2 de Mayo, 2015

import urllib, re, os, MySQLdb, string, urlparse, htmllib, time


class dividir_texto:
    # Esta clase es para dividir el texto en una lista de palabras "limpias" de menos de 15 caracteres
    # de longitud lo cual es adecuado para la inclusion en una tabla de base de datos en una 
    # busqueda de texto completo
    def __init__(self, text):
        self.__text = text
        self.__stop_word_list = ("all", "an", "and", "any", "as", "at", "be", "for", "from", "if", "in", "is", "it", "may", "name", "no", "not", "of", "on", "or", "telstra", "that", "the", "these", "this", "to", "use", "when", "with", "which")


    def get_split_text (self):
        reo = re.compile(r"""
		(\w+)	# Incluyendo caracteres alfanumericos
		""", re.VERBOSE|re.IGNORECASE)
	word_list = reo.findall(self.__text)
	result_list = []
	for word in word_list:
		if word not in result_list and len(word) > 1 and len(word) < 15 and word not in self.__stop_word_list:
			result_list.append(word)
	return result_list


class mi_analizador(htmllib.HTMLParser):
	# Esta clase la utilizo para analizar el titulo y el texto que esta fuera de las etiquietas
	# <html> para una explicacion mas amplia sobre el funcionamiento consultar la documentacion
	# de Python para el modulo htmllib

    def __init__(self):
        import formatter
        self.formatter = formatter.NullFormatter()
    	htmllib.HTMLParser.__init__(self, self.formatter)
    	self.extracted_text = ""
    	self.title = ""
    	
        
    def handle_starttag(self, tag, method, attrs):	
        if (tag == "title"):
            	self.flag_collect_title = 1

        
    def handle_endtag(self, tag, method):	
        if (tag == "title"):
            	self.flag_collect_title = 0

        
    def start_title(self, attrs):
        print "en el inicio del titulo"
        self.flag_collect_title = 1    
        


    def end_title(self, attrs):
        print "en el final del titulo"
        self.flag_collect_title = 0     
        

    def handle_data(self, data):
    	# Algunos datos "basura" se consiguen, incluso cuando usamos la condicion
    	# if len(data)> 10. los Strings de lineas nuevas puede ser y no parece afectar
    	# el rendimiento. 
        if len(data) > 6:
	     if self.flag_collect_title:
	         self.title = self.title + data
	     else:
	     	self.extracted_text = self.extracted_text + " " + data 
	     

    def get_text(self):
        result = self.extracted_text
        self.extracted_text = ""
	return result


    def get_title(self):
        result = self.title
        self.title = ""
	return result

# ========================================================
#	Funciones de ayuda para el sistema principal
# ========================================================
def create_WORDS_table():
    conn = MySQLdb.connect("localhost","root","sags","lizsags_db");
    cursor = conn.cursor()
    # Interesa solo guardar palabras pequenas, lo mismo limito el tamano del campo word a 15
    sql = """CREATE TABLE WORDS (
	    id 		INT AUTO_INCREMENT PRIMARY KEY NOT NULL,
	    word	VARCHAR(15) NOT NULL,
	    INDEX	(word)
    );"""

    result = cursor.execute(sql)
    conn.close()  
        
def create_WORDS_X_URL_table():
    conn = MySQLdb.connect("localhost","root","sags","lizsags_db");
    cursor = conn.cursor()

    sql = """CREATE TABLE WORDS_X_URL (
	    word_id 	INT NOT NULL, 
	    url_id	INT NOT NULL,
	    INDEX	(word_id),
	    INDEX	(url_id)
    );"""

    result = cursor.execute(sql)
    conn.close()      
    
def create_URLS_table():
    conn = MySQLdb.connect("localhost","root","sags","lizsags_db");
    cursor = conn.cursor()

    sql = """CREATE TABLE URLS (
	    id 		INT AUTO_INCREMENT PRIMARY KEY NOT NULL,
	    url		VARCHAR(255) NOT NULL,
	    title	VARCHAR(255),
	    INDEX	(url)
    );"""

    result = cursor.execute(sql)
    conn.close() 
    
def get_urls_list():
	# Esta funcion regresa una lista de direcciones url calificadas para todas las paginas que queremos indexar
	# La direccion a la cual estaremos analizando se encuentra en el os.path.join alojado en el objeto url_list_path
    result_list = []
    url_list_path = os.path.join("http://www.taringa.net/")
    url_object = urllib.urlopen(url_list_path)
    lines = url_object.readlines()
    reo = re.compile(r"""
		    \"
		    (?P<url>
		    (\S*?)
		    \.html)
		    """, re.VERBOSE|re.IGNORECASE|re.DOTALL)    
    line_count = 0
    for line in lines:
        line_count = line_count + 1
        # saltamos las primeras 10 lineas como estas incluyen <href> que no queremos
        if line_count > 10:
           result = reo.search(line)
           if result:
                url = result.group('url')
                full_url = urlparse.urljoin(url_list_path, url)
                result_list.append(full_url)
    return result_list   
    
def create_FTS_index(lista_url):
    try:
    	create_WORDS_table()
    except Exception:
        print "\nOcurrio un inconveniente mientras se creaba la tabla WORDS. Probablemente ya exista la tabla."
    try:
	create_URLS_table()
    except Exception:
        print "\nOcurrio un inconveniente mientras se creaba la tabla URLS. Probablemente ya exista la tabla."	
    try:
        create_WORDS_X_URL_table()
    except Exception:
        print "\nOcurrio un inconveniente mientras se creaba la tabla WORDS_X_URL. Probablemente ya exista la tabla.\n\n\n"
    
		    
		    
    conn = MySQLdb.connect("localhost","root","sags","lizsags_db")
    cursor = conn.cursor()
    known_words_list = []
    known_words_dict = {}

    for url in lista_url:
        
        print "procesando la url: ", url


        try:
		url_object = urllib.urlopen(url)
		text = url_object.read()
        	p = mi_analizador()
       		p.feed(text)
       		p.close()
    		parsed_text = p.get_text()
    		title = p.get_title()
    		if title == "":
    			title = "Sin titulo"
    		# Remplazar la comilla simple con la entidad html correspondiente
    		title = string.replace(title, "href", "'")
    		print title
    	except:
                print "No se puede abrir la siguiente Url: %s" % url
                continue

        # Crear entradas para la url en la tabla URLS, pero antes se debera anadir las diagonales
        # antes de cada \
        escaped_url = string.replace(url, "\\", "\\\\")
        sql = """INSERT INTO URLS (url, title) VALUES ('"""
        sql = sql + escaped_url + """' , '""" + title + """');"""
        cursor.execute(sql)                
                
	ts = dividir_texto(parsed_text)
        word_list = ts.get_split_text()
        for word in word_list:
        	# Convierte todas las palabras minusculas, asi tendremos casos de coincidencia insensibles
           word = string.lower(word)
           # Ignora palabras de mas de 14 caracteres

           if word not in known_words_list:
                known_words_list.append(word)
                if not known_words_dict.has_key(word):
                    known_words_dict[word] = 0
            
            # Asumimos que word no esta en la tabla WORDS (esto supone que estamos insertando en la tabla
            # desde cero). Asi que anadimos a la tabla
	        sql = """INSERT INTO WORDS (word) VALUES ('"""
	        sql = sql + word + """');"""
	        cursor.execute(sql)

	        # como aun no tenemos una entrada para word dentro de la tabla WORDS, esto no es una entrada
	        # para la combinacion word/url en la tabla WORDS_X_URL y la anadimos
	        sql = """SELECT id FROM WORDS WHERE word = '"""
	        sql = sql + word + """';"""
	        cursor.execute(sql)
	        result = cursor.fetchone()
	        word_id = result[0]

	        # Guardar nuestra cache aqui
	        known_words_dict[word] = word_id
	        
	        sql = """SELECT id FROM URLS WHERE url = '"""
	        sql = sql + escaped_url + """';"""
	        cursor.execute(sql)
	        result = cursor.fetchone()
	        url_id = result[0]
	        

	                
	        sql = """INSERT INTO WORDS_X_URL VALUES ("""
	        sql = sql + "%s" % (word_id)
	        sql = sql + """, """
	        sql = sql + "%s" % (url_id)
	        sql = sql + """);"""
	        cursor.execute(sql)
           else:
           		# Este word ya esta en la tabla WORDS. Existen entradas para WORDS_X_URL para la
           		# combinacion word/url?
           		# Primero, mantenemos word_id
                if known_words_dict.has_key(word):
                    word_id = known_words_dict[word]
                else:  
                    print "Recuperando word_id con SQL"           
                    sql = """SELECT id FROM WORDS WHERE word = '"""
                    sql = sql + word + """';"""
                    cursor.execute(sql)
                    result = cursor.fetchone()
                    word_id = result[0]
                
                # Ahora sostenemos url_id
                sql = """SELECT id FROM URLS WHERE url = '"""
	        sql = sql + escaped_url + """';"""
	        cursor.execute(sql)
	        result = cursor.fetchone()
	        url_id = result[0]
	                    
                sql = """SELECT * FROM WORDS_X_URL WHERE word_id = """
                sql = sql + "%s" % (word_id)
                sql = sql + """ AND url_id = """
                sql = sql + "%s" % (url_id)
                cursor.execute(sql)
                result = cursor.fetchall()
                if len(result) == 0:
                	# No existian entradas, asi que las agregamos
   	            sql = """INSERT INTO WORDS_X_URL VALUES ("""
		    sql = sql + "%s" % (word_id)
		    sql = sql + """, """
		    sql = sql + "%s" % (url_id)
		    sql = sql + """);"""
		    cursor.execute(sql)
              
    conn.close()
    print "\nFinalizo la creacion del indexado FTS"

    
def main():
    inicio_de_tiempo = time.time()
    lista_url = get_urls_list()
    for url in lista_url:
        print url
    lista_temp = lista_url
    for url in lista_temp:
        print url
    num_a_procesar = len(lista_temp)
    create_FTS_index(lista_temp)
    print "\nFinalizo la creacion del indexado en Lizsags"
    tiempo_final = time.time() 
    tiempo_transcurrido = tiempo_final - inicio_de_tiempo
    print "\nTomo %d segundos indexar %d paginas. Tiempo promedio por pagina= %d segundos\n\n\n" % (tiempo_transcurrido, num_a_procesar, tiempo_transcurrido/num_a_procesar)

        
main()


 


21-Jun-2015 00:12
Nacho Cabanes (+84)

Ese código es exageradamente largo para una consulta. Intenta reducir el problema a un fragmento más pequeño (lo ideal sería que fuera el fragmento más pequeño posible que aún se comportase de forma errónea).

¿Por qué no creas un programa muy simple que cree la base de datos, la tabla e inserte datos prefijados en ella?


21-Jun-2015 19:50
Invitado (Lizsags)

Como podría hacer eso todo este codigo lo recopilé de cursos y libros en la web y los adapté según mis necesidades pero aun no logro que se pueda insertar los datos


27-Jun-2015 12:30
Nacho Cabanes (+84)

Insisto: extrae el fragmento más pequeño posible.

Lo malo de copiar y pegar fragmentos de Internet es que puedes llegar a no entender lo que estás haciendo, y puede haber incompatibilidades entre unos fragmentos y otros.

Por eso, si tu problema actual se resume a "no consigo insertar", busca (o crea, o extrae de ese programa) un fragmento de código que inserte datos en una base de datos, comprueba si funciona y soluciónalo en caso de que no sea así.

Cuando funcione, añades otro pequeño fragmento que le dé otra posibilidad adicional, y te aseguras de que funciona correctamente, y así sucesivamente.






(No se puede continuar esta discusión porque tiene más de dos meses de antigüedad. Si tienes dudas parecidas, abre un nuevo hilo.)