you running run example are firefox selenium-webdriver web-scraping python-3.4

running - run firefox headless selenium python



¿Cómo devolver elementos X[Selenio]? (2)

Una página carga 35,000 elementos, que solo los primeros 10 me interesan. Devolver todos los elementos hace que el raspado sea extremadamente lento. Solo logré devolver el primer elemento con:

driver.find_element_by

O devolviendo todo, 35,000 elementos, con:

driver.find_elements_by

¿Alguien sabe cómo devolver x cantidad de elementos encontrados?


Aquí hay un enfoque significativamente diferente presentado como una respuesta diferente porque algunas personas preferirán esta a la otra que di, o la otra a esta.

Este se basa en el uso de XPath para cortar los resultados:

from selenium import webdriver driver = webdriver.Chrome() driver.get("http://www.example.com") # We add 35000 paragraphs with class `test` to the page so that we can # later show how to get the first 10 paragraphs of this class. Each # paragraph is uniquely numbered. These paragraphs are put into # individual `div` to make sure they are not siblings of one # another. (This prevents offering a naive XPath expression that would # work only if they *are* siblings.) driver.execute_script(""" var html = []; for (var i = 0; i < 35000; ++i) { html.push("<div><p class=''test''>"+ i + "</p></div>"); } document.body.innerHTML += html.join(""); """) elements = driver.find_elements_by_xpath( "(//p[@class=''test''])[position() < 11]") for element in elements: print element.text driver.quit()

Tenga en cuenta que XPath usa índices basados ​​en 1, por lo que < 11 es de hecho la expresión adecuada. Los paréntesis alrededor de la primera parte de la expresión son absolutamente necesarios. Con estos paréntesis, la prueba [position() < 11] verifica la posición que cada nodo tiene en el conjunto de nodos, que es el resultado de la expresión entre paréntesis . Sin ellos, la prueba de posición verificaría la posición de los nodos en relación con los nodos de sus padres , lo que coincidiría con todos los nodos porque todos <p> están en la primera posición en sus respectivos <div> . (Esta es la razón por la que he agregado esos elementos <div> arriba: para mostrar este problema).

Usaría esta solución si ya estuviera usando XPath para mi selección. De lo contrario, si estuviera haciendo una búsqueda por selector de CSS o por id, no la convertiría a XPath solo para realizar el corte. Utilizaría el otro método que he mostrado.


Selenium no proporciona una instalación que permita devolver solo una parte de los .find_elements... llamadas. Una solución general si quiere optimizar cosas para que no necesite tener Selenium devuelva cada elemento es realizar la operación de corte en el lado del navegador, en JavaScript. Presento esta solución en esta respuesta aquí. Si desea utilizar XPath para seleccionar los nodos DOM, podría adaptar la respuesta aquí, o podría usar el método en otra respuesta que he enviado.

from selenium import webdriver driver = webdriver.Chrome() driver.get("http://www.example.com") # We add 35000 paragraphs with class `test` to the page so that we can # later show how to get the first 10 paragraphs of this class. Each # paragraph is uniquely numbered. driver.execute_script(""" var html = []; for (var i = 0; i < 35000; ++i) { html.push("<p class=''test''>"+ i + "</p>"); } document.body.innerHTML += html.join(""); """) elements = driver.execute_script(""" return Array.prototype.slice.call(document.querySelectorAll("p.test"), 0, 10); """) # Verify that we got the first 10 elements by outputting the text they # contain to the console. The loop here is for illustration purposes # to show that the `elements` array contains what we want. In real # code, if I wanted to process the text of the first 10 elements, I''d # do what I show next. for element in elements: print element.text # A better way to get the text of the first 10 elements. This results # in 1 round-trip between this script and the browser. The loop above # would take 10 round-trips. print driver.execute_script(""" return Array.prototype.slice.call(document.querySelectorAll("p.test"), 0, 10) .map(function (x) { return x.textContent; });; """) driver.quit()

Array.prototype.slice.call rigmarole es necesario porque lo que document.querySelectorAll devuelve parece una Array pero no es realmente un objeto Array . (Es una NodeList ). Por lo tanto, no tiene un método .slice , pero puede pasarlo al método de slice Array .