Raspado del Registro de Farmacias
A los administradores de datos no les gusta. Vea cómo, ingresando dos comandos en la consola, descargó el registro de todas las farmacias en Polonia.
Daniel Gustaw
• 7 min read
Hay sitios web que están mejor o peor protegidos contra el scraping. Ahora echaremos un vistazo a un sitio web que no está protegido en absoluto: el Registro Médico que contiene datos sobre farmacias.
Del artículo, aprenderás cómo analizar sitios web y en qué debes prestar atención al hacer scraping de datos. Resulta que en algunos casos, realmente se necesitan cantidades mínimas de código para descargar datos en un formato conveniente para su posterior procesamiento.
El artículo se basa en un análisis de scraping en un caso específico. Aquí está el sitio web:
https://rejestrymedyczne.ezdrowie.gov.pl/main
Contiene varios registros de datos relacionados con la medicina.
Supongamos que queremos descargar todos los datos sobre farmacias de esta página. Hacemos clic en el registro de farmacias y vemos:
Curiosamente, la paginación no cambia la URL aquí, solo recarga la página y muestra la siguiente vista en la tabla.
Después de cambiar a la pestaña “Red” en la consola del navegador, podemos ver que se está enviando una solicitud en segundo plano.
Resulta que sin ningún token, clave o cookie, puedes descargar datos que se cargan en la tabla directamente desde la API utilizando un comando.
http -b https://rejestrymedyczne.ezdrowie.gov.pl/api/pharmacies/search\?page\=1\&size\=2\&sortField\=originId\&sortDirection\=ASC
No hay problema en descargar dos farmacias:
http -b https://rejestrymedyczne.ezdrowie.gov.pl/api/pharmacies/search\?page\=1\&size\=2\&sortField\=originId\&sortDirection\=ASC | jq '.[][] | {nr: .registrationNumber, name: .owners[0].name}'
No hay problema en descargar diez mil farmacias.
http -b https://rejestrymedyczne.ezdrowie.gov.pl/api/pharmacies/search\?page\=1\&size\=10000\&sortField\=originId\&sortDirection\=ASC | jq '.[][].owners[0].name' | wc
Para descargar 15 mil farmacias, el comando es
http -b https://rejestrymedyczne.ezdrowie.gov.pl/api/pharmacies/search\?page\=1\&size\=15000\&sortField\=originId\&sortDirection\=ASC | jq '.[][]' > ra.json
Para insertar datos en la base de datos
mongoimport --db test --collection ra --drop --file ./ra.json
Resulta que, desafortunadamente, solo tenemos 8006
documentos, no los 15000
esperados.
2021-02-17T22:59:19.216+0100 connected to: mongodb://localhost/
2021-02-17T22:59:19.217+0100 dropping: test.ra
2021-02-17T22:59:20.234+0100 8006 document(s) imported successfully. 0 document(s) failed to import.
Para 10,000
tenemos el resultado correcto para ambas descargas
http -b https://rejestrymedyczne.ezdrowie.gov.pl/api/pharmacies/search\?page\=1\&size\=10000\&sortField\=originId\&sortDirection\=ASC | jq '.[][]' > ra.json
cómo y importar
mongoimport --db test --collection ra --drop --file ./ra.json
2021-02-17T23:02:11.893+0100 connected to: mongodb://localhost/
2021-02-17T23:02:11.894+0100 dropping: test.ra
2021-02-17T23:02:13.143+0100 10000 document(s) imported successfully. 0 document(s) failed to import.
Es importante que al descargar la segunda página añadamos al archivo >>
y no sobrescribamos su contenido >
.
http -b https://rejestrymedyczne.ezdrowie.gov.pl/api/pharmacies/search\?page\=2\&size\=10000\&sortField\=originId\&sortDirection\=ASC | jq '.[][]' >> ra.json
Esta vez el registro muestra 13006
archivos y todo está explicado.
mongoimport --db test --collection ra --drop --file ./ra.json
2021-02-17T23:03:43.592+0100 connected to: mongodb://localhost/
2021-02-17T23:03:43.592+0100 dropping: test.ra
2021-02-17T23:03:45.173+0100 13006 document(s) imported successfully. 0 document(s) failed to import.
El resultado 8006
en size=15000
se debió al hecho de que las páginas están numeradas a partir de 0
en esta api
y 8006
= 23006 - 15000
, que fue el resultado correcto.
De todos modos, no importa si obtenemos en lotes de 10 o 15 mil, tenemos una solicitud restante con page=0
, por ejemplo:
http -b https://rejestrymedyczne.ezdrowie.gov.pl/api/pharmacies/search\?page\=0\&size\=10000\&sortField\=originId\&sortDirection\=ASC | jq '.[][]' >> ra.json
La última importación nos permite subir todas las farmacias.
mongoimport --db test --collection ra --drop --file ./ra.json
2021-02-17T23:08:02.038+0100 connected to: mongodb://localhost/
2021-02-17T23:08:02.038+0100 dropping: test.ra
2021-02-17T23:08:04.808+0100 23006 document(s) imported successfully. 0 document(s) failed to import.
En compass
podemos diseñar una agregación con dos etapas:
- Tomar una instantánea de la tabla
owners
{
'$unwind': {
'path': '$owners'
}
}
- Proyección de los campos más interesantes
{
'$project': {
'name': '$owners.name',
'firstName': '$owners.firstName',
'lastName': '$owners.lastName',
'krs': '$owners.krs',
'nip': '$owners.nip',
'regon': '$owners.regon',
'address': {
'$concat': [
'$address.street', ' ', '$address.homeNumber', ', ', '$address.postcode', ' ', '$address.city'
]
}
}
}
Nos permitirá generar el código del programa.
const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');
/*
* Requires the MongoDB Node.js Driver
* https://mongodb.github.io/node-mongodb-native
*/
const agg = [
{
'$unwind': {
'path': '$owners'
}
}, {
'$project': {
'name': '$owners.name',
'firstName': '$owners.firstName',
'lastName': '$owners.lastName',
'krs': '$owners.krs',
'nip': '$owners.nip',
'regon': '$owners.regon',
'address': {
'$concat': [
'$address.street', ' ', '$address.homeNumber', ', ', '$address.postcode', ' ', '$address.city'
]
}
}
}
];
MongoClient.connect(
'mongodb://localhost:27017/?readPreference=primary&appname=MongoDB%20Compass&ssl=false',
{ useNewUrlParser: true, useUnifiedTopology: true },
function(connectErr, client) {
assert.equal(null, connectErr);
const coll = client.db('test').collection('ra');
coll.aggregate(agg, (cmdErr, result) => {
assert.equal(null, cmdErr);
});
client.close();
});
Para habilitarlo, necesitamos instalar los controladores para mongo
y assert
.
npm init -y && npm i [email protected] assert
La versión @3.6.3
es el resultado de la aparición del error MongoError
faltante en la versión 3.6.4
.
El código presentado no hace nada además de realizar la agregación. Si quisiéramos guardar el resultado de la agregación en un archivo, necesitamos modificarlo ligeramente añadiendo la importación necesaria al principio.
const fs = require('fs')
y cambiando el callback a uno que realmente guarda el resultado.
MongoClient.connect(
'mongodb://localhost:27017/?readPreference=primary&appname=MongoDB%20Compass&ssl=false',
{ useNewUrlParser: true, useUnifiedTopology: true },
function(connectErr, client) {
assert.strictEqual(null, connectErr);
const coll = client.db('test').collection('ra');
coll.aggregate(agg, async (cmdErr, result) => {
assert.strictEqual(null, cmdErr);
const out = await result.toArray();
fs.writeFileSync('ra-project.json', JSON.stringify(out));
return client.close();
});
});
Resulta que los datos más interesantes son aproximadamente el 10% de lo que descargamos.
du -h ra*json
50M ra.json
4.8M ra-project.json
Esta entrada muestra lo fácil que es realizar scraping utilizando APIs proporcionadas por los creadores de sitios web.
Desafortunadamente, no todos los registros de este servicio son tan fáciles de recuperar. Uno de los más desafiantes es el registro de diagnosticadores médicos. Sin embargo, la dificultad aquí radica en que incluso una persona no puede acceder a algunos de los datos presentados en este registro debido a errores en el código del sitio web.
Resumen
Demostramos cómo detectar solicitudes al api
utilizando la consola del navegador y cómo utilizarlas para scraping de datos. Luego colocamos los datos en mongodb
y, a través de la agregación, generamos un conjunto de datos que es 10 veces más ligero, conteniendo solo la información más interesante.
En este proyecto, hay tan poco código que no se creó ningún repositorio para él. Los datos se pueden descargar desde los enlaces.
Todos los datos:
https://preciselab.fra1.digitaloceanspaces.com/blog/scraping/ra.json
Solo KRS, NIP, REGON, DIRECCIÓN, NOMBRE, APELLIDO, NOMBRE
https://preciselab.fra1.digitaloceanspaces.com/blog/scraping/ra-project.json
Si quieres desafiarme y sugerir un sitio web que valga la pena raspar, no dudes en programar una consulta sin compromiso.
Other articles
You can find interesting also.
Implementación de Rust de RFC 7396 - JSON Merge Patch
La velocidad y fiabilidad de Rust lo hacen ideal para implementar JSON Merge Patch, como se define en la RFC 7396. Esta especificación permite actualizaciones parciales eficientes y seguras de documentos JSON.
Daniel Gustaw
• 10 min read
Última Ocurrencia [Búsqueda Lineal] fácil
Encuentra e imprime el índice de la última ocurrencia del elemento en el array.
Daniel Gustaw
• 2 min read
Ruby on Rails - introducción rápida
Introducción a Ruby on Rails presentando CRUD, relaciones de base de datos, correo y comunicación por sockets web.
Daniel Gustaw
• 13 min read