better logging, fixing code smells

This commit is contained in:
Christian Kellner
2025-01-07 12:25:19 +01:00
parent 2aaf63c253
commit 90a4ee5dcf
5 changed files with 232 additions and 231 deletions

View File

@@ -1,5 +1,5 @@
import {NoNewListingsWarning} from './errors.js';
import {setKnownListings, getKnownListings} from './services/storage/listingsStorage.js';
import { NoNewListingsWarning } from './errors.js';
import { setKnownListings, getKnownListings } from './services/storage/listingsStorage.js';
import * as notify from './notification/notify.js';
import Extractor from './services/extractor/extractor.js';
import urlModifier from './services/queryStringMutator.js';
@@ -47,11 +47,17 @@ class FredyRuntime {
_getListings(url) {
const extractor = new Extractor();
return new Promise((resolve, reject) => {
extractor.execute(url,this._providerConfig.waitForSelector)
extractor
.execute(url, this._providerConfig.waitForSelector)
.then(() => {
const listings = extractor.parseResponseText(this._providerConfig.crawlContainer, this._providerConfig.crawlFields);
const listings = extractor.parseResponseText(
this._providerConfig.crawlContainer,
this._providerConfig.crawlFields,
url,
);
resolve(listings == null ? [] : listings);
}).catch(err => {
})
.catch((err) => {
reject(err);
/* eslint-disable no-console */
console.error(err);

View File

@@ -1,11 +1,9 @@
import restana from 'restana';
import fetch from 'node-fetch';
import * as jobStorage from '../../services/storage/jobStorage.js';
import * as userStorage from '../../services/storage/userStorage.js';
import * as immoscoutProvider from '../../provider/immoscout.js';
import { config } from '../../utils.js';
import { isAdmin } from '../security.js';
import {trackDemoJobCreated} from '../../services/tracking/Tracker.js';
import { trackDemoJobCreated } from '../../services/tracking/Tracker.js';
const service = restana();
const jobRouter = service.newRouter();
function doesJobBelongsToUser(job, req) {
@@ -28,7 +26,7 @@ jobRouter.get('/', async (req, res) => {
jobRouter.get('/processingTimes', async (req, res) => {
res.body = {
interval: config.interval,
lastRun: config.lastRun || null
lastRun: config.lastRun || null,
};
res.send();
});
@@ -51,7 +49,7 @@ jobRouter.post('/', async (req, res) => {
trackDemoJobCreated({
name,
provider,
adapter: notificationAdapter
adapter: notificationAdapter,
});
res.send();
});

View File

@@ -1,19 +1,18 @@
import {setDebug} from './utils.js';
import { setDebug } from './utils.js';
import puppeteerExtractor from './puppeteerExtractor.js';
import {loadParser, parse} from './parser/parser.js';
import { loadParser, parse } from './parser/parser.js';
const DEFAULT_OPTIONS = {
debug: false,
puppeteerTimeout: 20_000,
puppeteerHeadless: true
puppeteerTimeout: 60_000,
puppeteerHeadless: true,
};
export default class Extractor {
constructor(options) {
this.options = {
...DEFAULT_OPTIONS,
...options
...options,
};
this.responseText = null;
setDebug(this.options);
@@ -29,7 +28,7 @@ export default class Extractor {
this.responseText = null;
try {
this.responseText = await puppeteerExtractor(url, waitForSelector, this.options);
if(this.responseText != null) {
if (this.responseText != null) {
loadParser(this.responseText);
}
} catch (error) {
@@ -38,8 +37,7 @@ export default class Extractor {
return this;
};
parseResponseText = (crawlContainer, crawlFields) => {
return parse(crawlContainer, crawlFields, this.responseText);
parseResponseText = (crawlContainer, crawlFields, url) => {
return parse(crawlContainer, crawlFields, this.responseText, url);
};
}

View File

@@ -6,21 +6,21 @@ export function loadParser(text) {
$ = cheerio.load(text);
}
export function parse(crawlContainer, crawlFields, text) {
export function parse(crawlContainer, crawlFields, text, url) {
if (!text) {
console.warn('Cannot parse, text was empty.');
console.warn('Cannot parse, text was empty for url ', url);
return null;
}
if (!crawlContainer || !crawlFields) {
console.warn('Cannot parse, selector was empty.');
console.warn('Cannot parse, selector was empty for url ', url);
return null;
}
const result = [];
if ($(crawlContainer).length === 0) {
console.error('No elements in crawl container found!');
console.error('No elements in crawl container found for url ', url);
}
$(crawlContainer).each((_, element) => {
@@ -32,8 +32,9 @@ export function parse(crawlContainer, crawlFields, text) {
let value;
try {
const selector = fieldSelector.includes('|') ? fieldSelector.substring(0, fieldSelector.indexOf('|')).trim() : fieldSelector;
const selector = fieldSelector.includes('|')
? fieldSelector.substring(0, fieldSelector.indexOf('|')).trim()
: fieldSelector;
if (selector.includes('@')) {
const [sel, attr] = selector.split('@');
@@ -48,7 +49,9 @@ export function parse(crawlContainer, crawlFields, text) {
// Apply modifiers if specified
if (fieldSelector.includes('|')) {
const [_, ...modifiers] = fieldSelector.split('|').map(s => s.trim());
/* eslint-disable no-unused-vars */
const [_, ...modifiers] = fieldSelector.split('|').map((s) => s.trim());
/* eslint-disable no-unused-vars */
value = applyModifiers(value, modifiers);
}
@@ -73,7 +76,7 @@ export function parse(crawlContainer, crawlFields, text) {
function applyModifiers(value, modifiers) {
if (!value) return value;
modifiers.forEach(modifier => {
modifiers.forEach((modifier) => {
switch (modifier) {
case 'int':
value = parseInt(value, 10);
@@ -91,4 +94,3 @@ function applyModifiers(value, modifiers) {
return value;
}

View File

@@ -1,34 +1,31 @@
let debuggingOn = false;
export const DEFAULT_HEADER = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Connection': 'keep-alive',
Connection: 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
};
export const setDebug = options => {
export const setDebug = (options) => {
debuggingOn = !!options?.debug;
};
export const debug = (message) => {
if(debuggingOn) {
if (debuggingOn) {
/* eslint-disable no-console */
console.debug(message);
/* eslint-enable no-console */
}
};
export const botDetected = (pageSource, statusCode) => {
const suspiciousStatusCodes = [
403, 429
];
const botDetectionPatterns = [
/verify you are human/i,
/access denied/i,
/x-amz-cf-id/i,
];
const suspiciousStatusCodes = [403, 429];
const botDetectionPatterns = [/verify you are human/i, /access denied/i, /x-amz-cf-id/i];
const detectedInSource = botDetectionPatterns.some(pattern => pattern.test(pageSource));
const detectedInSource = botDetectionPatterns.some((pattern) => pattern.test(pageSource));
const detectedByStatus = suspiciousStatusCodes.includes(statusCode);
return detectedInSource || detectedByStatus;