with tutorial soup scraping scrap examples beautiful python parsing beautifulsoup

python - tutorial - BeautifulSoup `find_all` generator



web scraping python (3)

¿Hay alguna forma de convertir find_all en un generador más eficiente de memoria? Por ejemplo:

Dado:

soup = BeautifulSoup(content, "html.parser") return soup.find_all(''item'')

Me gustaría usar en su lugar:

soup = BeautifulSoup(content, "html.parser") while True: yield soup.next_item_generator()

(suponga que se entrega correctamente la excepción final de StopIteration )

Hay algunos generadores integrados, pero no para obtener el siguiente resultado en un hallazgo. find devuelve solo el primer elemento. Con miles de elementos, find_all absorbe mucha memoria. Para los 5792 ítems, veo un pico de poco más de 1 GB de RAM.

Soy muy consciente de que hay analizadores sintácticos más eficientes, como lxml, que pueden lograr esto. Supongamos que existen otras restricciones comerciales que me impiden usar cualquier otra cosa.

¿Cómo puedo convertir find_all en un generador para iterar de una manera más eficiente con la memoria ?


El método más simple es usar find_next :

soup = BeautifulSoup(content, "html.parser") def find_iter(tagname): tag = soup.find(tagname) while tag is not None: yield tag tag = tag.find_next(tagname)


Documento :

Di los nombres de los generadores PEP 8-obedientes, y los transformé en características:

childGenerator() -> children nextGenerator() -> next_elements nextSiblingGenerator() -> next_siblings previousGenerator() -> previous_elements previousSiblingGenerator() -> previous_siblings recursiveChildGenerator() -> descendants parentGenerator() -> parents

Hay un capítulo en el documento llamado Generadores , puedes leerlo.

SoupStrainer solo analizará la parte de html, puede ahorrar memoria, pero solo excluye la etiqueta irrelevante, si html tiene miles de etiquetas deseadas, dará como resultado el mismo problema de memoria.


No hay un generador de "búsqueda" en BeautifulSoup , por lo que sé, pero podemos combinar el uso de SoupStrainer y el generador de .children .

Imaginemos que tenemos este ejemplo de HTML:

<div> <item>Item 1</item> <item>Item 2</item> <item>Item 3</item> <item>Item 4</item> <item>Item 5</item> </div>

de lo cual necesitamos obtener el texto de todos los nodos de item .

Podemos usar el SoupStrainer para analizar solo las etiquetas de los item y luego iterar sobre el generador de .children y obtener los textos:

from bs4 import BeautifulSoup, SoupStrainer data = """ <div> <item>Item 1</item> <item>Item 2</item> <item>Item 3</item> <item>Item 4</item> <item>Item 5</item> </div>""" parse_only = SoupStrainer(''item'') soup = BeautifulSoup(data, "html.parser", parse_only=parse_only) for item in soup.children: print(item.get_text())

Huellas dactilares:

Item 1 Item 2 Item 3 Item 4 Item 5

En otras palabras, la idea es cortar el árbol hasta las etiquetas deseadas y usar uno de los generadores disponibles , como .children . También puede usar uno de estos generadores directamente y manualmente filtrar la etiqueta por nombre u otros criterios dentro del cuerpo del generador, por ejemplo, algo como:

def generate_items(soup): for tag in soup.descendants: if tag.name == "item": yield tag.get_text()

El .descendants genera los elementos hijos recursivamente, mientras que los .children solo consideran hijos directos de un nodo.