Raspe varias páginas con BeautifulSoup y Python

Mi código raspa con éxito las tags tr align = center de [ http://my.gwu.edu/mod/pws/courses.cfm?campId=1&termId=201501&subjId=ACCY ] y escribe los elementos td en un archivo de texto.

Sin embargo, hay varias páginas disponibles en el sitio anterior en las que me gustaría poder raspar.

Por ejemplo, con la url anterior, cuando hago clic en el enlace a “página 2”, la url general NO cambia. Miré la fuente de la página y vi un código javascript para avanzar a la página siguiente.

¿Cómo se puede cambiar mi código para raspar los datos de todas las páginas disponibles disponibles?

Mi código que funciona solo para la página 1:

import bs4 import requests response = requests.get('http://my.gwu.edu/mod/pws/courses.cfm?campId=1&termId=201501&subjId=ACCY') soup = bs4.BeautifulSoup(response.text) soup.prettify() acct = open("/Users/it/Desktop/accounting.txt", "w") for tr in soup.find_all('tr', align='center'): stack = [] for td in tr.findAll('td'): stack.append(td.text.replace('\n', '').replace('\t', '').strip()) acct.write(", ".join(stack) + '\n') 

El truco aquí es verificar las solicitudes que entran y salen de la acción de cambio de página al hacer clic en el enlace para ver las otras páginas. La forma de verificar esto es usar la herramienta de inspección de Chrome (presionando F12 ) o instalando la extensión Firebug en Firefox. Usaré la herramienta de inspección de Chrome en esta respuesta. Vea a continuación para mi configuración.

introduzca la descripción de la imagen aquí

Ahora, lo que queremos ver es una solicitud GET a otra página o una solicitud POST que cambie la página. Mientras la herramienta está abierta, haga clic en un número de página. Por un momento realmente breve, solo aparecerá una solicitud, y es un método POST . Todos los demás elementos seguirán rápidamente y llenarán la página. Vea a continuación lo que estamos buscando.

introduzca la descripción de la imagen aquí

Haga clic en el método POST anterior. Debería aparecer una sub-ventana de clases que tiene tabs. Haga clic en la pestaña Headers . Esta página enumera los encabezados de solicitud, más o menos las cosas de identificación que el otro lado (el sitio, por ejemplo) necesita de usted para poder conectarse (alguien más puede explicar esto mucho mejor que yo).

Cada vez que la URL tiene variables como números de página, marcadores de ubicación o categorías, más a menudo que no, el sitio utiliza cadenas de consulta. La historia larga es corta, es similar a una consulta SQL (en realidad, es una consulta SQL, a veces) que permite que el sitio obtenga la información que necesita. Si este es el caso, puede verificar los encabezados de solicitud para los parámetros de cadena de consulta. Desplázate hacia abajo un poco y deberías encontrarlo.

introduzca la descripción de la imagen aquí

Como puede ver, los parámetros de la cadena de consulta coinciden con las variables en nuestra URL. Un poco más abajo, puede ver los Form Data con pageNum: 2 debajo. Esta es la clave.

POST solicitudes POST conocen más comúnmente como solicitudes de formularios porque son el tipo de solicitudes que se realizan al enviar formularios, iniciar sesión en sitios web, etc. Básicamente, casi cualquier cosa en la que tenga que enviar información. Lo que la mayoría de la gente no ve es que las solicitudes POST tienen una URL que siguen. Un buen ejemplo de esto es cuando inicias sesión en un sitio web y, muy brevemente, ves que la barra de tu dirección se transforma en una especie de URL /index.html antes de decidirse por /index.html o somesuch.

Lo que básicamente significa el párrafo anterior es que puede (pero no siempre) adjuntar los datos del formulario a su URL y llevará a cabo la solicitud POST en su ejecución. Para saber la cadena exacta que debe agregar, haga clic en view source .

introduzca la descripción de la imagen aquí

Probar si funciona agregándolo a la URL.

introduzca la descripción de la imagen aquí

Et voila, funciona. Ahora, el verdadero desafío: obtener la última página automáticamente y raspar todas las páginas. Su código está más o menos allí. Lo único que queda por hacer es obtener el número de páginas, construir una lista de URL para raspar e iterarlas.

El código modificado está abajo:

 from bs4 import BeautifulSoup as bsoup import requests as rq import re base_url = 'http://my.gwu.edu/mod/pws/courses.cfm?campId=1&termId=201501&subjId=ACCY' r = rq.get(base_url) soup = bsoup(r.text) # Use regex to isolate only the links of the page numbers, the one you click on. page_count_links = soup.find_all("a",href=re.compile(r".*javascript:goToPage.*")) try: # Make sure there are more than one page, otherwise, set to 1. num_pages = int(page_count_links[-1].get_text()) except IndexError: num_pages = 1 # Add 1 because Python range. url_list = ["{}&pageNum={}".format(base_url, str(page)) for page in range(1, num_pages + 1)] # Open the text file. Use with to save self from grief. with open("results.txt","wb") as acct: for url_ in url_list: print "Processing {}...".format(url_) r_new = rq.get(url_) soup_new = bsoup(r_new.text) for tr in soup_new.find_all('tr', align='center'): stack = [] for td in tr.findAll('td'): stack.append(td.text.replace('\n', '').replace('\t', '').strip()) acct.write(", ".join(stack) + '\n') 

Usamos expresiones regulares para obtener los enlaces adecuados. Luego, utilizando la comprensión de lista, creamos una lista de cadenas de URL. Finalmente, iteramos sobre ellos.

Resultados:

 Processing http://my.gwu.edu/mod/pws/courses.cfm?campId=1&termId=201501&subjId=ACCY&pageNum=1... Processing http://my.gwu.edu/mod/pws/courses.cfm?campId=1&termId=201501&subjId=ACCY&pageNum=2... Processing http://my.gwu.edu/mod/pws/courses.cfm?campId=1&termId=201501&subjId=ACCY&pageNum=3... [Finished in 6.8s] 

introduzca la descripción de la imagen aquí

Espero que ayude.

EDITAR:

Por puro aburrimiento, creo que acabo de crear un raspador para todo el directorio de la clase. Además, actualizo los códigos de arriba y de abajo para que no se produzcan errores cuando solo hay una sola página disponible.

 from bs4 import BeautifulSoup as bsoup import requests as rq import re spring_2015 = "http://my.gwu.edu/mod/pws/subjects.cfm?campId=1&termId=201501" r = rq.get(spring_2015) soup = bsoup(r.text) classes_url_list = [c["href"] for c in soup.find_all("a", href=re.compile(r".*courses.cfm\?campId=1&termId=201501&subjId=.*"))] print classes_url_list with open("results.txt","wb") as acct: for class_url in classes_url_list: base_url = "http://my.gwu.edu/mod/pws/{}".format(class_url) r = rq.get(base_url) soup = bsoup(r.text) # Use regex to isolate only the links of the page numbers, the one you click on. page_count_links = soup.find_all("a",href=re.compile(r".*javascript:goToPage.*")) try: num_pages = int(page_count_links[-1].get_text()) except IndexError: num_pages = 1 # Add 1 because Python range. url_list = ["{}&pageNum={}".format(base_url, str(page)) for page in range(1, num_pages + 1)] # Open the text file. Use with to save self from grief. for url_ in url_list: print "Processing {}...".format(url_) r_new = rq.get(url_) soup_new = bsoup(r_new.text) for tr in soup_new.find_all('tr', align='center'): stack = [] for td in tr.findAll('td'): stack.append(td.text.replace('\n', '').replace('\t', '').strip()) acct.write(", ".join(stack) + '\n')