tutorial script scraping examples code python web-scraping urlopen

script - web scraping python examples



Web-scraping página de JavaScript con Python (11)

Estoy tratando de desarrollar un raspador web simple. Quiero extraer texto sin el código HTML. De hecho, logro este objetivo, pero he visto que en algunas páginas donde se carga JavaScript no obtuve buenos resultados.

Por ejemplo, si algún código JavaScript agrega algo de texto, no puedo verlo, porque cuando llamo

response = urllib2.urlopen(request)

Obtengo el texto original sin el agregado (porque JavaScript se ejecuta en el cliente).

Entonces, estoy buscando algunas ideas para resolver este problema.


Deberá usar urllib, requests, beautifulSoup y selenium web driver en su script para diferentes partes de la página (por nombrar algunos).
Algunas veces obtendrá lo que necesita con solo uno de estos módulos.
Algunas veces necesitará dos, tres o todos estos módulos.
Algunas veces deberá apagar el js en su navegador.
Algunas veces necesitará información de encabezado en su script.
No se pueden raspar sitios web de la misma manera y no se puede raspar el sitio web de la misma manera para siempre sin tener que modificar su rastreador, generalmente después de unos meses. ¡Pero todos pueden ser raspados! Donde hay voluntad, hay una forma segura.
Si necesita datos raspados continuamente en el futuro, simplemente raspe todo lo que necesita y guárdelo en archivos .dat con pickle.
Simplemente siga buscando cómo probar qué con estos módulos y copie y pegue sus errores en Google.


EDIT 30 / Dic / 2017: esta respuesta aparece en los mejores resultados de las búsquedas de Google, así que decidí actualizarla. La vieja respuesta todavía está al final.

dryscape ya no se mantiene y los desarrolladores de dryscape de la biblioteca lo recomiendan solo en Python 2. He descubierto que utilizar la biblioteca de Python de Selenium con Phantom JS como un controlador web es lo suficientemente rápido y fácil para realizar el trabajo.

Una vez que haya instalado Phantom JS , asegúrese de que el binario phantomjs esté disponible en la ruta actual:

phantomjs --version # result: 2.1.1

Ejemplo

Para dar un ejemplo, creé una página de muestra con el siguiente código HTML. ( link ):

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Javascript scraping test</title> </head> <body> <p id=''intro-text''>No javascript support</p> <script> document.getElementById(''intro-text'').innerHTML = ''Yay! Supports javascript''; </script> </body> </html>

sin javascript, dice: No javascript support y con javascript: Yay! Supports javascript Yay! Supports javascript

Raspado sin soporte JS:

import requests from bs4 import BeautifulSoup response = requests.get(my_url) soup = BeautifulSoup(response.text) soup.find(id="intro-text") # Result: <p id="intro-text">No javascript support</p>

Raspado con soporte JS:

from selenium import webdriver driver = webdriver.PhantomJS() driver.get(my_url) p_element = driver.find_element_by_id(id_=''intro-text'') print(p_element.text) # result: ''Yay! Supports javascript''

También puede usar la biblioteca de Python dryscrape para raspar sitios web controlados por JavaScript.

Raspado con soporte JS:

import dryscrape from bs4 import BeautifulSoup session = dryscrape.Session() session.visit(my_url) response = session.body() soup = BeautifulSoup(response) soup.find(id="intro-text") # Result: <p id="intro-text">Yay! Supports javascript</p>


El selenio es el mejor para raspar contenido de JS y Ajax.

Consulte este artículo https://likegeeks.com/python-web-scraping/

$ pip install selenium

Luego descarga Chrome webdriver.

from selenium import webdriver browser = webdriver.Chrome() browser.get("https://www.python.org/") nav = browser.find_element_by_id("mainnav") print(nav.text)

Fácil, ¿verdad?


Esta parece ser una buena solución también, tomada de una gran publicación de blog

import sys from PyQt4.QtGui import * from PyQt4.QtCore import * from PyQt4.QtWebKit import * from lxml import html #Take this class for granted.Just use result of rendering. class Render(QWebPage): def __init__(self, url): self.app = QApplication(sys.argv) QWebPage.__init__(self) self.loadFinished.connect(self._loadFinished) self.mainFrame().load(QUrl(url)) self.app.exec_() def _loadFinished(self, result): self.frame = self.mainFrame() self.app.quit() url = ''http://pycoders.com/archive/'' r = Render(url) result = r.frame.toHtml() # This step is important.Converting QString to Ascii for lxml to process # The following returns an lxml element tree archive_links = html.fromstring(str(result.toAscii())) print archive_links # The following returns an array containing the URLs raw_links = archive_links.xpath(''//div[@class="campaign"]/a/@href'') print raw_links


No estamos obteniendo los resultados correctos porque cualquier contenido generado por JavaScript debe representarse en el DOM. Cuando buscamos una página HTML, buscamos la inicial, no modificada por javascript, DOM.

Por lo tanto, debemos renderizar el contenido de JavaScript antes de rastrear la página.

Como el selenio ya se menciona muchas veces en este hilo (y lo lento que a veces se menciona también), voy a enumerar otras dos posibles soluciones.

Solución 1: Este es un tutorial muy bueno sobre cómo usar Scrapy para rastrear contenido generado por JavaScript y vamos a seguir exactamente eso.

Lo que necesitaremos:

  1. Docker instalado en nuestra máquina. Esto es una ventaja sobre otras soluciones hasta este punto, ya que utiliza una plataforma independiente del sistema operativo.

  2. Instale Splash siguiendo las instrucciones enumeradas para nuestro sistema operativo correspondiente.
    Citando de la documentación de splash:

    Splash es un servicio de renderizado de javascript. Es un navegador web liviano con una API HTTP, implementado en Python 3 usando Twisted y QT5.

    Básicamente, vamos a usar Splash para generar contenido generado por Javascript.

  3. Ejecute el servidor splash: sudo docker run -p 8050:8050 scrapinghub/splash .

  4. Instale el scrapy-splash : pip install scrapy-splash

  5. Suponiendo que ya tenemos un proyecto de Scrapy creado (si no, hagamos uno ), seguiremos la guía y actualizaremos la settings.py .py:

    Luego, vaya a settings.py de su proyecto scrapy y configure estos middlewares:

    DOWNLOADER_MIDDLEWARES = { ''scrapy_splash.SplashCookiesMiddleware'': 723, ''scrapy_splash.SplashMiddleware'': 725, ''scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware'': 810, }

    La URL del servidor Splash (si está usando Win o OSX, esta debería ser la URL de la máquina acoplable: ¿Cómo obtener la dirección IP de un contenedor Docker del host? ):

    SPLASH_URL = ''http://localhost:8050''

    Y finalmente necesita establecer estos valores también:

    DUPEFILTER_CLASS = ''scrapy_splash.SplashAwareDupeFilter'' HTTPCACHE_STORAGE = ''scrapy_splash.SplashAwareFSCacheStorage''

  6. Finalmente, podemos usar un SplashRequest :

    En una araña normal tienes objetos Request que puedes usar para abrir URLs. Si la página que desea abrir contiene datos generados por JS, debe utilizar SplashRequest (o SplashFormRequest) para representar la página. Aquí hay un ejemplo simple:

    class MySpider(scrapy.Spider): name = "jsscraper" start_urls = ["http://quotes.toscrape.com/js/"] def start_requests(self): for url in self.start_urls: yield SplashRequest( url=url, callback=self.parse, endpoint=''render.html'' ) def parse(self, response): for q in response.css("div.quote"): quote = QuoteItem() quote["author"] = q.css(".author::text").extract_first() quote["quote"] = q.css(".text::text").extract_first() yield quote

    SplashRequest representa la URL como html y devuelve la respuesta que puede usar en el método de devolución de llamada (analizar).

Solución 2: llamémoslo experimental en este momento (mayo de 2018) ...
Esta solución es solo para la versión 3.6 de Python (por el momento).

¿Conoces el módulo de requests (bueno, cómo no)?
Ahora tiene un pequeño hermano de rastreo web: https://html.python-requests.org/ :

Esta biblioteca tiene la intención de hacer que el análisis sintáctico de HTML (por ejemplo, raspado de la web) sea lo más simple e intuitivo posible.

  1. Instalar requests-html: pipenv install requests-html

  2. Realice una solicitud a la url de la página:

    from requests_html import HTMLSession session = HTMLSession() r = session.get(a_page_url)

  3. Renderice la respuesta para obtener los bits generados por Javascript:

    r.html.render()

Finalmente, el módulo parece ofrecer capacidades de raspado .
Alternativamente, podemos probar la forma bien documentada de usar BeautifulSoup con el objeto r.html que acabamos de presentar.


Parece que se puede acceder a los datos que realmente está buscando a través de una URL secundaria llamada por algún javascript en la página principal.

Si bien podrías intentar ejecutar javascript en el servidor para manejar esto, un enfoque más simple podría ser cargar la página usando Firefox y usar una herramienta como Charles o Firebug para identificar exactamente cuál es esa URL secundaria. Luego puede consultar esa URL directamente para los datos que le interesan.


Personalmente prefiero usar scrapy y selenio y dockerizing ambos en contenedores separados. De esta manera puede instalar ambos con un mínimo de problemas y rastrear sitios web modernos que casi todos contienen javascript de una forma u otra. Aquí hay un ejemplo:

Utilice el scrapy startproject de scrapy startproject para crear su raspador y escribir su araña, el esqueleto puede ser tan simple como esto:

import scrapy class MySpider(scrapy.Spider): name = ''my_spider'' start_urls = [''https://somewhere.com''] def start_requests(self): yield scrapy.Request(url=self.start_urls[0]) def parse(self, response): # do stuff with results, scrape items etc. # now were just checking everything worked print(response.body)

La verdadera magia ocurre en middlewares.py. Sobrescriba dos métodos en el middleware del descargador, __init__ y process_request , de la siguiente manera:

# import some additional modules that we need import os from copy import deepcopy from time import sleep from scrapy import signals from scrapy.http import HtmlResponse from selenium import webdriver class SampleProjectDownloaderMiddleware(object): def __init__(self): SELENIUM_LOCATION = os.environ.get(''SELENIUM_LOCATION'', ''NOT_HERE'') SELENIUM_URL = f''http://{SELENIUM_LOCATION}:4444/wd/hub'' chrome_options = webdriver.ChromeOptions() # chrome_options.add_experimental_option("mobileEmulation", mobile_emulation) self.driver = webdriver.Remote(command_executor=SELENIUM_URL, desired_capabilities=chrome_options.to_capabilities()) def process_request(self, request, spider): self.driver.get(request.url) # sleep a bit so the page has time to load # or monitor items on page to continue as soon as page ready sleep(4) # if you need to manipulate the page content like clicking and scrolling, you do it here # self.driver.find_element_by_css_selector(''.my-class'').click() # you only need the now properly and completely rendered html from your page to get results body = deepcopy(self.driver.page_source) # copy the current url in case of redirects url = deepcopy(self.driver.current_url) return HtmlResponse(url, body=body, encoding=''utf-8'', request=request)

No se olvide de habilitar este middlware al descomentar las siguientes líneas en el archivo settings.py:

DOWNLOADER_MIDDLEWARES = { ''sample_project.middlewares.SampleProjectDownloaderMiddleware'': 543,}

Siguiente para dockerization. Cree su Dockerfile partir de una imagen liviana (estoy usando Python Alpine aquí), copie el directorio de su proyecto, los requisitos de instalación:

# Use an official Python runtime as a parent image FROM python:3.6-alpine # install some packages necessary to scrapy and then curl because it''s handy for debugging RUN apk --update add linux-headers libffi-dev openssl-dev build-base libxslt-dev libxml2-dev curl python-dev WORKDIR /my_scraper ADD requirements.txt /my_scraper/ RUN pip install -r requirements.txt ADD . /scrapers

Y finalmente junten todo en docker-compose.yaml :

version: ''2'' services: selenium: image: selenium/standalone-chrome ports: - "4444:4444" shm_size: 1G my_scraper: build: . depends_on: - "selenium" environment: - SELENIUM_LOCATION=samplecrawler_selenium_1 volumes: - .:/my_scraper # use this command to keep the container running command: tail -f /dev/null

Ejecutar docker-compose up -d . Si está haciendo esto la primera vez, le llevará un tiempo obtener el último selenio / standalone-chrome y también crear su imagen de raspador.

Una vez hecho esto, puede verificar que sus contenedores se estén ejecutando con docker ps y también verificar que el nombre del contenedor de selenio coincida con el de la variable de entorno que pasamos a nuestro contenedor raspador (aquí, SELENIUM_LOCATION=samplecrawler_selenium_1 ).

Ingrese su contenedor scraper con docker exec -ti YOUR_CONTAINER_NAME sh , el comando para mí fue docker exec -ti samplecrawler_my_scraper_1 sh , cd en el directorio correcto y ejecute su scraper con scrapy crawl my_spider .

Todo está en mi página de github y puedes obtenerlo here


Si alguna vez usó el módulo de Requests para python anteriormente, descubrí recientemente que el desarrollador creó un nuevo módulo llamado Requests-HTML que ahora también tiene la capacidad de representar JavaScript.

También puede visitar https://html.python-requests.org/ para obtener más información sobre este módulo, o si solo le interesa la creación de JavaScript, puede visitar https://html.python-requests.org/?#javascript-support para aprender directamente cómo usar el módulo para renderizar JavaScript usando Python.

Esencialmente, una vez que instala correctamente el módulo Requests-HTML , el siguiente ejemplo, que se https://html.python-requests.org/?#javascript-support , muestra cómo puede usar este módulo para rastrear un sitio web y representar el contenido de JavaScript en el sitio web:

from requests_html import HTMLSession session = HTMLSession() r = session.get(''http://python-requests.org/'') r.html.render() r.html.search(''Python 2 will retire in only {months} months!'')[''months''] ''<time>25</time>'' #This is the result.

Hace poco aprendí sobre esto de un video de YouTube. ¡Haga clic aquí! para ver el video de YouTube, que muestra cómo funciona el módulo.


Tal vez el selenium puede hacerlo.

from selenium import webdriver import time driver = webdriver.Firefox() driver.get(url) time.sleep(5) htmlSource = driver.page_source


También puede ejecutar javascript usando webdriver.

from selenium import webdriver driver = webdriver.Firefox() driver.get(url) driver.execute_script(''document.title'')

o almacenar el valor en una variable

result = driver.execute_script(''var text = document.title ; return var'')


Una mezcla de BeautifulSoup y Selenium funciona muy bien para mí.

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from bs4 import BeautifulSoup as bs driver = webdriver.Firefox() driver.get("http://somedomain/url_that_delays_loading") try: element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "myDynamicElement"))) #waits 10 seconds until element is located. Can have other wait conditions such as visibility_of_element_located or text_to_be_present_in_element html = driver.page_source soup = bs(html, "lxml") dynamic_text = soup.find_all("p", {"class":"class_name"}) #or other attributes, optional else: print("Couldnt locate element")

PD: aquí puedes encontrar más condiciones de espera