Como una visión general, se tendrá que realizar cuatro tareas principales:
- a presentar la solicitud (s) en el sitio web,
- para recuperar la respuesta (s) del sitio
- para analizar estas respuestas
- que tienen cierta lógica para iterar en las tareas anteriores, con los parámetros asociados a la navegación (a páginas "siguiente" en la lista de resultados)
El manejo de solicitud y respuesta http se realiza con métodos y clases de la biblioteca estándar de Python urllib y urllib2. El análisis de las páginas HTML se puede hacer con HTMLParser o con otros módulos tales como Beautiful Soup
el siguiente fragmento de la biblioteca estándar de Python demuestra la solicitud y recepción de una búsqueda en el sitio indicado en la pregunta.Este sitio es impulsado por ASP y, como resultado, debemos asegurarnos de enviar varios campos de formulario, algunos de ellos con valores "horribles", ya que la lógica de ASP los utiliza para mantener el estado y para autenticar la solicitud hasta cierto punto. De hecho, sometiendo. Las solicitudes deben enviarse con el http POST método ya que esto es lo que se espera de esta aplicación ASP. La dificultad principal es identificar el campo de formulario y los valores asociados que ASP espera (obtener páginas con Python es la parte fácil).
Este código es funcional, o más precisamente, era funcional, hasta que eliminé la mayor parte del valor de VSTATE, y posiblemente introduje un error o dos agregando comentarios.
import urllib
import urllib2
uri = 'http://legistar.council.nyc.gov/Legislation.aspx'
#the http headers are useful to simulate a particular browser (some sites deny
#access to non-browsers (bots, etc.)
#also needed to pass the content type.
headers = {
'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13',
'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml; q=0.9,*/*; q=0.8',
'Content-Type': 'application/x-www-form-urlencoded'
}
# we group the form fields and their values in a list (any
# iterable, actually) of name-value tuples. This helps
# with clarity and also makes it easy to later encoding of them.
formFields = (
# the viewstate is actualy 800+ characters in length! I truncated it
# for this sample code. It can be lifted from the first page
# obtained from the site. It may be ok to hardcode this value, or
# it may have to be refreshed each time/each day, by essentially
# running an extra page request and parse, for this specific value.
(r'__VSTATE', r'7TzretNIlrZiKb7EOB3AQE ... ...2qd6g5xD8CGXm5EftXtNPt+H8B'),
# following are more of these ASP form fields
(r'__VIEWSTATE', r''),
(r'__EVENTVALIDATION', r'/wEWDwL+raDpAgKnpt8nAs3q+pQOAs3q/pQOAs3qgpUOAs3qhpUOAoPE36ANAve684YCAoOs79EIAoOs89EIAoOs99EIAoOs39EIAoOs49EIAoOs09EIAoSs99EI6IQ74SEV9n4XbtWm1rEbB6Ic3/M='),
(r'ctl00_RadScriptManager1_HiddenField', ''),
(r'ctl00_tabTop_ClientState', ''),
(r'ctl00_ContentPlaceHolder1_menuMain_ClientState', ''),
(r'ctl00_ContentPlaceHolder1_gridMain_ClientState', ''),
#but then we come to fields of interest: the search
#criteria the collections to search from etc.
# Check boxes
(r'ctl00$ContentPlaceHolder1$chkOptions$0', 'on'), # file number
(r'ctl00$ContentPlaceHolder1$chkOptions$1', 'on'), # Legislative text
(r'ctl00$ContentPlaceHolder1$chkOptions$2', 'on'), # attachement
# etc. (not all listed)
(r'ctl00$ContentPlaceHolder1$txtSearch', 'york'), # Search text
(r'ctl00$ContentPlaceHolder1$lstYears', 'All Years'), # Years to include
(r'ctl00$ContentPlaceHolder1$lstTypeBasic', 'All Types'), #types to include
(r'ctl00$ContentPlaceHolder1$btnSearch', 'Search Legislation') # Search button itself
)
# these have to be encoded
encodedFields = urllib.urlencode(formFields)
req = urllib2.Request(uri, encodedFields, headers)
f= urllib2.urlopen(req) #that's the actual call to the http site.
# *** here would normally be the in-memory parsing of f
# contents, but instead I store this to file
# this is useful during design, allowing to have a
# sample of what is to be parsed in a text editor, for analysis.
try:
fout = open('tmp.htm', 'w')
except:
print('Could not open output file\n')
fout.writelines(f.readlines())
fout.close()
Eso es todo para obtener la página inicial. Como se dijo anteriormente, entonces uno necesitaría analizar la página, es decir, encontrar las partes de interés y reunirlas según corresponda, y almacenarlas en el archivo/base de datos/donde sea. Este trabajo se puede hacer de muchas maneras: usando analizadores html, o tipo de tecnología XSLT (de hecho después de analizar el html a xml), o incluso para trabajos crudos, expresión regular simple. Además, uno de los elementos que normalmente se extrae es la "siguiente información", es decir, un tipo de enlace, que se puede utilizar en una nueva solicitud al servidor para obtener páginas subsiguientes.
Esto debería darle un sabor aproximado de lo que es el raspado html de "mano larga". Hay muchos otros enfoques para esto, como las utilidades dedicadas, los scripts en el complemento GreaseMonkey de Mozilla (FireFox), XSLT ...
Si estoy usando Google Chrome, ¿cómo debería reemplazar el valor de 'HTTP_USER_AGENT'? Lo siento si esta pregunta es tonta ya que no hice muchas cosas en la web. ¡Gracias! – taocp
@taocp, una manera fácil de saber qué cadena 'HTTP_USER_AGENT' usar para un navegador determinado es visitar http://www.all-nettools.com/toolbox/environmental-variables-test.php esta página le mostrará el valores de encabezado enviados por el navegador, busque "HTTP_USER_AGENT". La cadena real depende del sistema operativo y de la versión y compilación específica de Chrome, pero debería verse como 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, como Gecko) Chrome/29.0.1547.66 Safari/537.36' – mjv
muchas gracias por su respuesta. Probé tu código con los valores correctos establecidos en mi navegador Chrome. El resultado del archivo tmp.htm dice "no se encontraron resultados", mientras que cuando coloco "york" en el sitio web, se devuelve mucho. ¿Sabes por qué? – taocp