Cómo comprobar si el valor en un sitio web ha cambiado

Básicamente, estoy intentando ejecutar algo de código (Python 3.2) si cambia un valor en un sitio web, de lo contrario, espere un poco y verifíquelo más tarde.

Primero pensé que solo podía guardar el valor en una variable y compararlo con el nuevo valor que se recuperó la próxima vez que se ejecutara el script. Pero eso rápidamente se convirtió en un problema ya que el valor se sobrescribió cuando el script se ejecutaba de nuevo e inicializaba esa variable.

Entonces intenté simplemente guardar el html de la página web como un archivo y luego compararlo con el html que se llamaría la próxima vez que se ejecutara el script. Tampoco hubo suerte allí, ya que seguía apareciendo Falso, incluso cuando no había cambios.

Lo siguiente fue seleccionar la página web y luego tratar de compararla con el html. Curiosamente eso tampoco funcionó dentro del guión. PERO, si escribo file = pickle.load (abrir (‘D: \ Download \ htmlString.p’, ‘rb’)) después de que el script se haya ejecutado y luego file == html, se muestra True cuando no ha habido algún cambio.

Estoy un poco confundido en cuanto a por qué no funcionará cuando se ejecute el script, pero si hago lo anterior, muestra la respuesta correcta.

Edit: Gracias por las respuestas hasta ahora chicos. La pregunta que tengo no era realmente sobre otras formas de hacer esto (¡aunque siempre es bueno aprender más formas de realizar una tarea!) Sino por qué el código siguiente no funciona cuando se ejecuta como un script, pero si vuelva a cargar el objeto pickle en el indicador después de que se haya ejecutado el script y luego pruébelo con el html, devolverá True si no ha habido cambios.

try: file = pickle.load( open( 'D:\\Download\\htmlString.p', 'rb')) if pickle.load( open( 'D:\\Download\\htmlString.p', 'rb')) == htmlString: print("Values haven't changed!") sys.exit(0) else: pickle.dump( htmlString, open( 'D:\\Download\\htmlString.p', "wb" ) ) print('Saving') except: pickle.dump( htmlString, open( 'D:\\Download\\htmlString.p', "wb" ) ) print('ERROR') 

Edit : no me había dado cuenta de que solo buscabas el problema con tu script. Esto es lo que creo que es el problema, seguido de mi respuesta original que aborda otro enfoque para el problema más grande que está tratando de resolver.

Su guión es un gran ejemplo de los peligros de usar una manta, except statement: se captura todo. Incluyendo, en este caso, su sys.exit(0) .

Supongo que el bloque try está ahí para detectar el caso en el que D:\Download\htmlString.p aún no existe. Ese error se llama IOError , y puede detectarlo específicamente con la except IOError:

Aquí está su script más un poco de código antes de hacerlo funcionar, resuelto para su problema de except :

 import sys import pickle import urllib2 request = urllib2.Request('http://www.iana.org/domains/example/') response = urllib2.urlopen(request) # Make the request htmlString = response.read() try: file = pickle.load( open( 'D:\\Download\\htmlString.p', 'rb')) if file == htmlString: print("Values haven't changed!") sys.exit(0) else: pickle.dump( htmlString, open( 'D:\\Download\\htmlString.p', "wb" ) ) print('Saving') except IOError: pickle.dump( htmlString, open( 'D:\\Download\\htmlString.p', "wb" ) ) print('Created new file.') 

Como nota al margen, podría considerar usar os.path para sus rutas de archivo; más adelante, quien quiera usar su script en otra plataforma, le ayudará a evitar las feos dobles barras diagonales.

Edición 2 : Adaptado para su URL específica.

Hay un número generado dinámicamente para los anuncios en esa página que cambia con cada carga de página. Está cerca del final después de todo el contenido, por lo que solo podemos dividir la cadena HTML en ese punto y tomar la primera mitad, descartando la parte con el número dynamic.

 import sys import pickle import urllib2 request = urllib2.Request('http://ecal.forexpros.com/e_cal.php?duration=weekly') response = urllib2.urlopen(request) # Make the request # Grab everything before the dynabic double-click link htmlString = response.read().split('

Tu cadena ya no es un documento HTML válido si eso era importante. Si lo fuera, podrías eliminar esa línea o algo así. Probablemente haya una forma más elegante de hacer esto, tal vez eliminar el número con una expresión regular, pero al menos esto satisface su pregunta.

Respuesta original : un enfoque alternativo a su problema.

¿Cómo se ven los encabezados de respuesta desde el servidor web? HTTP especifica una propiedad de Last-Modified que podría usar para verificar si el contenido ha cambiado (suponiendo que el servidor diga la verdad). Use este con una solicitud HEAD como mostró Uku en su respuesta. Si desea conservar el ancho de banda y ser amable con el servidor que está sondeando.

Y también hay un encabezado If-Modified-Since que suena como lo que podría estar buscando.

Si los combinamos, podría llegar a algo como esto:

 import sys import os.path import urllib2 url = 'http://www.iana.org/domains/example/' saved_time_file = 'last time check.txt' request = urllib2.Request(url) if os.path.exists(saved_time_file): """ If we've previously stored a time, get it and add it to the request""" last_time = open(saved_time_file, 'r').read() request.add_header("If-Modified-Since", last_time) try: response = urllib2.urlopen(request) # Make the request except urllib2.HTTPError, err: if err.code == 304: print "Nothing new." sys.exit(0) raise # some other http error (like 404 not found etc); re-raise it. last_modified = response.info().get('Last-Modified', False) if last_modified: open(saved_time_file, 'w').write(last_modified) else: print("Server did not provide a last-modified property. Continuing...") """ Alternately, you could save the current time in HTTP-date format here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 This might work for some servers that don't provide Last-Modified, but do respect If-Modified-Since. """ """ You should get here if the server won't confirm the content is old. Hopefully, that means it's new. HTML should be in response.read(). """ 

También puedes ver esta publicación de blog de Stii que puede proporcionarte alguna inspiración. No sé lo suficiente sobre ETags para ponerlos en mi ejemplo, pero su código también los verifica.

Sería más eficiente hacer una solicitud HEAD y verificar la longitud del contenido del documento.

 import urllib2 """ read old length from file into variable """ request = urllib2.Request('http://www.yahoo.com') request.get_method = lambda : 'HEAD' response = urllib2.urlopen(request) new_length = response.info()["Content-Length"] if old_length != new_length: print "something has changed" 

Tenga en cuenta que es poco probable, aunque es posible que la longitud del contenido sea exactamente la misma, pero al mismo tiempo es la forma más eficiente. Este método puede ser adecuado o inadecuado según el tipo de cambios que espere.

Siempre puede indicar CUALQUIER cambio dentro de los datos entre el archivo almacenado local y el control remoto mediante la encoding del contenido de ambos. Esto se emplea comúnmente para verificar la veracidad de los datos descargados. Para una verificación continua, necesitará un bucle while.

 import hashlib import urllib num_checks = 20 last_check = 1 while last_check != num_checks: remote_data = urllib.urlopen('http://remoteurl').read() remote_hash = hashlib.md5(remote_data).hexdigest() local_data = open('localfilepath').read() local_hash = hashlib.md5(local_data).hexdigest() if remote_hash == local_hash: print 'right now, we match!' else: print 'right now, we are different' 

Si los datos reales no necesitan guardarse localmente, solo almacenaré el hash md5 y lo calcularé sobre la marcha al verificar.

No estaba completamente claro si deseaba o no simplemente ver si el sitio web ha cambiado o si iba a hacer más con los datos del sitio web. Si es el primero, definitivamente hash, como se mencionó anteriormente. Aquí hay un ejemplo de trabajo (python 2.6.1 en un mac) que compara el html antiguo completo con el html nuevo; debe ser fácil de modificar, por lo que utiliza hashes o solo una parte específica del sitio web, según sea necesario. Esperemos que los comentarios y documentación dejen todo claro.

 import urllib2 def getFilename(url): ''' Input: url Return: a (string) filename to be used later for storing the urls contents ''' return str(url).lstrip('http://').replace("/",":")+'.OLD' def getOld(url): ''' Input: url- a string containing a url Return: a string containing the old html, or None if there is no old file (checks if there already is a url.OLD file, and make an empty one if there isn't to handle the case that this is the first run) Note: the file created with the old html is the format url(with : for /).OLD ''' oldFilename = getFilename(url) oldHTML = "" try: oldHTMLfile = open(oldFilename,'r') except: # file doesn't exit! so make it with open(oldFilename,'w') as oldHTMLfile: oldHTMLfile.write("") return None else: oldHTML = oldHTMLfile.read() oldHTMLfile.close() return oldHTML class ConnectionError(Exception): def __init__(self, value): if type(value) != type(''): self.value = str(value) else: self.value = value def __str__(self): return 'ConnectionError: ' + self.value def htmlHasChanged(url): ''' Input: url- a string containing a url Return: a boolean stating whether the website at url has changed ''' try: fileRecvd = urllib2.urlopen(url).read() except: print 'Could not connect to %s, sorry!' % url #handle bad connection error... raise ConnectionError("urlopen() failed to open " + str(url)) else: oldHTML = getOld(url) if oldHTML == fileRecvd: hasChanged = False else: hasChanged = True # rewrite file with open(getFilename(url),'w') as f: f.write(fileRecvd) return hasChanged if __name__ == '__main__': # test it out with whatismyip.com try: print htmlHasChanged("http://automation.whatismyip.com/n09230945.asp") except ConnectionError,e: print e