deepl-scraper/index.js
2021-10-04 19:17:39 +02:00

100 lines
3.4 KiB
JavaScript

const
puppeteer = require('puppeteer'),
axios = require('axios'),
homepage = 'https://www.deepl.com/translator';
let browser;
const getBrowser = async () => {
if(browser) return browser;
browser = await puppeteer.launch({ args: ['--window-size=1920,1080'] });
browser.on('disconnected', () => browser = null);
return browser;
};
const getNewPage = async () => await (await getBrowser()).newPage();
module.exports = {
translate: async (sentence, sourceLanguage = 'auto', targetLanguage) => {
if(!/^(auto|[a-z]{2})$/.test(sourceLanguage))
throw new Error('INVALID_SOURCE_LANGUAGE');
if(!/^[a-z]{2}-[A-Z]{2}$/.test(targetLanguage))
throw new Error('INVALID_TARGET_LANGUAGE');
const
sourceLangSelect = '.lmt__language_select--source button',
targetLangSelect = '.lmt__language_select--target button',
sourceLangMenu = '.lmt__language_select__menu_source',
targetLangMenu = '.lmt__language_select__menu_target',
sourceLangButton = `${sourceLangMenu} [dl-test=translator-lang-option-${sourceLanguage}]`,
targetLangButton = `${targetLangMenu} [dl-test=translator-lang-option-${targetLanguage}]`,
originalSentenceField = '.lmt__source_textarea',
targetSentenceField = '.lmt__target_textarea'/*,
targetSentencesContainer = '.lmt__translations_as_text'*/;
const page = await getNewPage();
await page.goto(homepage);
await page.click(sourceLangSelect);
await page.waitForSelector(sourceLangMenu, { visible: true });
try {
await page.click(sourceLangButton);
}
catch(_){
throw new Error('UNSUPPORTED_SOURCE_LANGUAGE');
}
await page.waitForSelector(sourceLangMenu, { hidden: true });
await page.click(targetLangSelect);
await page.waitForSelector(targetLangMenu, { visible: true });
try {
await page.click(targetLangButton);
}
catch(_){
throw new Error('UNSUPPORTED_TARGET_LANGUAGE');
}
await page.waitForSelector(targetLangMenu, { hidden: true });
await page.waitForSelector(originalSentenceField);
await page.type(originalSentenceField, sentence);
let sentences = [];
let _res = {};
page.on('requestfinished', request => request.response().json().then(res => {
if(!res['result']) return;
sentences.push(...res['result']['translations'][0]['beams'].map(item => ({
value: item['postprocessed_sentence'],
confidence: item['totalLogProb']
})));
_res = {
source: {
lang: res['result']['source_lang'].toLowerCase(),
...(sourceLanguage === 'auto' ? {
confident: !!res['result']['source_lang_is_confident']
} : {}),
sentence
},
target: {
lang: res['result']['target_lang'].toLowerCase(),
sentences: sentences.sort((a, b) => a.confidence - b.confidence).map(item => item.value)
}
};
}).catch(() => {}));
await page.waitForSelector('.lmt--active_translation_request');
await page.waitForSelector('.lmt--active_translation_request', { hidden: true });
_res.target.translation = await page.$eval(targetSentenceField, el => el.value);
page.close().catch(() => {});
return _res;
},
quit: async () => {
if(browser)
await browser.close();
},
getSupportedLanguages: async () => {
const { data } = await axios.get(homepage);
return {
sourceLanguages: [...data.matchAll(/selectLang\.source\.([a-z]{2})/g)].map(match => match[1]),
targetLanguages: [...data.matchAll(/translator\/selectLang\.target\.(?:([a-z]{2})'|[a-z]{2}\.([a-z]{2}-[A-Z]{2}))/g)].map(match => match[2] || `${match[1]}-${match[1].toUpperCase()}`)
};
}
};