Scraping épico para gente sin APIs

Ulises Gascón

Desarrollador Full Stack y orgulloso co-organizor de la comunidad Open Source Weekends (OSW)

 

Colaborador activo en la Comunidad Open Source

 

Trabaja como freelance, además de ser profesor en Fictizia.

  • Sin live coding ni ejecución de demos

  • Sin teoría pesada

  • Se adapta el nivel para tod@s (mayoría)

DISCLAIMER

  • Lo básico
  • ¡Scrapear es un Arte!
  • Herramientas
  • Ejemplos
  • ¡Preguntas!

Esta es una charla de

Una charla ofrecida por Fictizia sobre el arte de extraer datos de cualquier lugar usando librerías de Web Scraping como X-RAY, Cheerio (Node.js) o Beautiful Soup (Python) y automatizando las tareas más aburridas y repetitivas (login, clicks automáticos, completar formularios…) para hacer cosas útiles con la información obtenida usando Node.js, Python, CRON Jobs…

Lo básico

Lo básico

Hello World!

¿Qué es Scraping?

Web scraping es una técnica utilizada mediante programas de software para extraer información de sitios web. Usualmente, estos programas simulan la navegación de un humano en la World Wide Web ya sea utilizando el protocolo HTTP manualmente, o incrustando un navegador en una aplicación. - Wikipedia

¿Qué es un Crawler?

Un rastreador web, indexador web, indizador web o araña web es un programa informático que inspecciona las páginas del World Wide Web de forma metódica y automatizada. Uno de los usos más frecuentes que se les da consiste en crear una copia de todas las páginas web visitadas para su procesado posterior por un motor de búsqueda que indexa las páginas proporcionando un sistema de búsquedas rápido. Las arañas web suelen ser bots. - Wikipedia

Robots.txt

Este protocolo es consultivo. Confía en la cooperación de los robots del sitio Web, de modo que marca una o más áreas de un sitio fuera de los límites de búsqueda con el uso de un archivo robots.txt, aunque este no necesariamente garantice aislamiento completo. (...) En algunos casos el incluir un directorio en este archivo le anuncia su presencia a posibles hackers, así ellos pueden determinar fácilmente algunos softwares usados en el sitio mediante buscar "huellas típicas" en el robots.txt. - Wikipedia

Robots.txt

User-agent: *
Disallow: /cgi-bin/
Disallow: /images/
Disallow: /tmp/
Disallow: /private/

En resumen

¿Qué hace un buscador?

  • Indexa contenido web basándose en robots.txt y más criterios con sus arañas
  • Almacena la información
  • Procesa la información recolectada con un algoritmo que analiza muchos factores
  • Ordena los resultados según ciertos criterios  

Imagen cortesia prowebscraping.com

Lo básico

Técnico

Partes de una URL

┌─────────────────────────────────────────────────────────────────────────────────────────────┐
│                                            href                                             │
├──────────┬──┬─────────────────────┬─────────────────────┬───────────────────────────┬───────┤
│ protocol │  │        auth         │        host         │           path            │ hash  │
│          │  │                     ├──────────────┬──────┼──────────┬────────────────┤       │
│          │  │                     │   hostname   │ port │ pathname │     search     │       │
│          │  │                     │              │      │          ├─┬──────────────┤       │
│          │  │                     │              │      │          │ │    query     │       │
"  https:   //    user   :   pass   @ sub.host.com : 8080   /p/a/t/h  ?  query=string   #hash "
│          │  │          │          │   hostname   │ port │          │                │       │
│          │  │          │          ├──────────────┴──────┤          │                │       │
│ protocol │  │ username │ password │        host         │          │                │       │
├──────────┴──┼──────────┴──────────┼─────────────────────┤          │                │       │
│   origin    │                     │       origin        │ pathname │     search     │ hash  │
├─────────────┴─────────────────────┴─────────────────────┴──────────┴────────────────┴───────┤
│                                            href                                             │
└─────────────────────────────────────────────────────────────────────────────────────────────┘
(all spaces in the "" line should be ignored -- they are purely for formatting)

¿Como funciona HTTP?

Respuesta/Petición HTTP

Imagen cortesia MDN

Browser Engine

Imagen cortesia Mozilla.org

¿Que es Server Render?

¿Que es AJAX?

Lo básico

Legalidad

¿Esto es nuevo?

Este tema, cobró gran importancia en nuestro país allá en el año 2012, a raíz de la Sentencia del Tribunal Supremo de 9 de octubre, nº 572/2012, de RyanAir VS Atrápalo, en la cual se determinó que, en ese caso, el web scraping llevado a cabo por Atrápalo era legal. Si bien esta decisión señaló que no todo web scraping lo era, la importancia de esta sentencia radica en el hecho de que el Tribunal Supremo considerase legales las técnicas de web scraping, siempre y cuando, eso sí, se cumpliesen una serie de condiciones. - ecija.com

Cosas que debes verificar

Puntos importantes:

  • ¿Vulneración de los derechos de propiedad intelectual?
  • ¿Consideración de una conducta de competencia desleal?
  • ¿Violación de los términos legales y condiciones de uso?
  • ¿Incumplimiento de la normativa en protección de datos y la vulneración de los derechos de los titulares de los datos personales?

¿Que opinan l@s expert@s?

Por tanto, y a pesar del atractivo y facilidades en la utilización de las técnicas de web scraping en nuestro negocio para nuestro propio beneficio, antes de decantarnos por su utilización deberemos actuar con cautela, plantearnos una posible confrontación entre nuestra finalidad y la del propio website y realizar un análisis previo de las páginas objeto de scraping así como de sus términos y condiciones de uso, garantizando, en todo caso, que no se está incurriendo en una actividad ilegal en base a todos los puntos anteriormente expuestos. - ecija.com

Mi opinión personal

Consulta los detalles con un abogad@ especialista en esta materia.

Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY

Lo básico

Alternativas

¿Qué alternativas tengo?

Posibles alternativas

  • ¿Existe una API? ¡Estamos en 2018!
  • ¿Una API oculta al público?
  • ¿Otras webs similares con APIs?
  • ¿Open Data y haciendo cálculos?
  • ¿Generar la información?
  • ¿Hablar con la persona responsable y negociar soluciones?

Scraping es un arte

Las ventajas

La parte genial de esto:

  • Se acabó esperar actualizaciones de las APIs de turno
  • Acceso "ilimitado". Todo lo que puedes "ver", lo puedes extraer
  • Adios a los rate-limits
  • Adios a los sistemas freemium
  • Acceso "sin control (Anónimo)"
  • La ingeniería inversa es muuuy divertida
  • Mantener los bots funcionando y actualizados es un trabajo seguro
  • Está muy bien pagado
  • Es un súper poder que no puedes simplemente ignorar

El proceso

  1. Selección de objetivos
  2. Ingeniería inversa de la web objetivo
  3. Armonización de la extracción de datos
  4. Enriquecimiento y limpiado de la información
  5. Almacenamiento
  6. Distribución

Creando un algoritmo propio

Soluciones Comerciales

  1. Selección de objetivos
  2. Buscar una herramienta
  3. Definir el flujo y frecuencia
  4. Reutilización de la información

Browser Automation

Browser automation enables you to programmatically control a browser. For example, you could do some of the following:

  • Observe product pricing updates on an online store to discover the best time to purchase a particular product.
  • Log into your online banking account to download statements on a periodic basis.
  • Write functional tests or acceptance tests against a website you develop, in order to validate user functionality.
  • Complete a long and tedious HTML form which typically requires repetitive manual entry.

All modern browsers may be automated, including Chrome, Firefox, Edge & Safari. - Sitepen

Headless Browser

Notice that the browser has supporting elements for your use, such as a menu bar, address bar, and toolbar. These items are part of the GUI (Graphical User Interface). A headless browser has no GUI and no visual components. It runs as a process, and will expose a mechanism to enable outside interaction from source code or other software programs. - Sitepen

La vida del scrapper

La vida del scrapper

Juego del gato y el ratón

  • Sesión de navegación
  • Uso de Javascript
  • Bloqueos (proxys, peticiones...)
  • Cambios en el sitio objetivo (clases css, api, etc..)
  • Actualizaciones de seguridad
  • Captchas
  • Necesidad de mantener todo funcionando sin interrupciones

La defensa

  • Lista negra por monitorización y baneo sistemático de IPs y comportamientos
  • Usar cuentas y sistemas protegidos para trackear lo que realizan l@s usuari@s
  • CAPTCHAS
  • JavaScript exageradamente complejo y ofuscado
  • Cambios estructurales constantes
  • Configuraciones avanzadas en .htaccess
  • Defensas altas contra Cross Site Request Forgery (CSRF)
  • Prevención del hotlinking (Caso IMBD)
  • "Honeypots" para scrappers
  • Reportar l@s atacantes al proveedor ISP de turno

 

Herramientas

Herramientas

Para tod@s

Las más populares

Herramientas

Para desarrollador@s

Scraping via AJAX con Proxy

Selenium

Selenium automates browsers. That's it! What you do with that power is entirely up to you. Primarily, it is for automating web applications for testing purposes, but is certainly not limited to just that. Boring web-based administration tasks can (and should!) be automated as well.

Python

Python

  • Scrapy - Scrapy, a fast high-level web crawling & scraping framework for Python

  • Beautiful Soup - Python library for pulling data out of HTML and XML files.

  • Request - Python HTTP Requests for Humans™

  • Selenium with Python

  • pyspider - A powerful spider system.

  • PDFMiner - A tool for extracting information from PDF documents.

  • Ghost.py - wrapper of QtWebKit (requires PyQT)

  • WikiTeam - Tools for downloading and preserving wikis.

Node.js

Node.js

  • phantomjs - Scriptable Headless WebKit.

  • slimerjs - A PhantomJS-like tool running Gecko.

  • casperjs - Navigation scripting & testing utility for PhantomJS and SlimerJS.

  • zombie - Insanely fast, full-stack, headless browser testing using node.js.

  • nightmare - Nightmare is a high level wrapper for PhantomJS that lets you automate browser tasks

  • nightwatch - Automated testing and continous integration framework based on node.js and selenium webdriver

  • webdriverio - A Node.js bindings implementation for the W3C WebDriver protocol

Node.js

  • x-ray - The next web scraper. See through the <html> noise.

  • cheerio - Fast, flexible, and lean implementation of core jQuery designed specifically for the server.

  • scheduled - Scheduled job manager for JavaScript, linux cron pattern.

Headless Browser

A headless browser is a web browser without a graphical user interface.

 

Headless browsers provide automated control of a web page in an environment similar to popular web browsers, but are executed via a command-line interface or using network communication.

- Wikipedia

Headless Chrome

chrome \
  --headless \                   # Runs Chrome in headless mode.
  --disable-gpu \                # Temporarily needed if running on Windows.
  --remote-debugging-port=9222 \
  https://www.chromestatus.com   # URL to open. Defaults to about:blank.

chrome-launcher

const chromeLauncher = require('chrome-launcher');

chromeLauncher.launch({
  startingUrl: 'https://google.com',
  chromeFlags: ['--headless', '--disable-gpu']
}).then(chrome => {
  console.log(`Chrome debugging port running on ${chrome.port}`);
});

DevTools Protocol

The Chrome Developer Tools are a set of web authoring and debugging tools built into Google Chrome. The DevTools provide web developers deep access into the internals of the browser and their web application.

npm i --save chrome-remote-interface
/*
The following snippet loads https://github.com 
and dumps every request made
*/

const CDP = require('chrome-remote-interface');

CDP((client) => {
    // extract domains
    const {Network, Page} = client;
    // setup handlers
    Network.requestWillBeSent((params) => {
        console.log(params.request.url);
    });
    Page.loadEventFired(() => {
        client.close();
    });
    // enable events then start!
    Promise.all([
        Network.enable(),
        Page.enable()
    ]).then(() => {
        return Page.navigate({url: 'https://github.com'});
    }).catch((err) => {
        console.error(err);
        client.close();
    });
}).on('error', (err) => {
    // cannot connect to the remote endpoint
    console.error(err);
});

Herramientas

Puppeteer

Puppeteer

  • Generate screenshots and PDFs of pages.
  • Crawl a SPA and generate pre-rendered content (i.e. "SSR").
  • Automate form submission, UI testing, keyboard input, etc.
  • Create an up-to-date, automated testing environment. 
  • Capture a timeline trace of your site to help diagnose performance issues.

Headless Chrome Node API

npm i --save puppeteer

Captura de pantalla

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://fictizia.com');
  await page.screenshot({path: 'fictizia.png'});

  await browser.close();
})();

Generar un PDF

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle2'});
  await page.pdf({path: 'hn.pdf', format: 'A4'});

  await browser.close();
})();

Código en consola

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');

  // Get the "viewport" of the page, as reported by the page.
  const dimensions = await page.evaluate(() => {
    return {
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight,
      deviceScaleFactor: window.devicePixelRatio
    };
  });

  console.log('Dimensions:', dimensions);

  await browser.close();
})();

Ejemplos

Buscamos alojamiento en Berlin usando Booking.com, guardamos un screenshot e imprimimos el top 10

/**
 * @name Booking.com search
 * @see {@link https://github.com/checkly/puppeteer-examples/blob/master/2.%20search/booking.js}
 * @desc Finds accommodations in Berlin on Booking.com, takes a screenshot and logs the top 10.
 */
const puppeteer = require('puppeteer')
const screenshot = 'booking_results.png'
try {
  (async () => {
    const browser = await puppeteer.launch()
    const page = await browser.newPage()
    await page.goto('https://booking.com')
    await page.type('#ss', 'Berlin')
    await page.click('.sb-searchbox__button')
    await page.waitForSelector('#hotellist_inner')
    await page.screenshot({ path: screenshot })
    const hotels = await page.evaluate(() => {
      const anchors = Array.from(document.querySelectorAll('span.sr-hotel__name'))
      return anchors.map(anchor => anchor.textContent.trim()).slice(0, 10)
    })
    console.log(hotels)
    await browser.close()
    console.log('See screenshot: ' + screenshot)
  })()
} catch (err) {
  console.error(err)
}
Logearse en Github y generando un snapshot
/**
 * @name Github
 * @see {@link https://github.com/checkly/puppeteer-examples/blob/master/3.%20login/github.js}
 * @desc Logs into Github. Provide your username and password as environment variables when running the script, i.e:
 * `GITHUB_USER=myuser GITHUB_PWD=mypassword node github.js`
 *
 */
const puppeteer = require('puppeteer')
const screenshot = 'github.png';
(async () => {
  const browser = await puppeteer.launch({headless: true})
  const page = await browser.newPage()
  await page.goto('https://github.com/login')
  await page.type('#login_field', process.env.GITHUB_USER)
  await page.type('#password', process.env.GITHUB_PWD)
  await page.click('[name="commit"]')
  await page.waitForNavigation()
  await page.screenshot({ path: screenshot })
  browser.close()
  console.log('See screenshot: ' + screenshot)
})()
Descargar la documentación de la web de Puppeteer en PDF (A4 Multipagina)
/**
 * @name pdf
 * @desc Renders a PDF of the Puppeteer API spec. This is a pretty long page and will generate a nice, A4 size multi-page PDF.
 * @see {@link https://github.com/checkly/puppeteer-examples/blob/master/1.%20basics/pdf.js}
 * @see {@link https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pdf}
 */

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()
  await page.goto('https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pdf')
  await page.pdf({ path: 'api.pdf', format: 'A4' })
  await browser.close()
})()
Número de datasets listados en data.gov
# Number of datasets currently listed on data.gov
# https://github.com/stanfordjournalism/search-script-scrape/blob/master/scripts/1.py
from lxml import html
import requests
response = requests.get('http://www.data.gov/')
doc = html.fromstring(response.text)
link = doc.cssselect('small a')[0]
print(link.text)
Número de personas que visitan webs del gobierno americano con IE 6.0
# The number of people who visited a U.S. government website using Internet Explorer 6.0 in the last 90 days
# https://github.com/stanfordjournalism/search-script-scrape/blob/master/scripts/3.py
import requests
r = requests.get("https://analytics.usa.gov/data/live/ie.json")
print(r.json()['totals']['ie_version']['6.0'])
Número de alertas vigentes del departamento de estado de USA para viajes internacionales
# The number of international travel alerts from the U.S. State Department currently in effect
# https://github.com/stanfordjournalism/search-script-scrape/blob/master/scripts/47.py
import requests
from lxml import html
url = 'http://travel.state.gov/content/passports/english/alertswarnings.html'
doc = html.fromstring(requests.get(url).text)
print(len(doc.cssselect('td.alert')))
El número de personas en la Lista de los más buscados del FBI por crímenes de guante blanco
# The number of people on FBI's Most Wanted List for white collar crimes
# https://github.com/stanfordjournalism/search-script-scrape/blob/master/scripts/54.py
import requests
from lxml import html
url = 'http://www.fbi.gov/wanted/wcc/@@wanted-group-listing'
doc = html.fromstring(requests.get(url).text)
print(len(doc.cssselect('.contenttype-FBIPerson')))
La escuela de New York con el ratio más alto de excepciones religiosas para vacunas
# The New York school with the highest rate of religious exemptions to vaccinations
# https://github.com/stanfordjournalism/search-script-scrape/blob/master/scripts/97.py
import requests
url = 'https://health.data.ny.gov/resource/5pme-xbs5.json'
data = requests.get(url).json()

def foo(d):
    return float(d['percentreligiousexemptions'])

school = max([r for r in data if '2014' in r['report_period']], key = foo)
print(school['schoolname'])

Más ejemplos

Un paso más alla

Enriquecimiento

  • APIs externas de inteligencia Artificial (api.ai, GCloud, AWS, Watson, etc...)

  • IA in the house (Tensorflow, OpenCV, etc...)

  • Análisis de textos (semántica, expresiones regulares)

  • Mezclando datos con Datos abiertos

  • Mezclando datos con APIs específicas (Fullcontact, OWM, etc...)

Almacenar y distribuir

  • Montar una API Rest

  • Utilizar un sistema de WebSockets

  • Facilitar acceso por Base de datos

  • Sistema de queries de pago en bases de datos analíticas

  • Venta de información puntual o cíclica

  • Servicios Freemium (newsletters, etc..)

 

 

PREGUNTAS

Esta es una charla de

¡Gracias!

Los sueños son sumamente importantes. Nada se hace sin que antes se imagine.  

- George Lucas