Парсер любого сайта

Статус
В этой теме нельзя размещать новые ответы.
Когда речь заходит о парсинге всех интересующих страниц сайта, я прибегаю к помощи своего скрипта. На мой взгляд, это наиболее быстрый и простой способ. Учитывая размеры скрипта, можно не бояться его потярять и в последствии восстановить по памяти (уже несколько раз писал).

Логика скрипта будет проста как туфелька:
- Поднять http-сервер
- На входящий запрос
- проверить, есть ли сохраненные данные по данному URL
- если есть, то отдать их в браузер
- если нет, то сделать запрос на целевой ресурс, заменить все упоминания целевого ресурса, сохранить данные в файл и отдать данные в браузер

Таким образом, мы можем авторизовываться на сайте, ходить по сайту, по разным страницам, и вся статика будет сохраняться.

Требования:
- Nodejs (теоретически, любой версии, используется core функционал)
- свободное дисковое пространство (для сохранения всех медиа файлов)

Пишем скрипт:
JavaScript:
const { request: request1 } = require('https');
const { request: request2, createServer } = require('http');
const { normalize } = require('path');
const { writeFile, readFile, existsSync, mkdir } = require('fs');


const targetHost = 'https://a.b'; // целевой ресурс
const saveDir = '/tmp/files'; // путь к директории, куда сохранять файлы
const port = 3000; // порт сервера, на котором поднимаем парсер

const ownHost = ''; // в большинстве случаев оставляем пустым
const always = false; // save existing files on every request

// маппинг расширений файлов и заголовков. Решает проблему браузера, когда "получил javascript , а тип контента text/html"
const ext2type = {
    'html': 'text/html',
    'css': 'text/css',
};

// игнорирование некорректных ssl сертификатов
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;


////////////////////////////////////////////////////////////////////////////////
const request = targetHost[4] === 's' ? request1 : request2;

const server = createServer((req, res) => {
    const path = req.url.split('?')[0];
    const pathSegments = path.split('/');
    let file = path;
    let fpath = path;
    const pathSegmentsLast = pathSegments[pathSegments.length - 1];
    if (pathSegmentsLast.length === 0) {
        file += 'index.html';
    } else {
        const lfext = pathSegmentsLast.split('.');
        if (lfext.length === 1) {
            file += '.html';
        }

        fpath = [...pathSegments];
        fpath.pop();
        fpath = fpath.join('/');
    }


    if (!always && existsSync(saveDir + file)) {
        let ext = file.split('.');
        ext = ext[ext.length-1];

        if (ext2type.hasOwnProperty(ext)) {
            res.setHeader('content-type', ext2type[ext]);
        }
        return readFile(saveDir + file, (err, data) => {
            if (err) {
                console.log('error readFile `%s`: ', file, err);
                res.end();
                return;
            }
            res.end(data);
        });
    }

    req.headers.host = targetHost.replace(/^https?:\/\//, ownHost);
    delete req.headers['accept-encoding'];
    console.log(' => ', req.method, targetHost + req.url);
    console.log(' => ', req.headers);
    //console.log(' => ', path, fpath, file);

    const r = request(targetHost + req.url, {
        method: req.method,
        headers: req.headers
    }, (resp) => {
        console.log(' <= ', resp.statusCode, resp.headers);
        Object.keys(resp.headers).forEach((header) => res.setHeader(header, resp.headers[header]));

        let data = Buffer.alloc(0);
        resp.on('data', (dat) => {
            data = Buffer.concat([data, dat], data.length + dat.length);
            res.write(dat);
        });
        resp.on('end', () => {
            res.end();
            console.log(' <= END event ');
            mkdir(saveDir + fpath, { recursive: true }, (err) => {
                if (err) {
                    console.log('err creating dirs %s:', fpath, err);
                    res.end(data);
                } else {
                    writeFile(saveDir + file, data, (err) => {
                        if (err) {
                            console.log('err with file %s:', req.url, err);
                        }
                    });
                }
            });
        });
    });

    req.on('data', (data) => r.write(data));
    req.on('end', () => r.end());
});

server.listen(port);

Данный скрипт был создан для решения моей цели - быстро скачать всю статику. Другие решения, которые встречались, имели ряд проблем, которые меня не устраивали.

Данный скрипт может содержать разные неточности и не во всех случаях будет отрабатывать корректно (в частности, из-за заголовков)
 
Последнее редактирование модератором:

DontWorry

По всем вопросам.
Администратор

DontWorry

По всем вопросам.
Администратор
Статус
Offline
Регистрация
14 Мар 2021
Сообщения
288
Лайки
2,533
Когда речь заходит о парсинге всех интересующих страниц сайта, я прибегаю к помощи своего скрипта. На мой взгляд, это наиболее быстрый и простой способ. Учитывая размеры скрипта, можно не бояться его потярять и в последствии восстановить по памяти (уже несколько раз писал).

Логика скрипта будет проста как туфелька:
- Поднять http-сервер
- На входящий запрос
- проверить, есть ли сохраненные данные по данному URL
- если есть, то отдать их в браузер
- если нет, то сделать запрос на целевой ресурс, заменить все упоминания целевого ресурса, сохранить данные в файл и отдать данные в браузер

Таким образом, мы можем авторизовываться на сайте, ходить по сайту, по разным страницам, и вся статика будет сохраняться.

Требования:
- Nodejs (теоретически, любой версии, используется core функционал)
- свободное дисковое пространство (для сохранения всех медиа файлов)

Пишем скрипт:
JavaScript:
const { request: request1 } = require('https');
const { request: request2, createServer } = require('http');
const { normalize } = require('path');
const { writeFile, readFile, existsSync, mkdir } = require('fs');


const targetHost = 'https://a.b'; // целевой ресурс
const saveDir = '/tmp/files'; // путь к директории, куда сохранять файлы
const port = 3000; // порт сервера, на котором поднимаем парсер

const ownHost = ''; // в большинстве случаев оставляем пустым
const always = false; // save existing files on every request

// маппинг расширений файлов и заголовков. Решает проблему браузера, когда "получил javascript , а тип контента text/html"
const ext2type = {
    'html': 'text/html',
    'css': 'text/css',
};

// игнорирование некорректных ssl сертификатов
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;


////////////////////////////////////////////////////////////////////////////////
const request = targetHost[4] === 's' ? request1 : request2;

const server = createServer((req, res) => {
    const path = req.url.split('?')[0];
    const pathSegments = path.split('/');
    let file = path;
    let fpath = path;
    const pathSegmentsLast = pathSegments[pathSegments.length - 1];
    if (pathSegmentsLast.length === 0) {
        file += 'index.html';
    } else {
        const lfext = pathSegmentsLast.split('.');
        if (lfext.length === 1) {
            file += '.html';
        }

        fpath = [...pathSegments];
        fpath.pop();
        fpath = fpath.join('/');
    }


    if (!always && existsSync(saveDir + file)) {
        let ext = file.split('.');
        ext = ext[ext.length-1];

        if (ext2type.hasOwnProperty(ext)) {
            res.setHeader('content-type', ext2type[ext]);
        }
        return readFile(saveDir + file, (err, data) => {
            if (err) {
                console.log('error readFile `%s`: ', file, err);
                res.end();
                return;
            }
            res.end(data);
        });
    }

    req.headers.host = targetHost.replace(/^https?:\/\//, ownHost);
    delete req.headers['accept-encoding'];
    console.log(' => ', req.method, targetHost + req.url);
    console.log(' => ', req.headers);
    //console.log(' => ', path, fpath, file);

    const r = request(targetHost + req.url, {
        method: req.method,
        headers: req.headers
    }, (resp) => {
        console.log(' <= ', resp.statusCode, resp.headers);
        Object.keys(resp.headers).forEach((header) => res.setHeader(header, resp.headers[header]));

        let data = Buffer.alloc(0);
        resp.on('data', (dat) => {
            data = Buffer.concat([data, dat], data.length + dat.length);
            res.write(dat);
        });
        resp.on('end', () => {
            res.end();
            console.log(' <= END event ');
            mkdir(saveDir + fpath, { recursive: true }, (err) => {
                if (err) {
                    console.log('err creating dirs %s:', fpath, err);
                    res.end(data);
                } else {
                    writeFile(saveDir + file, data, (err) => {
                        if (err) {
                            console.log('err with file %s:', req.url, err);
                        }
                    });
                }
            });
        });
    });

    req.on('data', (data) => r.write(data));
    req.on('end', () => r.end());
});

server.listen(port);

Данный скрипт бы лсоздан для решения моей цели - быстро скачать всю статику. Другие решения, которые встречались, имели ряд проблем, которые меня не устраивали.

Данный скрипт может содержать разные неточности и не во всех случаях будет отрабатывать корректно (в частности, из-за заголовков)
Воу. Это круто. Молодец (если код твой конечно же).

Так же предлагаю альтернативу ребятам. Есть софт WebSite-Watcher. С богатым функционалом, с ее помощью можно отслеживать изменения на страницах с уведомлением.
 

Node

HTM.
Модератор

Node

HTM.
Модератор
Статус
Offline
Регистрация
20 Мар 2021
Сообщения
81
Лайки
377
Когда речь заходит о парсинге всех интересующих страниц сайта, я прибегаю к помощи своего скрипта. На мой взгляд, это наиболее быстрый и простой способ. Учитывая размеры скрипта, можно не бояться его потярять и в последствии восстановить по памяти (уже несколько раз писал).

Логика скрипта будет проста как туфелька:
- Поднять http-сервер
- На входящий запрос
- проверить, есть ли сохраненные данные по данному URL
- если есть, то отдать их в браузер
- если нет, то сделать запрос на целевой ресурс, заменить все упоминания целевого ресурса, сохранить данные в файл и отдать данные в браузер

Таким образом, мы можем авторизовываться на сайте, ходить по сайту, по разным страницам, и вся статика будет сохраняться.

Требования:
- Nodejs (теоретически, любой версии, используется core функционал)
- свободное дисковое пространство (для сохранения всех медиа файлов)

Пишем скрипт:
JavaScript:
const { request: request1 } = require('https');
const { request: request2, createServer } = require('http');
const { normalize } = require('path');
const { writeFile, readFile, existsSync, mkdir } = require('fs');


const targetHost = 'https://a.b'; // целевой ресурс
const saveDir = '/tmp/files'; // путь к директории, куда сохранять файлы
const port = 3000; // порт сервера, на котором поднимаем парсер

const ownHost = ''; // в большинстве случаев оставляем пустым
const always = false; // save existing files on every request

// маппинг расширений файлов и заголовков. Решает проблему браузера, когда "получил javascript , а тип контента text/html"
const ext2type = {
    'html': 'text/html',
    'css': 'text/css',
};

// игнорирование некорректных ssl сертификатов
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;


////////////////////////////////////////////////////////////////////////////////
const request = targetHost[4] === 's' ? request1 : request2;

const server = createServer((req, res) => {
    const path = req.url.split('?')[0];
    const pathSegments = path.split('/');
    let file = path;
    let fpath = path;
    const pathSegmentsLast = pathSegments[pathSegments.length - 1];
    if (pathSegmentsLast.length === 0) {
        file += 'index.html';
    } else {
        const lfext = pathSegmentsLast.split('.');
        if (lfext.length === 1) {
            file += '.html';
        }

        fpath = [...pathSegments];
        fpath.pop();
        fpath = fpath.join('/');
    }


    if (!always && existsSync(saveDir + file)) {
        let ext = file.split('.');
        ext = ext[ext.length-1];

        if (ext2type.hasOwnProperty(ext)) {
            res.setHeader('content-type', ext2type[ext]);
        }
        return readFile(saveDir + file, (err, data) => {
            if (err) {
                console.log('error readFile `%s`: ', file, err);
                res.end();
                return;
            }
            res.end(data);
        });
    }

    req.headers.host = targetHost.replace(/^https?:\/\//, ownHost);
    delete req.headers['accept-encoding'];
    console.log(' => ', req.method, targetHost + req.url);
    console.log(' => ', req.headers);
    //console.log(' => ', path, fpath, file);

    const r = request(targetHost + req.url, {
        method: req.method,
        headers: req.headers
    }, (resp) => {
        console.log(' <= ', resp.statusCode, resp.headers);
        Object.keys(resp.headers).forEach((header) => res.setHeader(header, resp.headers[header]));

        let data = Buffer.alloc(0);
        resp.on('data', (dat) => {
            data = Buffer.concat([data, dat], data.length + dat.length);
            res.write(dat);
        });
        resp.on('end', () => {
            res.end();
            console.log(' <= END event ');
            mkdir(saveDir + fpath, { recursive: true }, (err) => {
                if (err) {
                    console.log('err creating dirs %s:', fpath, err);
                    res.end(data);
                } else {
                    writeFile(saveDir + file, data, (err) => {
                        if (err) {
                            console.log('err with file %s:', req.url, err);
                        }
                    });
                }
            });
        });
    });

    req.on('data', (data) => r.write(data));
    req.on('end', () => r.end());
});

server.listen(port);

Данный скрипт бы лсоздан для решения моей цели - быстро скачать всю статику. Другие решения, которые встречались, имели ряд проблем, которые меня не устраивали.

Данный скрипт может содержать разные неточности и не во всех случаях будет отрабатывать корректно (в частности, из-за заголовков)
За NodeJS респект, а так проще сделать парсер на питоне, имхо)
 

Glock

Пользователь

Glock

Пользователь
Статус
Offline
Регистрация
13 Май 2021
Сообщения
7
Лайки
47
С легкостью могу сказаьть, что есть сайт с парсером, где раздают по 150 рублей. (TurboParser) (не реклама)
А так, гайд прикольный, но больше труда там надо(
 

Diz_rus

Пользователь

Diz_rus

Пользователь
Статус
Offline
Регистрация
5 Апр 2021
Сообщения
8
Лайки
48
Спасибо за парсер. По сути можно заюзать его для коммерческих целей. Если автор конечно не против))
 

vovceks

Новорег

vovceks

Новорег
Статус
Offline
Регистрация
23 Мар 2021
Сообщения
4
Лайки
12
С легкостью могу сказаьть, что есть сайт с парсером, где раздают по 150 рублей. (TurboParser) (не реклама)
А так, гайд прикольный, но больше труда там надо(
если есть вопросы или что-то не получается - спрашивайте =)
 
Статус
В этой теме нельзя размещать новые ответы.
Сверху