Delete some unused stuff
This commit is contained in:
parent
b4c2d92c17
commit
e76e558a7a
84
bak/baxk1.js
84
bak/baxk1.js
@ -1,84 +0,0 @@
|
|||||||
// utils/filterProperties.js
|
|
||||||
import Fuse from 'fuse.js';
|
|
||||||
|
|
||||||
export const filterSimilarProperties = (properties) => {
|
|
||||||
const options = {
|
|
||||||
keys: ['dataValues.title'], // Buscamos similitud en el título dentro de dataValues
|
|
||||||
threshold: 0.6, // 0.6 es un umbral para 40% de similitud
|
|
||||||
includeScore: true // Incluye la puntuación de similitud
|
|
||||||
};
|
|
||||||
|
|
||||||
const fuse = new Fuse(properties, options);
|
|
||||||
const uniqueProperties = [];
|
|
||||||
const seen = new Set(); // Usaremos un conjunto para rastrear propiedades vistas
|
|
||||||
|
|
||||||
properties.forEach((property) => {
|
|
||||||
const propertyData = property.dataValues;
|
|
||||||
|
|
||||||
if (!seen.has(propertyData.id)) {
|
|
||||||
// Inicializa el array para IDs similares en dataValues
|
|
||||||
propertyData.similarIds = [];
|
|
||||||
|
|
||||||
// Buscamos propiedades similares por título
|
|
||||||
const result = fuse.search(propertyData.title).filter((res) => {
|
|
||||||
const similarProperty = res.item.dataValues;
|
|
||||||
// Filtramos resultados que no sean la misma propiedad y sean similares en título
|
|
||||||
const isSimilar = res.item.dataValues.id !== propertyData.id && res.score < options.threshold;
|
|
||||||
|
|
||||||
if (isSimilar) {
|
|
||||||
propertyData.similarIds.push(similarProperty.id);
|
|
||||||
|
|
||||||
// Enriquecer la propiedad actual con datos de la propiedad similar
|
|
||||||
propertyData.title = getLongest(propertyData.title, similarProperty.title);
|
|
||||||
propertyData.url = propertyData.url || similarProperty.url;
|
|
||||||
propertyData.price = propertyData.price || similarProperty.price;
|
|
||||||
propertyData.rooms = propertyData.rooms || similarProperty.rooms;
|
|
||||||
propertyData.area = propertyData.area || similarProperty.area;
|
|
||||||
propertyData.level = propertyData.level || similarProperty.level;
|
|
||||||
propertyData.description = getLongest(propertyData.description, similarProperty.description);
|
|
||||||
propertyData.pic = propertyData.pic || similarProperty.pic;
|
|
||||||
propertyData.baths = propertyData.baths || similarProperty.baths;
|
|
||||||
propertyData.neighborhood = propertyData.neighborhood || similarProperty.neighborhood;
|
|
||||||
propertyData.phone = propertyData.phone || similarProperty.phone;
|
|
||||||
|
|
||||||
// Marcar la propiedad similar como vista
|
|
||||||
seen.add(similarProperty.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return isSimilar;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Comprobamos coincidencia exacta en price, rooms y area
|
|
||||||
const matchingProperties = result.filter((res) => {
|
|
||||||
const similarProperty = res.item.dataValues;
|
|
||||||
return (
|
|
||||||
similarProperty.price === propertyData.price &&
|
|
||||||
(similarProperty.rooms === propertyData.rooms ||
|
|
||||||
similarProperty.area === propertyData.area)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (matchingProperties.length === 0) {
|
|
||||||
uniqueProperties.push(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Añadir los IDs de propiedades similares al array similarIds y marcarlas como vistas
|
|
||||||
matchingProperties.forEach((res) => {
|
|
||||||
const similarProperty = res.item.dataValues;
|
|
||||||
propertyData.similarIds.push(similarProperty.id);
|
|
||||||
seen.add(similarProperty.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Añadir la propiedad actual como vista
|
|
||||||
seen.add(propertyData.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return uniqueProperties;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getLongest = (a, b) => {
|
|
||||||
if (!a) return b;
|
|
||||||
if (!b) return a;
|
|
||||||
return a.length >= b.length ? a : b;
|
|
||||||
};
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
// utils/completePropertyData.js
|
|
||||||
import Fuse from 'fuse.js';
|
|
||||||
|
|
||||||
export const completePropertyData = (uniqueProperties, allProperties) => {
|
|
||||||
// Creamos una copia de allProperties con sólo los dataValues
|
|
||||||
const allDataValues = allProperties.map(prop => prop.dataValues);
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
keys: ['title'], // Buscamos similitud en el título
|
|
||||||
threshold: 0.6, // Umbral de similitud
|
|
||||||
includeScore: true // Incluye la puntuación de similitud
|
|
||||||
};
|
|
||||||
|
|
||||||
const fuse = new Fuse(allDataValues, options);
|
|
||||||
|
|
||||||
const enrichedProperties = uniqueProperties.map(property => {
|
|
||||||
// Buscamos propiedades similares por título para completar datos
|
|
||||||
const result = fuse.search(property.title).filter((res) => {
|
|
||||||
// Filtramos resultados que no sean la misma propiedad y sean similares en título
|
|
||||||
return res.item.id !== property.id && res.score < options.threshold;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Combinamos las propiedades similares para completar datos
|
|
||||||
let enrichedProperty = { ...property };
|
|
||||||
|
|
||||||
result.forEach((res) => {
|
|
||||||
const item = res.item;
|
|
||||||
enrichedProperty = {
|
|
||||||
...enrichedProperty,
|
|
||||||
title: getLongest(enrichedProperty.title, item.title),
|
|
||||||
url: enrichedProperty.url || item.url,
|
|
||||||
price: enrichedProperty.price || item.price,
|
|
||||||
rooms: enrichedProperty.rooms || item.rooms,
|
|
||||||
area: enrichedProperty.area || item.area,
|
|
||||||
level: enrichedProperty.level || item.level,
|
|
||||||
description: getLongest(enrichedProperty.description, item.description),
|
|
||||||
pic: enrichedProperty.pic || item.pic,
|
|
||||||
baths: enrichedProperty.baths || item.baths,
|
|
||||||
neighborhood: enrichedProperty.neighborhood || item.neighborhood,
|
|
||||||
phone: enrichedProperty.phone || item.phone,
|
|
||||||
createdAt: enrichedProperty.createdAt || item.createdAt,
|
|
||||||
updatedAt: enrichedProperty.updatedAt || item.updatedAt
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return enrichedProperty;
|
|
||||||
});
|
|
||||||
|
|
||||||
return enrichedProperties;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Función auxiliar para obtener el texto más largo o el que no sea nulo
|
|
||||||
const getLongest = (a, b) => {
|
|
||||||
if (!a) return b;
|
|
||||||
if (!b) return a;
|
|
||||||
return a.length >= b.length ? a : b;
|
|
||||||
};
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
// utils/filterProperties.js
|
|
||||||
import Fuse from 'fuse.js';
|
|
||||||
|
|
||||||
export const filterSimilarProperties = (properties) => {
|
|
||||||
const options = {
|
|
||||||
keys: ['title'], // Solo buscamos similitud en el título
|
|
||||||
threshold: 0.6, // 0.6 es un umbral para 40% de similitud
|
|
||||||
includeScore: true // Incluye la puntuación de similitud
|
|
||||||
};
|
|
||||||
|
|
||||||
const fuse = new Fuse(properties, options);
|
|
||||||
const uniqueProperties = [];
|
|
||||||
const seen = new Set(); // Usaremos un conjunto para rastrear propiedades vistas
|
|
||||||
|
|
||||||
properties.forEach((property) => {
|
|
||||||
const propertyData = property.dataValues;
|
|
||||||
if (!seen.has(propertyData.id)) {
|
|
||||||
// Inicializa el array para IDs similares en dataValues
|
|
||||||
propertyData.similarIds = [];
|
|
||||||
|
|
||||||
// Buscamos propiedades similares por título
|
|
||||||
const result = fuse.search(propertyData.title).filter((res) => {
|
|
||||||
const similarProperty = res.item.dataValues;
|
|
||||||
// Filtramos resultados que no sean la misma propiedad y sean similares en título
|
|
||||||
const isSimilar = res.item.dataValues.id !== propertyData.id && res.score < options.threshold;
|
|
||||||
|
|
||||||
if (isSimilar) {
|
|
||||||
propertyData.similarIds.push(similarProperty.id);
|
|
||||||
|
|
||||||
// Enriquecer la propiedad actual con datos de la propiedad similar
|
|
||||||
// propertyData.title = getLongest(propertyData.title, similarProperty.title);
|
|
||||||
// propertyData.url = propertyData.url || similarProperty.url;
|
|
||||||
// propertyData.price = propertyData.price || similarProperty.price;
|
|
||||||
// propertyData.rooms = propertyData.rooms || similarProperty.rooms;
|
|
||||||
// propertyData.area = propertyData.area || similarProperty.area;
|
|
||||||
// propertyData.level = propertyData.level || similarProperty.level;
|
|
||||||
// propertyData.description = getLongest(propertyData.description, similarProperty.description);
|
|
||||||
// propertyData.pic = propertyData.pic || similarProperty.pic;
|
|
||||||
// propertyData.baths = propertyData.baths || similarProperty.baths;
|
|
||||||
// propertyData.neighborhood = propertyData.neighborhood || similarProperty.neighborhood;
|
|
||||||
// propertyData.phone = propertyData.phone || similarProperty.phone;
|
|
||||||
|
|
||||||
// Marcar la propiedad similar como vista
|
|
||||||
//seen.add(similarProperty.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return isSimilar;
|
|
||||||
});
|
|
||||||
|
|
||||||
uniqueProperties.push(property);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return uniqueProperties;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const getLongest = (a, b) => {
|
|
||||||
if (!a) return b;
|
|
||||||
if (!b) return a;
|
|
||||||
return a.length >= b.length ? a : b;
|
|
||||||
};
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
// utils/filterProperties.js
|
|
||||||
import Fuse from 'fuse.js';
|
|
||||||
|
|
||||||
export const filterSimilarProperties = (properties) => {
|
|
||||||
const options = {
|
|
||||||
keys: ['title'], // Solo buscamos similitud en el título
|
|
||||||
threshold: 0.6, // 0.6 es un umbral para 40% de similitud
|
|
||||||
includeScore: true // Incluye la puntuación de similitud
|
|
||||||
};
|
|
||||||
|
|
||||||
const fuse = new Fuse(properties, options);
|
|
||||||
const uniqueProperties = [];
|
|
||||||
const seen = new Set(); // Usaremos un conjunto para rastrear propiedades vistas
|
|
||||||
|
|
||||||
properties.forEach((property) => {
|
|
||||||
if (!seen.has(property.id)) {
|
|
||||||
// Buscamos propiedades similares por título
|
|
||||||
const result = fuse.search(property.title).filter((res) => {
|
|
||||||
// Filtramos resultados que no sean la misma propiedad y sean similares en título
|
|
||||||
return res.item.id !== property.id && res.score < options.threshold;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Comprobamos coincidencia exacta en price, rooms y area
|
|
||||||
const matchingProperties = result.filter((res) => {
|
|
||||||
const item = res.item;
|
|
||||||
return (
|
|
||||||
item.price === property.price &&
|
|
||||||
(item.rooms === property.rooms ||
|
|
||||||
item.area === property.area)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (matchingProperties.length === 0) {
|
|
||||||
uniqueProperties.push(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marcar propiedades similares como vistas
|
|
||||||
matchingProperties.forEach((res) => {
|
|
||||||
seen.add(res.item.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Añadir la propiedad actual como vista
|
|
||||||
seen.add(property.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return uniqueProperties;
|
|
||||||
};
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
// utils/filterProperties.js
|
|
||||||
import Fuse from 'fuse.js';
|
|
||||||
|
|
||||||
export const filterSimilarProperties = (properties) => {
|
|
||||||
const options = {
|
|
||||||
keys: ['title'], // Solo buscamos similitud en el título
|
|
||||||
threshold: 0.6, // 0.6 es un umbral para 40% de similitud
|
|
||||||
includeScore: true // Incluye la puntuación de similitud
|
|
||||||
};
|
|
||||||
|
|
||||||
const fuse = new Fuse(properties, options);
|
|
||||||
const uniqueProperties = [];
|
|
||||||
const seen = new Set(); // Usaremos un conjunto para rastrear propiedades vistas
|
|
||||||
|
|
||||||
properties.forEach((property) => {
|
|
||||||
if (!seen.has(property.id)) {
|
|
||||||
// Buscamos propiedades similares por título
|
|
||||||
const result = fuse.search(property.title).filter((res) => {
|
|
||||||
// Filtramos resultados que no sean la misma propiedad y sean similares en título
|
|
||||||
return res.item.id !== property.id && res.score < options.threshold;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Comprobamos coincidencia exacta en price, rooms y area
|
|
||||||
const matchingProperties = result.filter((res) => {
|
|
||||||
const item = res.item;
|
|
||||||
return (
|
|
||||||
item.price === property.price &&
|
|
||||||
(item.rooms === property.rooms ||
|
|
||||||
item.area === property.area)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (matchingProperties.length === 0) {
|
|
||||||
uniqueProperties.push(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marcar propiedades similares como vistas
|
|
||||||
matchingProperties.forEach((res) => {
|
|
||||||
seen.add(res.item.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Añadir la propiedad actual como vista
|
|
||||||
seen.add(property.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return uniqueProperties;
|
|
||||||
};
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
import puppeteer from 'puppeteer';
|
|
||||||
import Listing from '../../server/db/models/Listing';
|
|
||||||
//import { notifyNewListing } from './telegramBot';
|
|
||||||
|
|
||||||
export default async function scrapeFotocasa() {
|
|
||||||
console.log('Starting Fotocasa scraping process');
|
|
||||||
|
|
||||||
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
|
|
||||||
const page = await browser.newPage();
|
|
||||||
await page.goto('https://www.fotocasa.es/es/comprar/viviendas/granada-capital/todas-las-zonas/l');
|
|
||||||
const pageSourceHTML = await page.content();
|
|
||||||
await browser.close();
|
|
||||||
console.log('Navigated to Fotocasa',pageSourceHTML);
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
const listings = await page.evaluate(() => {
|
|
||||||
const results = [];
|
|
||||||
document.querySelectorAll('.re-CardPackPremium').forEach((element) => {
|
|
||||||
const title = element.querySelector('.re-CardTitle')?.innerText.trim();
|
|
||||||
const priceText = element.querySelector('.re-CardPrice')?.innerText.trim();
|
|
||||||
const price = parseFloat(priceText.replace(/[^\d]/g, ''));
|
|
||||||
const location = title;
|
|
||||||
const description = element.querySelector('.re-CardDescription-text')?.innerText.trim();
|
|
||||||
const url = element.querySelector('a')?.href;
|
|
||||||
|
|
||||||
if (title && price && location && description && url) {
|
|
||||||
results.push({ title, price, location, description, url });
|
|
||||||
}
|
|
||||||
console.log(title, price, location, description, url);
|
|
||||||
});
|
|
||||||
return results;
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Found ${listings.length} listings`);
|
|
||||||
|
|
||||||
for (const listingData of listings) {
|
|
||||||
try {
|
|
||||||
const listing = new Listing(listingData);
|
|
||||||
//await listing.save();
|
|
||||||
console.log(`Saved listing to database: ${listing.title}`);
|
|
||||||
|
|
||||||
//await notifyNewListing(listing);
|
|
||||||
console.log(`Sent notification for listing: ${listing.title}`);
|
|
||||||
} catch (innerError) {
|
|
||||||
console.error(`Error processing listing: ${innerError.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error scraping Fotocasa:', error.message);
|
|
||||||
throw new Error('Failed to scrape Fotocasa');
|
|
||||||
} finally {
|
|
||||||
await browser.close();
|
|
||||||
console.log('Finished Fotocasa scraping process');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
//import { notifyNewListing } from './telegramBot';
|
|
||||||
|
|
||||||
// import Listing from '../../server/db/models/Listing';
|
|
||||||
|
|
||||||
export default async function scrapeHabitaclia(puppeteer) {
|
|
||||||
console.log("Starting Habitaclia scraping process");
|
|
||||||
|
|
||||||
const url = 'https://idealista7.p.rapidapi.com/listhomes?order=relevance&operation=sale&locationId=0-EU-ES-28-07-001-079&locationName=Madrid&numPage=1&maxItems=40&location=es&locale=en';
|
|
||||||
const options = {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'x-rapidapi-key': '080e9362e4mshdbcf5473f82adb9p196205jsne6a02c4e6381',
|
|
||||||
'x-rapidapi-host': 'idealista7.p.rapidapi.com'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(url, options);
|
|
||||||
const result = await response.text();
|
|
||||||
console.log(result);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
// const browser = await puppeteer.launch({
|
|
||||||
// headless: true,
|
|
||||||
// args: [
|
|
||||||
// '--no-sandbox' ,
|
|
||||||
// '--disable-setuid-sandbox'
|
|
||||||
// ]
|
|
||||||
// });
|
|
||||||
// const page = await browser.newPage();
|
|
||||||
|
|
||||||
// console.log(puppeteer, browser);
|
|
||||||
try {
|
|
||||||
// await page.goto('https://www.habitaclia.com/viviendas-granada_ciudad.htm', {
|
|
||||||
// waitUntil: 'networkidle2',
|
|
||||||
// });
|
|
||||||
// console.log('Navigated to Habitaclia');
|
|
||||||
// const content = await page.content();
|
|
||||||
// console.log(content);
|
|
||||||
// await page.screenshot({ path: 'habitaclia_state.png', fullPage: true })
|
|
||||||
// const listings = await page.evaluate(() => {
|
|
||||||
// const results = [];
|
|
||||||
// document.querySelectorAll('.list-item').forEach((element) => {
|
|
||||||
// const title = element.querySelector('.list-item-title')?.innerText.trim();
|
|
||||||
// const priceText = element.querySelector('.list-item-price')?.innerText.trim();
|
|
||||||
// const price = parseFloat(priceText.replace(/[^\d]/g, ''));
|
|
||||||
// const location = element.querySelector('.list-item-location')?.innerText.trim();
|
|
||||||
// const description = element.querySelector('.list-item-description')?.innerText.trim();
|
|
||||||
// const url = element.querySelector('.list-item-title a')?.href;
|
|
||||||
// if (title && price && location && description && url) {
|
|
||||||
// results.push({ title, price, location, description, url });
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// return results;
|
|
||||||
// });
|
|
||||||
// console.log(`Found ${listings.length} listings`);
|
|
||||||
// for (const listingData of listings) {
|
|
||||||
// try {
|
|
||||||
// const listing = new Listing(listingData);
|
|
||||||
// //await listing.save();
|
|
||||||
// console.log(`Saved listing to database: ${listing.title}`);
|
|
||||||
// //await notifyNewListing(listing);
|
|
||||||
// console.log(`Sent notification for listing: ${listing.title}`);
|
|
||||||
// } catch (innerError) {
|
|
||||||
// console.error(`Error processing listing: ${innerError.message}`);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error scraping Habitaclia:", error.message);
|
|
||||||
throw new Error("Failed to scrape Habitaclia");
|
|
||||||
} finally {
|
|
||||||
await browser.close();
|
|
||||||
console.log("Finished Habitaclia scraping process");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const Sequelize = require('sequelize');
|
|
||||||
const process = require('process');
|
|
||||||
const basename = path.basename(__filename);
|
|
||||||
const env = process.env.NODE_ENV || 'development';
|
|
||||||
const config = require(__dirname + '/../../../../config/sequelize.js')[env];
|
|
||||||
const db = {};
|
|
||||||
|
|
||||||
let sequelize;
|
|
||||||
if (config.use_env_variable) {
|
|
||||||
sequelize = new Sequelize(process.env[config.use_env_variable], config);
|
|
||||||
} else {
|
|
||||||
sequelize = new Sequelize(config.database, config.username, config.password, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
fs
|
|
||||||
.readdirSync(__dirname)
|
|
||||||
.filter(file => {
|
|
||||||
return (
|
|
||||||
file.indexOf('.') !== 0 &&
|
|
||||||
file !== basename &&
|
|
||||||
file.slice(-3) === '.js' &&
|
|
||||||
file.indexOf('.test.js') === -1
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.forEach(file => {
|
|
||||||
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
|
|
||||||
db[model.name] = model;
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(db).forEach(modelName => {
|
|
||||||
if (db[modelName].associate) {
|
|
||||||
db[modelName].associate(db);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
db.sequelize = sequelize;
|
|
||||||
db.Sequelize = Sequelize;
|
|
||||||
|
|
||||||
module.exports = db;
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
// import Listing from '../db/models/Listing';
|
|
||||||
|
|
||||||
export default defineEventHandler(async () => {
|
|
||||||
// return await Listing.find();
|
|
||||||
});
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
/* import {DEFAULT_INTERCEPT_RESOLUTION_PRIORITY} from 'puppeteer';
|
|
||||||
import Puppeteer from 'puppeteer';
|
|
||||||
import { addExtra } from 'puppeteer-extra';
|
|
||||||
import StealthPlugin from "puppeteer-extra-plugin-stealth";
|
|
||||||
import AdblockerPlugin from "puppeteer-extra-plugin-adblocker";
|
|
||||||
|
|
||||||
import chromeApp from "puppeteer-extra-plugin-stealth/evasions/chrome.app";
|
|
||||||
import chromeCsi from "puppeteer-extra-plugin-stealth/evasions/chrome.csi";
|
|
||||||
import chromeLoadTimes from "puppeteer-extra-plugin-stealth/evasions/chrome.loadTimes";
|
|
||||||
import chromeRuntime from "puppeteer-extra-plugin-stealth/evasions/chrome.runtime";
|
|
||||||
import defaultArgs from "puppeteer-extra-plugin-stealth/evasions/defaultArgs";
|
|
||||||
import iframeContentWindow from "puppeteer-extra-plugin-stealth/evasions/iframe.contentWindow";
|
|
||||||
import mediaCodecs from "puppeteer-extra-plugin-stealth/evasions/media.codecs";
|
|
||||||
import navigatorHardwareConcurrency from "puppeteer-extra-plugin-stealth/evasions/navigator.hardwareConcurrency";
|
|
||||||
import navigatorLanguages from "puppeteer-extra-plugin-stealth/evasions/navigator.languages";
|
|
||||||
import navigatorPermissions from "puppeteer-extra-plugin-stealth/evasions/navigator.permissions";
|
|
||||||
import navigatorPlugins from "puppeteer-extra-plugin-stealth/evasions/navigator.plugins";
|
|
||||||
import navigatorVendor from "puppeteer-extra-plugin-stealth/evasions/navigator.vendor";
|
|
||||||
import navigatorWebdriver from "puppeteer-extra-plugin-stealth/evasions/navigator.webdriver";
|
|
||||||
import sourceUrl from "puppeteer-extra-plugin-stealth/evasions/sourceurl";
|
|
||||||
import userAgentOverride from "puppeteer-extra-plugin-stealth/evasions/user-agent-override";
|
|
||||||
import webglVendor from "puppeteer-extra-plugin-stealth/evasions/webgl.vendor";
|
|
||||||
import windowOuterDimensions from "puppeteer-extra-plugin-stealth/evasions/window.outerdimensions";
|
|
||||||
|
|
||||||
export default defineNitroPlugin((nitroApp) => {
|
|
||||||
console.log("Loading puppeteer plugin...");
|
|
||||||
const puppeteer = addExtra(Puppeteer);
|
|
||||||
const stealth = StealthPlugin();
|
|
||||||
configureEvasions(stealth);
|
|
||||||
puppeteer.use(stealth);
|
|
||||||
|
|
||||||
puppeteer.use(
|
|
||||||
AdblockerPlugin({
|
|
||||||
// Optionally enable Cooperative Mode for several request interceptors
|
|
||||||
interceptResolutionPriority: DEFAULT_INTERCEPT_RESOLUTION_PRIORITY
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
nitroApp.hooks.hook("request", (event) => {
|
|
||||||
event.context.$puppeteer = puppeteer;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Configurar evasiones
|
|
||||||
const configureEvasions = (stealth) => {
|
|
||||||
// Clear existing evasions
|
|
||||||
stealth.enabledEvasions.clear();
|
|
||||||
|
|
||||||
// Add evasions explicitly
|
|
||||||
stealth.enabledEvasions.add(chromeApp);
|
|
||||||
stealth.enabledEvasions.add(chromeCsi);
|
|
||||||
stealth.enabledEvasions.add(chromeLoadTimes);
|
|
||||||
stealth.enabledEvasions.add(chromeRuntime);
|
|
||||||
stealth.enabledEvasions.add(defaultArgs);
|
|
||||||
stealth.enabledEvasions.add(iframeContentWindow);
|
|
||||||
stealth.enabledEvasions.add(mediaCodecs);
|
|
||||||
stealth.enabledEvasions.add(navigatorHardwareConcurrency);
|
|
||||||
stealth.enabledEvasions.add(navigatorLanguages);
|
|
||||||
stealth.enabledEvasions.add(navigatorPermissions);
|
|
||||||
stealth.enabledEvasions.add(navigatorPlugins);
|
|
||||||
stealth.enabledEvasions.add(navigatorVendor);
|
|
||||||
stealth.enabledEvasions.add(navigatorWebdriver);
|
|
||||||
stealth.enabledEvasions.add(sourceUrl);
|
|
||||||
stealth.enabledEvasions.add(userAgentOverride);
|
|
||||||
stealth.enabledEvasions.add(webglVendor);
|
|
||||||
stealth.enabledEvasions.add(windowOuterDimensions);
|
|
||||||
}; */
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
import puppeteer from 'puppeteer';
|
|
||||||
import Listing from '../../server/db/models/Listing';
|
|
||||||
//import { notifyNewListing } from './telegramBot';
|
|
||||||
|
|
||||||
export default async function scrapePisoscom() {
|
|
||||||
console.log('Starting Pisos.com scraping process');
|
|
||||||
|
|
||||||
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
|
|
||||||
const page = await browser.newPage();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await page.goto('https://www.pisos.com/venta/pisos-area_de_granada_granada_capital/', {
|
|
||||||
waitUntil: 'networkidle2',
|
|
||||||
});
|
|
||||||
console.log('Navigated to Pisos.com');
|
|
||||||
|
|
||||||
const listings = await page.evaluate(() => {
|
|
||||||
const results = [];
|
|
||||||
document.querySelectorAll('.ad-preview').forEach((element) => {
|
|
||||||
const title = element.querySelector('.ad-preview__title').innerText.trim();
|
|
||||||
const priceText = element.querySelector('.ad-preview__price').innerText.trim();
|
|
||||||
const price = parseFloat(priceText.replace(/[^\d]/g, ''));
|
|
||||||
const location = element.querySelector('.ad-preview__subtitle').innerText.trim();
|
|
||||||
const description = element.querySelector('.ad-preview__description').innerText.trim();
|
|
||||||
const phone = element.querySelector('.contact-box__phone').getAttribute('data-number');
|
|
||||||
const url = element.getAttribute('data-lnk-href');
|
|
||||||
if (title && price && location && description && url) {
|
|
||||||
results.push({ title, price, location, description, url, phone });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return results;
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Found ${listings.length} listings. Listing data: ${JSON.stringify(listings)}`);
|
|
||||||
|
|
||||||
for (const listingData of listings) {
|
|
||||||
try {
|
|
||||||
const listing = new Listing(listingData);
|
|
||||||
//await listing.save();
|
|
||||||
console.log(`Saved listing to database: ${listing.title}`);
|
|
||||||
|
|
||||||
//await notifyNewListing(listing);
|
|
||||||
console.log(`Sent notification for listing: ${listing.title}`);
|
|
||||||
} catch (innerError) {
|
|
||||||
console.error(`Error processing listing: ${innerError.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error scraping Pisos.com:', error.message);
|
|
||||||
throw new Error('Failed to scrape Pisos.com');
|
|
||||||
} finally {
|
|
||||||
await browser.close();
|
|
||||||
console.log('Finished Pisos.com scraping process');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex items-center justify-center min-h-screen bg-gray-100">
|
|
||||||
<div class="bg-white p-6 rounded-lg shadow-lg text-center max-w-md w-full">
|
|
||||||
<h1 class="text-2xl font-bold mb-4">Initiate Scraping</h1>
|
|
||||||
<button
|
|
||||||
@click="scrape"
|
|
||||||
class="bg-blue-500 text-white font-semibold py-2 px-4 rounded hover:bg-blue-700 transition-colors duration-300"
|
|
||||||
>
|
|
||||||
Start Scraping
|
|
||||||
</button>
|
|
||||||
<p v-if="message" class="mt-4 text-green-600">{{ message }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue';
|
|
||||||
|
|
||||||
const message = ref('');
|
|
||||||
const scrapers= [
|
|
||||||
//'scrapepisoscom',
|
|
||||||
'scrapehabitaclia'
|
|
||||||
]
|
|
||||||
|
|
||||||
const scrape = async () => {
|
|
||||||
message.value = 'Starting scraping...\n';
|
|
||||||
for (const portal of scrapers) {
|
|
||||||
await initiateScraping(portal);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const initiateScraping = async (portal) => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`/api/${portal}`, {
|
|
||||||
method: 'POST',
|
|
||||||
});
|
|
||||||
const result = await response.json();
|
|
||||||
if (result.success) {
|
|
||||||
message.value = 'Scraping initiated successfully: ' + JSON.stringify(result);
|
|
||||||
} else {
|
|
||||||
message.value = `Error: ${result.message}`;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
message.value = `Error: ${error.message}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* Tailwind CSS se encargará de los estilos, por lo que no se necesita nada aquí */
|
|
||||||
</style>
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
import scrapeHabitaclia from '../services/scraper/habitacliaScraper';
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
const puppeteer = event.context.$puppeteer;
|
|
||||||
try {
|
|
||||||
const result = await scrapeHabitaclia(puppeteer);
|
|
||||||
return { success: true, message: 'Habitacia scraping initiated.' , data: result || {}};
|
|
||||||
} catch (error) {
|
|
||||||
return { success: false, message: 'Failed to initiate Habitacia scraping.', error: error.message };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
// import pisoscomScraper from '../../modules/scraping/pisoscomScraper';
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
const result = await pisoscomScraper();
|
|
||||||
return { success: true, message: 'Fotocasa scraping initiated.' , data: result || {}};
|
|
||||||
} catch (error) {
|
|
||||||
return { success: false, message: 'Failed to initiate Fotocasa scraping.', error: error.message };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
15
bak/test.ts
15
bak/test.ts
@ -1,15 +0,0 @@
|
|||||||
import { createClient } from 'pexels';
|
|
||||||
const client = createClient('dKrFwkztM2MT9ss60BIBukRyTQpwEN4cFdydJis2zi2xeQ1NC4irqWGl');
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
const {query} = await readBody(event);
|
|
||||||
const result = await client.photos.search({ query, per_page: 20 });
|
|
||||||
|
|
||||||
return result?.photos.map(item => {
|
|
||||||
return {
|
|
||||||
id :item.id,
|
|
||||||
alt: item.alt,
|
|
||||||
src: item.src.large
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import mongoose from 'mongoose';
|
|
||||||
|
|
||||||
export const useDatabase = () => {
|
|
||||||
const config = useRuntimeConfig();
|
|
||||||
|
|
||||||
mongoose.connect(config.mongodbUri, { useNewUrlParser: true, useUnifiedTopology: true });
|
|
||||||
|
|
||||||
return mongoose;
|
|
||||||
}
|
|
||||||
0
composables/.gitikeep
Normal file
0
composables/.gitikeep
Normal file
0
modules/.gitikeep
Normal file
0
modules/.gitikeep
Normal file
0
plugins/.gitikeep
Normal file
0
plugins/.gitikeep
Normal file
BIN
project.zip
BIN
project.zip
Binary file not shown.
Loading…
Reference in New Issue
Block a user