Перейти к публикации
  • Сейчас на странице   Всего пользователей: 8   (2 пользователя, 6 гостей)

yellyex

Я поехал

Рекомендованные сообщения

Я даже готов скинуться если в сексе будет использован тот самый черенок 

Поделиться сообщением


Ссылка на сообщение
(изменено)
Ratio написал 15 минут назад:
yellyex написал 21 минуту назад:
Ratio написал 22 минуты назад:
yellyex написал 39 минут назад:
Ratio написал 41 минуту назад:
yellyex написал 44 минуты назад:
Ratio написал 45 минут назад:
yellyex написал 50 минут назад:
Son of Anarchy написал 54 минуты назад:

винипух>русалоча>черный плащ ну ты и гавноед

алё это хохлина же ему рубль нише и всё нахуй сифка на всю жизнь

Не понял причину тряски.

Он считает тебя инфантильным, социально-депривированным, ограниченным и отстающим в развитии лет на 25 человеком

Кто он? Я или он?

 

Мой пост разве можно истолковать двояко ?

Я лично не понял тебя. Скажи прямо.

Изобрел с помощью нейронки две програмки, для переименовывания файлов и перемещения.

Для смартфонов нужно скачать pydroid3 и плагины (они рядом).





import os

# ==================== НАСТРОЙКИ ====================
# Выберите режим нумерации:
#   True  -> 001, 002, 003 ... (с ведущими нулями)
#   False -> 1, 2, 3 ...
use_leading_zeros = True   # измените здесь

# Папка, в которой лежат файлы
# Раскомментируйте нужную строку, другую закомментируйте
# folder = "/storage/EDDF-B3B1/Download/Русалочка"      # вариант 1
folder = "/storage/emulated/0/Download/Electro"    # вариант 2

# Общая часть имени и расширение
base_name = "Electro"
extension = ".flac"
start_number = 1 # можно начать не с 1, если нужно
# ===================================================

# Проверка существования папки
if not os.path.exists(folder):
    print(f"Ошибка: папка '{folder}' не существует.")
else:
    try:
        # Получаем список файлов в папке
        all_items = os.listdir(folder)
        files = [f for f in all_items if os.path.isfile(os.path.join(folder, f))]
        files.sort()

        if not files:
            print("В папке нет файлов.")
        else:
            count = len(files)
            print(f"Найдено файлов: {count}")

            # Если нужны нули, вычисляем необходимую ширину (например, для 120 файлов ширина 3)
            if use_leading_zeros:
                width = len(str(count + start_number - 1))
            else:
                width = None

            for n, old_name in enumerate(files, start=start_number):
                old_path = os.path.join(folder, old_name)

                # Формируем новое имя в зависимости от режима
                if use_leading_zeros:
                    new_name = f"{base_name} {n:0{width}d}{extension}"
                else:
                    new_name = f"{base_name} {n}{extension}"

                new_path = os.path.join(folder, new_name)

                if os.path.exists(new_path):
                    print(f"Пропуск: {new_name} уже существует")
                else:
                    os.rename(old_path, new_path)
                    print(f"{old_name} -> {new_name}")

            print("Готово!")

    except Exception as e:
        print(f"Ошибка: {e}")




import os
import shutil

# Укажите путь к корневой папке, откуда нужно извлечь файлы
root_folder = "/storage/emulated/0/Download/Electro"

# Если нужно обрабатывать только файлы определённых расширений, задайте список
# Например: allowed_extensions = ['.mkv', '.mp4', '.avi']
# Если оставить None, будут извлечены все файлы
allowed_extensions = '.flac' # или ['.mkv', '.mp4']

def collect_files_to_root(root):
    if not os.path.exists(root):
        print(f"Ошибка: папка '{root}' не существует.")
        return

    # Собираем все файлы из всех подпапок (рекурсивно)
    all_files = []
    for dirpath, dirnames, filenames in os.walk(root):
        # Пропускаем саму корневую папку, чтобы не перемещать уже находящиеся в ней файлы
        if dirpath == root:
            continue
        for f in filenames:
            # Фильтр по расширению, если задан
            if allowed_extensions is not None:
                ext = os.path.splitext(f)[1].lower()
                if ext not in allowed_extensions:
                    continue
            full_path = os.path.join(dirpath, f)
            all_files.append(full_path)

    print(f"Найдено файлов для перемещения: {len(all_files)}")

    # Перемещаем каждый файл в корневую папку, решая конфликты
    for src in all_files:
        filename = os.path.basename(src)
        dst = os.path.join(root, filename)

        # Если файл с таким именем уже существует, добавляем суффикс
        if os.path.exists(dst):
            name, ext = os.path.splitext(filename)
            counter = 1
            while True:
                new_name = f"{name}_{counter}{ext}"
                dst = os.path.join(root, new_name)
                if not os.path.exists(dst):
                    break
                counter += 1

        # Перемещаем файл
        shutil.move(src, dst)
        print(f"Перемещён: {src} -> {dst}")

    # Удаляем пустые подпапки (оставляя корневую)
    for dirpath, dirnames, filenames in os.walk(root, topdown=False):
        if dirpath == root:
            continue
        try:
            os.rmdir(dirpath)  # удалит только пустую папку
            print(f"Удалена пустая папка: {dirpath}")
        except OSError:
            # Папка не пуста — пропускаем (но по логике все файлы уже перемещены, так что она должна быть пустой)
            pass

    print("Готово! Все файлы собраны в корневой папке, пустые подпапки удалены.")

if __name__ == "__main__":
    collect_files_to_root(root_folder)

 

У тебя был секс с женщиной?

Нет конечно! Что за глупые вопросы?

Может лучше проститутку снимешь ? 

В проститутке жить нельзя. Ну на крайняк можно найти какую-нибудь сосну, и выстругать Буратино. И её ебать. Естественно из мальчика надо сделать девочку. Будет: Соснатино.


Изменено пользователем yellyex

moonfangtopich написал 29.08.2019 в 14:57:
У вас недостаточно широкий кругозор, пацаны

Я странствия этого еблана видел в покерных топанах, а потом в таверне - это один из самых безумных людей на форуме. Я искренне надеялся, что его зов о помощи останется незамеченным, но нет, нашелся доброволец и вот уже три страницы мы пожинаем плоды

Поделиться сообщением


Ссылка на сообщение
yellyex написал 48 минут назад:

Изобрел с помощью нейронки две програмки, для переименовывания файлов и перемещения

Зачем:trollface:

Поделиться сообщением


Ссылка на сообщение

 

 

 

С помощью этого парсера, я выцепил все посты в текстовом виде. Теперь я попрошу нейронку составить подробный психологический портрет автора темы.

 

# -*- coding: utf-8 -*-
"""
Парсер форума prodota.ru с выбором режима:
- Режим 1 (только текст): сохраняет сообщения (автор, номер, текст) в JSON или CSV.
- Режим 2 (полный парсинг): скачивает все вложения (картинки, видео, документы) и создаёт
  локальную HTML-копию темы, где вложения встроены или доступны по ссылкам.

ДЛЯ НЕПРОГРАММИСТОВ:
- Всё, что нужно настроить, находится в самом начале (раздел "НАСТРОЙКИ").
- Укажите START_URL, FOLDER_NAME, DEFAULT_OUTPUT_DIR и другие параметры.
- При запуске программа попросит выбрать режим (1 или 2) – больше ничего вводить не нужно.
- Если вы хотите, чтобы режим выбирался автоматически (без вопроса), установите DEFAULT_MODE = 1 или 2.
"""

import os
import re
import time
import json
import csv
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse, unquote
from pathlib import Path

# ========================== НАСТРОЙКИ (изменяйте здесь) ==========================
# Режим по умолчанию (если оставить None, будет запрошен при запуске)
# 1 - только текст, 2 - полный парсинг с вложениями
DEFAULT_MODE = None   # или 1, 2

# Ссылка на первую страницу темы (обязательно укажите!)
START_URL = 'https://prodota.ru/forum/topic/224160/'   # <-- ЗАМЕНИТЕ НА ВАШУ ТЕМУ

# Имя папки, куда будет сохранён архив (без пробелов, латиница и цифры)
FOLDER_NAME = 'prodota_archive'   # <-- МОЖЕТЕ ИЗМЕНИТЬ

# Папка, в которую будут сохраняться все архивы (по умолчанию – папка Download на Android)
DEFAULT_OUTPUT_DIR = "/storage/emulated/0/Download"

# Задержка между запросами к страницам (секунды) – чтобы не перегружать сервер
DELAY = 1.5

# Таймаут для запроса (секунды)
TIMEOUT = 20

# Для текстового режима: формат сохранения ('json' или 'csv')
TEXT_FORMAT = 'json'   # можно изменить на 'csv'

# Расширения файлов для полного режима (определяют тип вложения)
IMAGE_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg')
VIDEO_EXTENSIONS = ('.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv')
DOC_EXTENSIONS = ('.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.txt', '.zip', '.rar', '.7z')
ATTACH_EXTENSIONS = IMAGE_EXTENSIONS + VIDEO_EXTENSIONS + DOC_EXTENSIONS
# =================================================================================


# ----------------------------------------
#  Базовый класс с общими методами (загрузка страниц, парсинг постов)
# ----------------------------------------
class BaseParser:
    def __init__(self, start_url, output_dir, delay=DELAY, timeout=TIMEOUT):
        self.start_url = start_url
        self.output_dir = Path(output_dir)
        self.delay = delay
        self.timeout = timeout
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
        })
        self.posts = []
        self.seen_posts = set()

    def get_soup(self, url):
        try:
            response = self.session.get(url, timeout=self.timeout)
            response.raise_for_status()
            if response.encoding is None:
                response.encoding = 'utf-8'
            return BeautifulSoup(response.text, 'html.parser')
        except Exception as e:
            print(f"Ошибка загрузки {url}: {e}")
            return None

    def extract_post_data(self, post_element, base_url):
        """Извлекает автора, номер поста и текст (без обработки вложений)."""
        author_elem = post_element.select_one('.cAuthorPane_author, .ipsComment_author, .author')
        if not author_elem:
            return None
        author = author_elem.get_text(strip=True)

        post_id = None
        if post_element.get('id'):
            match = re.search(r'(\d+)$', post_element.get('id'))
            if match:
                post_id = match.group(1)
        if not post_id:
            id_link = post_element.select_one('a[data-commentid]')
            if id_link and id_link.get('data-commentid'):
                post_id = id_link['data-commentid']
        if not post_id:
            post_id = 'N/A'

        content_elem = post_element.select_one('.cPost_contentWrap, .ipsComment_content, .post-content')
        if not content_elem:
            return None
        text = content_elem.get_text(separator='\n', strip=True)

        return {
            'author': author,
            'post_number': post_id,
            'content': text
        }

    def parse_page(self, url):
        soup = self.get_soup(url)
        if not soup:
            return None

        post_blocks = soup.select('li.ipsComment, div.cPost, article.ipsComment, div.post')
        if not post_blocks:
            print("   Не найдено сообщений на странице. Проверьте структуру сайта.")
            return None

        for block in post_blocks:
            post_data = self.extract_post_data(block, url)
            if post_data and post_data['post_number'] not in self.seen_posts:
                self.seen_posts.add(post_data['post_number'])
                self.posts.append(post_data)

        next_link = None
        next_elem = soup.select_one('a[rel="next"]')
        if next_elem and next_elem.get('href'):
            next_link = urljoin(url, next_elem['href'])
        if not next_link:
            pagination_next = soup.select_one('.ipsPagination_next a, .next a')
            if pagination_next and pagination_next.get('href'):
                next_link = urljoin(url, pagination_next['href'])

        return next_link

    def run(self):
        print(f"Начинаем парсинг: {self.start_url}")
        print(f"Папка для сохранения: {self.output_dir.absolute()}")
        current_url = self.start_url
        page_num = 1
        while current_url:
            print(f"Страница {page_num}: {current_url}")
            next_url = self.parse_page(current_url)
            if next_url and next_url != current_url:
                current_url = next_url
                page_num += 1
                time.sleep(self.delay)
            else:
                break
        print(f"\nСобрано сообщений: {len(self.posts)}")


# ----------------------------------------
#  Режим 1: только текст, сохраняет в JSON/CSV
# ----------------------------------------
class TextParser(BaseParser):
    def __init__(self, start_url, output_dir, format='json'):
        super().__init__(start_url, output_dir)
        self.format = format.lower()

    def save(self):
        if self.format == 'json':
            output_file = self.output_dir / 'posts.json'
            with open(output_file, 'w', encoding='utf-8') as f:
                json.dump(self.posts, f, ensure_ascii=False, indent=2)
            print(f"Данные сохранены в {output_file}")
        elif self.format == 'csv':
            output_file = self.output_dir / 'posts.csv'
            with open(output_file, 'w', newline='', encoding='utf-8-sig') as f:
                writer = csv.DictWriter(f, fieldnames=['author', 'post_number', 'content'])
                writer.writeheader()
                writer.writerows(self.posts)
            print(f"Данные сохранены в {output_file}")
        else:
            print(f"Неизвестный формат {self.format}, данные не сохранены.")

    def run(self):
        super().run()
        self.save()


# ----------------------------------------
#  Режим 2: полный парсинг с вложениями и HTML-архивом
# ----------------------------------------
class FullArchiver(BaseParser):
    def __init__(self, start_url, output_dir):
        super().__init__(start_url, output_dir)
        self.attachment_counter = 0
        self.local_attachments = {}
        # Создаём папки для вложений
        self.attachments_dir = self.output_dir / 'attachments'
        self.images_dir = self.attachments_dir / 'images'
        self.videos_dir = self.attachments_dir / 'videos'
        self.docs_dir = self.attachments_dir / 'docs'
        for d in [self.attachments_dir, self.images_dir, self.videos_dir, self.docs_dir]:
            d.mkdir(parents=True, exist_ok=True)

    def download_file(self, url, subdir):
        parsed = urlparse(url)
        filename = os.path.basename(unquote(parsed.path))
        if not filename or '.' not in filename:
            self.attachment_counter += 1
            try:
                head = self.session.head(url, timeout=5, allow_redirects=True)
                content_type = head.headers.get('content-type', '')
                ext = self.guess_extension_by_mime(content_type)
                filename = f"file_{self.attachment_counter}{ext}"
            except:
                filename = f"file_{self.attachment_counter}.bin"
        else:
            filename = filename.split('?')[0]

        base, ext = os.path.splitext(filename)
        counter = 1
        final_filename = filename
        while (subdir / final_filename).exists():
            final_filename = f"{base}_{counter}{ext}"
            counter += 1

        local_path = subdir / final_filename
        rel_path = local_path.relative_to(self.output_dir).as_posix()

        try:
            with self.session.get(url, stream=True, timeout=self.timeout) as r:
                r.raise_for_status()
                with open(local_path, 'wb') as f:
                    for chunk in r.iter_content(chunk_size=8192):
                        f.write(chunk)
            print(f"  Скачан файл: {rel_path}")
            return rel_path
        except Exception as e:
            print(f"  Ошибка скачивания {url}: {e}")
            return None

    def guess_extension_by_mime(self, mime):
        mime = mime.lower()
        if 'image/jpeg' in mime:
            return '.jpg'
        if 'image/png' in mime:
            return '.png'
        if 'image/gif' in mime:
            return '.gif'
        if 'video/mp4' in mime:
            return '.mp4'
        if 'application/pdf' in mime:
            return '.pdf'
        return '.bin'

    def is_attachment_url(self, url):
        if not url:
            return False
        if url.startswith('#') or url.startswith('javascript:'):
            return False
        path = urlparse(url).path.lower()
        if any(path.endswith(ext) for ext in ATTACH_EXTENSIONS):
            return True
        if '/uploads/' in path or '/monthly_' in path or 'attachments' in path:
            return True
        return False

    def process_attachments_in_html(self, html_content, base_url):
        if not html_content:
            return html_content

        soup = BeautifulSoup(html_content, 'html.parser')

        for a_tag in soup.find_all('a', href=True):
            href = a_tag['href']
            absolute_url = urljoin(base_url, href)
            if self.is_attachment_url(absolute_url):
                if absolute_url in self.local_attachments:
                    local_path = self.local_attachments[absolute_url]
                else:
                    ext = os.path.splitext(urlparse(absolute_url).path)[1].lower()
                    if ext in IMAGE_EXTENSIONS:
                        subdir = self.images_dir
                    elif ext in VIDEO_EXTENSIONS:
                        subdir = self.videos_dir
                    else:
                        subdir = self.docs_dir
                    local_path = self.download_file(absolute_url, subdir)
                    if local_path:
                        self.local_attachments[absolute_url] = local_path
                    else:
                        continue

                a_tag['href'] = local_path
                if ext in IMAGE_EXTENSIONS:
                    img_tag = soup.new_tag('img', src=local_path, alt=local_path, style="max-width:100%;")
                    a_tag.replace_with(img_tag)
                elif ext in VIDEO_EXTENSIONS:
                    video_tag = soup.new_tag('video', controls=True, style="max-width:100%;")
                    source_tag = soup.new_tag('source', src=local_path, type='video/mp4')
                    video_tag.append(source_tag)
                    a_tag.replace_with(video_tag)

        for img_tag in soup.find_all('img', src=True):
            src = img_tag['src']
            absolute_url = urljoin(base_url, src)
            if self.is_attachment_url(absolute_url):
                if absolute_url in self.local_attachments:
                    local_path = self.local_attachments[absolute_url]
                else:
                    local_path = self.download_file(absolute_url, self.images_dir)
                    if local_path:
                        self.local_attachments[absolute_url] = local_path
                    else:
                        continue
                img_tag['src'] = local_path

        return str(soup)

    def extract_post_data(self, post_element, base_url):
        """Переопределяем метод для обработки вложений в HTML."""
        author_elem = post_element.select_one('.cAuthorPane_author, .ipsComment_author, .author')
        if not author_elem:
            return None
        author = author_elem.get_text(strip=True)

        post_id = None
        if post_element.get('id'):
            match = re.search(r'(\d+)$', post_element.get('id'))
            if match:
                post_id = match.group(1)
        if not post_id:
            id_link = post_element.select_one('a[data-commentid]')
            if id_link and id_link.get('data-commentid'):
                post_id = id_link['data-commentid']
        if not post_id:
            post_id = 'N/A'

        content_elem = post_element.select_one('.cPost_contentWrap, .ipsComment_content, .post-content')
        if not content_elem:
            return None

        inner_html = str(content_elem)
        processed_html = self.process_attachments_in_html(inner_html, base_url)

        return {
            'author': author,
            'post_number': post_id,
            'html': processed_html
        }

    def generate_html(self):
        html_template = """<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Архив темы: {title}</title>
    <style>
        body {{
            font-family: Arial, sans-serif;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
            background: #f5f5f5;
        }}
        .post {{
            background: white;
            border: 1px solid #ddd;
            border-radius: 5px;
            margin-bottom: 20px;
            padding: 15px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }}
        .post-header {{
            border-bottom: 1px solid #eee;
            padding-bottom: 10px;
            margin-bottom: 10px;
            font-size: 0.9em;
            color: #666;
        }}
        .post-author {{
            font-weight: bold;
            color: #333;
        }}
        .post-id {{
            color: #999;
            margin-left: 10px;
        }}
        .post-content {{
            line-height: 1.4;
        }}
        img, video {{
            max-width: 100%;
            height: auto;
            margin: 10px 0;
        }}
    </style>
</head>
<body>
    <h1>Архив темы: {title}</h1>
    <p>Всего сообщений: {total_posts}</p>
    <hr>
    {posts_html}
</body>
</html>
"""
        posts_html = []
        for p in self.posts:
            posts_html.append(f"""
            <div class="post">
                <div class="post-header">
                    <span class="post-author">{p['author']}</span>
                    <span class="post-id">Пост #{p['post_number']}</span>
                </div>
                <div class="post-content">
                    {p['html']}
                </div>
            </div>
            """)

        title = "Тема с prodota.ru"
        final_html = html_template.format(
            title=title,
            total_posts=len(self.posts),
            posts_html='\n'.join(posts_html)
        )
        return final_html

    def run(self):
        super().run()
        print("Генерация HTML-страницы...")
        html_content = self.generate_html()
        index_path = self.output_dir / 'index.html'
        with open(index_path, 'w', encoding='utf-8') as f:
            f.write(html_content)
        print(f"HTML-страница сохранена: {index_path.absolute()}")


# ----------------------------------------
#  Точка входа: только выбор режима
# ----------------------------------------
if __name__ == '__main__':
    print("=== Парсер форума prodota.ru ===")

    # Определяем режим
    mode = DEFAULT_MODE
    if mode is None:
        print("Выберите режим работы:")
        print("1 - Только текст (сохранить в JSON/CSV)")
        print("2 - Полный парсинг (скачать вложения, создать HTML-архив)")
        mode = input("Введите 1 или 2: ").strip()
        while mode not in ('1', '2'):
            print("Ошибка: введите 1 или 2")
            mode = input("Введите 1 или 2: ").strip()
        mode = int(mode)
    else:
        print(f"Используем режим {mode} (указан в настройках)")

    # Проверяем, что START_URL и FOLDER_NAME заданы
    if not START_URL:
        print("ОШИБКА: переменная START_URL не задана. Отредактируйте настройки в начале кода.")
        exit(1)
    if not FOLDER_NAME:
        print("ОШИБКА: переменная FOLDER_NAME не задана. Отредактируйте настройки в начале кода.")
        exit(1)

    # Создаём полный путь к папке
    full_path = os.path.join(DEFAULT_OUTPUT_DIR, FOLDER_NAME)
    os.makedirs(full_path, exist_ok=True)

    # Запуск в зависимости от режима
    if mode == 1:
        print("\n=== Режим: только текст ===")
        parser = TextParser(START_URL, full_path, format=TEXT_FORMAT)
        parser.run()
    else:
        print("\n=== Режим: полный парсинг с вложениями ===")
        archiver = FullArchiver(START_URL, full_path)
        archiver.run()

    print("\nГотово! Файлы сохранены в папку:", full_path)

 

Вот, что выдала нейронка:

 

Привет! Пожалуйста составь подробный психологический портрет автора темы. Ник yellyex. Я скачал все посты с сайта, где он упоминается в один файл.

 

Ответ:

Конечно, вот подробный психологический портрет автора темы (ник yellyex), составленный на основе анализа предоставленных сообщений.

 

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

 

---

 

Психологический портрет yellyex (Елекс)

 

1. Центральный конфликт: Инфантилизм и Эскапизм

 

Главная движущая сила персонажа — это глубокое желание эскапизма, побега от реальности. Он постоянно подчеркивает свое отвращение к "городу", "обществу" и "миру денег". Однако его эскапизм не является следствием зрелого выбора, а проистекает из инфантильной позиции.

 

Он не стремится построить новую жизнь, а пытается вернуться в "детство", описывая идиллическую картину: «Ты словно маленький бегаешь босиком на лужайке перед домом в одних трусиках и радуешься солнышку». Это регрессивное желание, где все заботы о нем решает кто-то другой (сначала мама, затем — форум).

 

2. Личностные черты и когнитивные особенности

 

· Амбивалентность и неспособность к действию: Он генерирует множество планов (землянка, квартира, работа в пятерочке), которые постоянно меняются и откладываются. Каждый раз, когда требуется приступить к действию (пойти копать землянку, устроиться на работу), он находит причину для отсрочки: «Я спать лег. Отдых до 2 ноября». Его действия ограничиваются приготовлениями (покупка черенка для лопаты), а не самой стройкой. Это классическая прокрастинация, подкрепленная внешним локусом контроля (он ищет причину неудач во внешних обстоятельствах, а не в себе).

· Инфантильная финансовая зависимость: Для него естественно просить деньги у матери («Мамка может дать»), а после конфликта с ней — перенаправить запрос на аудиторию форума. Он искренне не видит в этом проблемы, аргументируя это тем, что зрители «покупают шмотки в кс и доте». Он не воспринимает свой "проект" как труд, за который нужно платить, а как повод для донатов. Даже когда он говорит «строю бесплатно», он тут же добавляет ссылку на донаты.

· Магическое мышление и фантазийность: Его планы не имеют ничего общего с реальностью. Он собирается построить дом за 5 дней, используя сырой валежник, камни из реки и бензопилу без опыта работы с ней. Он верит, что сможет законно присвоить себе дом в заброшенной деревне, сказав, что это «дом моего прадеда». Восприятие времени и ресурсов искажено. Он искренне верит в свои грандиозные планы, что указывает на сложности с реальностью.

· Низкая фрустрационная толерантность: Любое физическое или психологическое препятствие вызывает у него сильное расстройство и желание "отдохнуть". Распиленное бревно (один ствол) — это уже "рабочий день", после которого он уходит отдыхать. Отказ матери в деньгах или критика на форуме вызывают вспышки гнева и обиды.

 

3. Коммуникация и социальное взаимодействие

 

· Проекция и обвинение: Он проецирует свою лень и неспособность на других. Когда ему указывают на нереалистичность планов, он обвиняет критиков в том, что они «долбоебы», которые не могут вникнуть в его ситуацию. Его защитная реакция — агрессия и обесценивание («Идите нахуй все хейтеры!»).

· Поиск "идеальной" аудитории: Он хочет получать донаты и восхищение, но не хочет принимать конструктивную критику или выполнять обещанное. Он пытается выстроить нарратив "зритель платит за контент", но сам не способен этот контент произвести. Он ждет, что аудитория будет платить ему авансом за его "энтузиазм".

· Искажение реальности (склонность к псевдологии): В ходе темы он несколько раз рассказывает истории, которые с высокой долей вероятности являются выдумкой или сильным преувеличением (приход ментов с изъятием бензопилы и снятием отпечатков пальцев). Эта история становится для него способом легитимно "отменить" строительство землянки, не беря на себя ответственность за неудачу. Он не просто врет, он верит в эту историю, что подтверждает его отрыв от реальности.

 

4. Диагностические гипотезы

 

С большой вероятностью можно предположить наличие следующих особенностей:

 

· Расстройство личности (возможно, шизоидного или зависимого типа): Социальная изоляция, странность мышления, эмоциональная холодность (сменяющаяся вспышками гнева), крайняя зависимость от матери и, как следствие, от мнения "значимых других" (аудитории форума).

· Астенический невроз / расстройство: Он сам упоминает это. Постоянная усталость, неспособность выдерживать длительные нагрузки, потребность в длительном отдыхе после минимальных усилий — ключевые симптомы. Его знаменитое «Я спать пошел» — это квинтэссенция этого состояния.

· Инфантилизм: Сочетание эмоциональной незрелости, зависимости, гедонистических установок и отсутствия ответственности за свои действия и жизнь в целом.

· Высокий уровень нарциссической защиты: Его претензия на уникальность, неприятие критики, требование к аудитории воспринимать его как "художника" и спонсировать его "творчество" без доказательств.

 

5. Динамика и развитие персонажа

 

Персонаж практически не развивается на протяжении всего топика. Он проживает один и тот же цикл снова и снова:

 

1. Грандиозный план: Объявляет о начале строительства землянки.

2. Столкновение с реальностью: Сталкивается с первой же трудностью (дождь, нет транспорта, холод).

3. Регрессия и требования: Обращается за деньгами к маме или форуму, аргументируя это тем, что без денег контент невозможен.

4. Отдых/Избегание: Вместо стройки выбирает отдых или придумывает отвлекающую активность (поездку за квартирой).

5. Рационализация и обесценивание: Объясняет неудачу внешними факторами (менты, зима, мама), обесценивает свой прошлый план ("это была временная землянка") и начинает новый цикл.

 

Заключение

 

Yellyex — это трагикомический образ человека, застывшего в инфантильной фазе развития. Его попытка стать "выживальщиком" — это не акт взросления, а его отрицание, попытка обрести "детство" не через преодоление трудностей, а через бегство от них. Форум для него становится не местом для отчета, а заменой родительской фигуре, которая должна обеспечить ему безопасность (деньгами и вниманием), пока он будет играть в свою "взрослую" игру.

 

Он не просто не способен построить землянку; он не способен выйти из замкнутого круга собственных фантазий. Его уникальность заключается в том, что он стал идеальным "зеркалом" для форумного сообщества, которое в его патологическом поведении увидело развлекательный контент.

E1azor и Langusto понравилось это

moonfangtopich написал 29.08.2019 в 14:57:
У вас недостаточно широкий кругозор, пацаны

Я странствия этого еблана видел в покерных топанах, а потом в таверне - это один из самых безумных людей на форуме. Я искренне надеялся, что его зов о помощи останется незамеченным, но нет, нашелся доброволец и вот уже три страницы мы пожинаем плоды

Поделиться сообщением


Ссылка на сообщение

Я пока читал несколько раз проплакался от смеха.

Надо топики русверса также прогнать. Где он там обитает? 🤣🤣🤣

 

 

Запрос:

Нужен полный диагноз. Может он шизик и опасен для общества.

 

Ответ:

Поскольку я — искусственный интеллект, а не психиатр, я не могу поставить клинический диагноз. Это может сделать только врач после личной беседы и обследования. Однако на основе анализа предоставленных данных я могу описать наблюдаемые психопатологические феномены и дать экспертную оценку возможных рисков.

 

---

 

Анализ наблюдаемых признаков

 

1. Отсутствие признаков психотического распада

 

Вывод: Нет оснований говорить о шизофрении или бредовом расстройстве в классическом понимании.

 

У yellyex отсутствуют ключевые признаки психоза:

 

· Нет галлюцинаций. Он не утверждает, что слышит голоса или видит то, чего нет.

· Нет систематизированного бреда. Его идеи (построить землянку, подключиться к столбу) — это фантазии, основанные на незнании реальности и инфантильном мышлении, а не на бредовых убеждениях, которым он слепо следует. Он способен менять планы, реагировать на критику (пусть и агрессивно), что нехарактерно для больного с бредом.

· Сохранена ориентировка. Он понимает, где находится, кто он, какой сейчас год.

· Эпизоды «искажения реальности» (история с ментами) больше похожи на псевдологию (патологическую ложь) с целью избежать ответственности за провал, а не на бред.

 

2. Структура личности: Расстройство личности с пограничными чертами и зависимостью

 

Наиболее вероятная диагностическая гипотеза — это смешанное расстройство личности с доминированием зависимых, шизоидных и нарциссических черт.

 

· Зависимое расстройство личности (F60.7):

  · Неспособность принимать решения без чрезмерных советов и поддержки (постоянное согласование планов с форумом).

  · Пассивная позиция в финансовых вопросах (ожидание денег от мамы и донатов).

  · Поиск «опекающей» фигуры (сначала мать, потом форум).

· Шизоидное расстройство личности (F60.1):

  · Предпочтение уединенной деятельности.

  · Отсутствие близких дружеских и романтических связей (сам признается, что секса с женщиной не было и не хочет).

  · Эмоциональная холодность и отстраненность, сменяющаяся вспышками гнева.

  · Ограниченная способность выражать чувства.

· Нарциссические черты (не полное расстройство):

  · Грандиозность планов при отсутствии достижений.

  · Ожидание особого отношения (дотаты, внимание форума без предоставления реального контента).

  · Непереносимость критики с гневными реакциями обесценивания («идите нахуй»).

 

3. Невротический уровень функционирования

 

· Астенический невроз (синдром): Постоянная усталость, неспособность выдерживать физическую и психологическую нагрузку, потребность в длительном отдыхе после минимальных усилий — центральная проблема, которую он сам осознает.

· Инфантилизм: Ядро его личности. Сочетание интеллектуальной сохранности (способен строить сложные планы, писать код) с полной эмоциональной и социальной незрелостью. Он играет во «взрослого выживальщика», как ребенок играет в войнушку, но искренне верит в свою игру.

 

---

 

Оценка опасности для общества

 

Низкая. Он не опасен для окружающих.

 

1. Отсутствие агрессивных импульсов. Его гнев — вербальный, защитный. Он не угрожает насилием, не описывает фантазий насилия над людьми (история с ментами — это история о нем как о жертве, а не агрессоре).

2. Нет параноидной направленности. Он не подозревает окружающих в заговорах против него, не ищет врагов.

3. Его действия направлены на самоизоляцию, а не на вторжение в чужую жизнь. Максимум, что ему грозит — это административное правонарушение (незаконное подключение к электросетям, незаконная вырубка леса), но не уголовные преступления против личности.

 

Высокий риск аутодеструктивного поведения (опасен для себя).

 

· Высокая вероятность травматизма. Работа с бензопилой без опыта, строительство без навыков, нахождение в лесу в одиночестве без связи и запаса еды.

· Риск переохлаждения, ожогов, отравления угарным газом. Его планы печей из камней и глины без дымохода — это прямой путь к летальному исходу.

· Риск суицидального поведения на фоне краха идеалов. Если его очередная попытка закончится полным провалом, он может впасть в тяжелую депрессию. Его навязчивая идея «уйти в лес» может трансформироваться в пассивное желание «не вернуться».

 

---

 

Заключение

 

Yellyex не является «шизиком» в бытовом смысле. У него нет психоза, он не опасен для окружающих. Его состояние укладывается в рамки сложного расстройства личности (вероятно, смешанного, с преобладанием зависимых и шизоидных черт) на невротическом уровне, осложненного инфантилизмом и астеническим синдромом.

 

Главная угроза, которую он представляет, — это угроза самому себе. Его эскапады в лес — это не преступный умысел, а способ саморазрушения через некомпетентность.

 

Рекомендации:

 

· Не нуждается в изоляции или принудительном лечении.

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

· Близким (матери) рекомендуется прекратить финансовое поощрение его инфантильных проектов и мягко, но настойчиво направлять его к психиатру и психотерапевту для плановой работы, а не для «спасения» после очередной неудачной вылазки.

NaxrenApm понравилось это

moonfangtopich написал 29.08.2019 в 14:57:
У вас недостаточно широкий кругозор, пацаны

Я странствия этого еблана видел в покерных топанах, а потом в таверне - это один из самых безумных людей на форуме. Я искренне надеялся, что его зов о помощи останется незамеченным, но нет, нашелся доброволец и вот уже три страницы мы пожинаем плоды

Поделиться сообщением


Ссылка на сообщение
yellyex написал 13 минут назад:

секса с женщиной не было и не хочет

Единственный признак, почему елекса можно считать здоровым человеком

Поделиться сообщением


Ссылка на сообщение

Сейчас я скормлю нейронке тему Отдыха. Пусть ознакомится и дополнит психологический портрет.

 

https://prodota.ru/forum/topic/212849/?ysclid=mngd89mxix985021776


moonfangtopich написал 29.08.2019 в 14:57:
У вас недостаточно широкий кругозор, пацаны

Я странствия этого еблана видел в покерных топанах, а потом в таверне - это один из самых безумных людей на форуме. Я искренне надеялся, что его зов о помощи останется незамеченным, но нет, нашелся доброволец и вот уже три страницы мы пожинаем плоды

Поделиться сообщением


Ссылка на сообщение

Мла, я бы не отказался посмотреть, как это чудо полезло бы на столб "чето там подключать"

 


2.) алоха - гремлин ебаный, с него только ржать можно. Если бы не был олигофреном, то мог капитанить, проталкивать свои идеи и результат был бы лучше.

Поделиться сообщением


Ссылка на сообщение
insigniA написал 13 минут назад:

Мла, я бы не отказался посмотреть, как это чудо полезло бы на столб "чето там подключать"

 

15к и можно будет посмотреть.


moonfangtopich написал 29.08.2019 в 14:57:
У вас недостаточно широкий кругозор, пацаны

Я странствия этого еблана видел в покерных топанах, а потом в таверне - это один из самых безумных людей на форуме. Я искренне надеялся, что его зов о помощи останется незамеченным, но нет, нашелся доброволец и вот уже три страницы мы пожинаем плоды

Поделиться сообщением


Ссылка на сообщение
(изменено)

del. сори


Изменено пользователем Qreeq

Поделиться сообщением


Ссылка на сообщение

Закинь этой своей проге все три полит топика, целиком. Пусть найдет в них самых отбитых шизофреника.  Хотя думаю может перегореть и загючить

bitrix, Methamphetamine, Herokills и 1 другому понравилось это

Поделиться сообщением


Ссылка на сообщение
(изменено)
yellyex написал 1 час назад:

 

 

 

С помощью этого парсера, я выцепил все посты в текстовом виде. Теперь я попрошу нейронку составить подробный психологический портрет автора темы.

 


# -*- coding: utf-8 -*-
"""
Парсер форума prodota.ru с выбором режима:
- Режим 1 (только текст): сохраняет сообщения (автор, номер, текст) в JSON или CSV.
- Режим 2 (полный парсинг): скачивает все вложения (картинки, видео, документы) и создаёт
  локальную HTML-копию темы, где вложения встроены или доступны по ссылкам.

ДЛЯ НЕПРОГРАММИСТОВ:
- Всё, что нужно настроить, находится в самом начале (раздел "НАСТРОЙКИ").
- Укажите START_URL, FOLDER_NAME, DEFAULT_OUTPUT_DIR и другие параметры.
- При запуске программа попросит выбрать режим (1 или 2) – больше ничего вводить не нужно.
- Если вы хотите, чтобы режим выбирался автоматически (без вопроса), установите DEFAULT_MODE = 1 или 2.
"""

import os
import re
import time
import json
import csv
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse, unquote
from pathlib import Path

# ========================== НАСТРОЙКИ (изменяйте здесь) ==========================
# Режим по умолчанию (если оставить None, будет запрошен при запуске)
# 1 - только текст, 2 - полный парсинг с вложениями
DEFAULT_MODE = None   # или 1, 2

# Ссылка на первую страницу темы (обязательно укажите!)
START_URL = 'https://prodota.ru/forum/topic/224160/'   # <-- ЗАМЕНИТЕ НА ВАШУ ТЕМУ

# Имя папки, куда будет сохранён архив (без пробелов, латиница и цифры)
FOLDER_NAME = 'prodota_archive'   # <-- МОЖЕТЕ ИЗМЕНИТЬ

# Папка, в которую будут сохраняться все архивы (по умолчанию – папка Download на Android)
DEFAULT_OUTPUT_DIR = "/storage/emulated/0/Download"

# Задержка между запросами к страницам (секунды) – чтобы не перегружать сервер
DELAY = 1.5

# Таймаут для запроса (секунды)
TIMEOUT = 20

# Для текстового режима: формат сохранения ('json' или 'csv')
TEXT_FORMAT = 'json'   # можно изменить на 'csv'

# Расширения файлов для полного режима (определяют тип вложения)
IMAGE_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg')
VIDEO_EXTENSIONS = ('.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv')
DOC_EXTENSIONS = ('.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.txt', '.zip', '.rar', '.7z')
ATTACH_EXTENSIONS = IMAGE_EXTENSIONS + VIDEO_EXTENSIONS + DOC_EXTENSIONS
# =================================================================================


# ----------------------------------------
#  Базовый класс с общими методами (загрузка страниц, парсинг постов)
# ----------------------------------------
class BaseParser:
    def __init__(self, start_url, output_dir, delay=DELAY, timeout=TIMEOUT):
        self.start_url = start_url
        self.output_dir = Path(output_dir)
        self.delay = delay
        self.timeout = timeout
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
        })
        self.posts = []
        self.seen_posts = set()

    def get_soup(self, url):
        try:
            response = self.session.get(url, timeout=self.timeout)
            response.raise_for_status()
            if response.encoding is None:
                response.encoding = 'utf-8'
            return BeautifulSoup(response.text, 'html.parser')
        except Exception as e:
            print(f"Ошибка загрузки {url}: {e}")
            return None

    def extract_post_data(self, post_element, base_url):
        """Извлекает автора, номер поста и текст (без обработки вложений)."""
        author_elem = post_element.select_one('.cAuthorPane_author, .ipsComment_author, .author')
        if not author_elem:
            return None
        author = author_elem.get_text(strip=True)

        post_id = None
        if post_element.get('id'):
            match = re.search(r'(\d+)$', post_element.get('id'))
            if match:
                post_id = match.group(1)
        if not post_id:
            id_link = post_element.select_one('a[data-commentid]')
            if id_link and id_link.get('data-commentid'):
                post_id = id_link['data-commentid']
        if not post_id:
            post_id = 'N/A'

        content_elem = post_element.select_one('.cPost_contentWrap, .ipsComment_content, .post-content')
        if not content_elem:
            return None
        text = content_elem.get_text(separator='\n', strip=True)

        return {
            'author': author,
            'post_number': post_id,
            'content': text
        }

    def parse_page(self, url):
        soup = self.get_soup(url)
        if not soup:
            return None

        post_blocks = soup.select('li.ipsComment, div.cPost, article.ipsComment, div.post')
        if not post_blocks:
            print("   Не найдено сообщений на странице. Проверьте структуру сайта.")
            return None

        for block in post_blocks:
            post_data = self.extract_post_data(block, url)
            if post_data and post_data['post_number'] not in self.seen_posts:
                self.seen_posts.add(post_data['post_number'])
                self.posts.append(post_data)

        next_link = None
        next_elem = soup.select_one('a[rel="next"]')
        if next_elem and next_elem.get('href'):
            next_link = urljoin(url, next_elem['href'])
        if not next_link:
            pagination_next = soup.select_one('.ipsPagination_next a, .next a')
            if pagination_next and pagination_next.get('href'):
                next_link = urljoin(url, pagination_next['href'])

        return next_link

    def run(self):
        print(f"Начинаем парсинг: {self.start_url}")
        print(f"Папка для сохранения: {self.output_dir.absolute()}")
        current_url = self.start_url
        page_num = 1
        while current_url:
            print(f"Страница {page_num}: {current_url}")
            next_url = self.parse_page(current_url)
            if next_url and next_url != current_url:
                current_url = next_url
                page_num += 1
                time.sleep(self.delay)
            else:
                break
        print(f"\nСобрано сообщений: {len(self.posts)}")


# ----------------------------------------
#  Режим 1: только текст, сохраняет в JSON/CSV
# ----------------------------------------
class TextParser(BaseParser):
    def __init__(self, start_url, output_dir, format='json'):
        super().__init__(start_url, output_dir)
        self.format = format.lower()

    def save(self):
        if self.format == 'json':
            output_file = self.output_dir / 'posts.json'
            with open(output_file, 'w', encoding='utf-8') as f:
                json.dump(self.posts, f, ensure_ascii=False, indent=2)
            print(f"Данные сохранены в {output_file}")
        elif self.format == 'csv':
            output_file = self.output_dir / 'posts.csv'
            with open(output_file, 'w', newline='', encoding='utf-8-sig') as f:
                writer = csv.DictWriter(f, fieldnames=['author', 'post_number', 'content'])
                writer.writeheader()
                writer.writerows(self.posts)
            print(f"Данные сохранены в {output_file}")
        else:
            print(f"Неизвестный формат {self.format}, данные не сохранены.")

    def run(self):
        super().run()
        self.save()


# ----------------------------------------
#  Режим 2: полный парсинг с вложениями и HTML-архивом
# ----------------------------------------
class FullArchiver(BaseParser):
    def __init__(self, start_url, output_dir):
        super().__init__(start_url, output_dir)
        self.attachment_counter = 0
        self.local_attachments = {}
        # Создаём папки для вложений
        self.attachments_dir = self.output_dir / 'attachments'
        self.images_dir = self.attachments_dir / 'images'
        self.videos_dir = self.attachments_dir / 'videos'
        self.docs_dir = self.attachments_dir / 'docs'
        for d in [self.attachments_dir, self.images_dir, self.videos_dir, self.docs_dir]:
            d.mkdir(parents=True, exist_ok=True)

    def download_file(self, url, subdir):
        parsed = urlparse(url)
        filename = os.path.basename(unquote(parsed.path))
        if not filename or '.' not in filename:
            self.attachment_counter += 1
            try:
                head = self.session.head(url, timeout=5, allow_redirects=True)
                content_type = head.headers.get('content-type', '')
                ext = self.guess_extension_by_mime(content_type)
                filename = f"file_{self.attachment_counter}{ext}"
            except:
                filename = f"file_{self.attachment_counter}.bin"
        else:
            filename = filename.split('?')[0]

        base, ext = os.path.splitext(filename)
        counter = 1
        final_filename = filename
        while (subdir / final_filename).exists():
            final_filename = f"{base}_{counter}{ext}"
            counter += 1

        local_path = subdir / final_filename
        rel_path = local_path.relative_to(self.output_dir).as_posix()

        try:
            with self.session.get(url, stream=True, timeout=self.timeout) as r:
                r.raise_for_status()
                with open(local_path, 'wb') as f:
                    for chunk in r.iter_content(chunk_size=8192):
                        f.write(chunk)
            print(f"  Скачан файл: {rel_path}")
            return rel_path
        except Exception as e:
            print(f"  Ошибка скачивания {url}: {e}")
            return None

    def guess_extension_by_mime(self, mime):
        mime = mime.lower()
        if 'image/jpeg' in mime:
            return '.jpg'
        if 'image/png' in mime:
            return '.png'
        if 'image/gif' in mime:
            return '.gif'
        if 'video/mp4' in mime:
            return '.mp4'
        if 'application/pdf' in mime:
            return '.pdf'
        return '.bin'

    def is_attachment_url(self, url):
        if not url:
            return False
        if url.startswith('#') or url.startswith('javascript:'):
            return False
        path = urlparse(url).path.lower()
        if any(path.endswith(ext) for ext in ATTACH_EXTENSIONS):
            return True
        if '/uploads/' in path or '/monthly_' in path or 'attachments' in path:
            return True
        return False

    def process_attachments_in_html(self, html_content, base_url):
        if not html_content:
            return html_content

        soup = BeautifulSoup(html_content, 'html.parser')

        for a_tag in soup.find_all('a', href=True):
            href = a_tag['href']
            absolute_url = urljoin(base_url, href)
            if self.is_attachment_url(absolute_url):
                if absolute_url in self.local_attachments:
                    local_path = self.local_attachments[absolute_url]
                else:
                    ext = os.path.splitext(urlparse(absolute_url).path)[1].lower()
                    if ext in IMAGE_EXTENSIONS:
                        subdir = self.images_dir
                    elif ext in VIDEO_EXTENSIONS:
                        subdir = self.videos_dir
                    else:
                        subdir = self.docs_dir
                    local_path = self.download_file(absolute_url, subdir)
                    if local_path:
                        self.local_attachments[absolute_url] = local_path
                    else:
                        continue

                a_tag['href'] = local_path
                if ext in IMAGE_EXTENSIONS:
                    img_tag = soup.new_tag('img', src=local_path, alt=local_path, style="max-width:100%;")
                    a_tag.replace_with(img_tag)
                elif ext in VIDEO_EXTENSIONS:
                    video_tag = soup.new_tag('video', controls=True, style="max-width:100%;")
                    source_tag = soup.new_tag('source', src=local_path, type='video/mp4')
                    video_tag.append(source_tag)
                    a_tag.replace_with(video_tag)

        for img_tag in soup.find_all('img', src=True):
            src = img_tag['src']
            absolute_url = urljoin(base_url, src)
            if self.is_attachment_url(absolute_url):
                if absolute_url in self.local_attachments:
                    local_path = self.local_attachments[absolute_url]
                else:
                    local_path = self.download_file(absolute_url, self.images_dir)
                    if local_path:
                        self.local_attachments[absolute_url] = local_path
                    else:
                        continue
                img_tag['src'] = local_path

        return str(soup)

    def extract_post_data(self, post_element, base_url):
        """Переопределяем метод для обработки вложений в HTML."""
        author_elem = post_element.select_one('.cAuthorPane_author, .ipsComment_author, .author')
        if not author_elem:
            return None
        author = author_elem.get_text(strip=True)

        post_id = None
        if post_element.get('id'):
            match = re.search(r'(\d+)$', post_element.get('id'))
            if match:
                post_id = match.group(1)
        if not post_id:
            id_link = post_element.select_one('a[data-commentid]')
            if id_link and id_link.get('data-commentid'):
                post_id = id_link['data-commentid']
        if not post_id:
            post_id = 'N/A'

        content_elem = post_element.select_one('.cPost_contentWrap, .ipsComment_content, .post-content')
        if not content_elem:
            return None

        inner_html = str(content_elem)
        processed_html = self.process_attachments_in_html(inner_html, base_url)

        return {
            'author': author,
            'post_number': post_id,
            'html': processed_html
        }

    def generate_html(self):
        html_template = """<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Архив темы: {title}</title>
    <style>
        body {{
            font-family: Arial, sans-serif;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
            background: #f5f5f5;
        }}
        .post {{
            background: white;
            border: 1px solid #ddd;
            border-radius: 5px;
            margin-bottom: 20px;
            padding: 15px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }}
        .post-header {{
            border-bottom: 1px solid #eee;
            padding-bottom: 10px;
            margin-bottom: 10px;
            font-size: 0.9em;
            color: #666;
        }}
        .post-author {{
            font-weight: bold;
            color: #333;
        }}
        .post-id {{
            color: #999;
            margin-left: 10px;
        }}
        .post-content {{
            line-height: 1.4;
        }}
        img, video {{
            max-width: 100%;
            height: auto;
            margin: 10px 0;
        }}
    </style>
</head>
<body>
    <h1>Архив темы: {title}</h1>
    <p>Всего сообщений: {total_posts}</p>
    <hr>
    {posts_html}
</body>
</html>
"""
        posts_html = []
        for p in self.posts:
            posts_html.append(f"""
            <div class="post">
                <div class="post-header">
                    <span class="post-author">{p['author']}</span>
                    <span class="post-id">Пост #{p['post_number']}</span>
                </div>
                <div class="post-content">
                    {p['html']}
                </div>
            </div>
            """)

        title = "Тема с prodota.ru"
        final_html = html_template.format(
            title=title,
            total_posts=len(self.posts),
            posts_html='\n'.join(posts_html)
        )
        return final_html

    def run(self):
        super().run()
        print("Генерация HTML-страницы...")
        html_content = self.generate_html()
        index_path = self.output_dir / 'index.html'
        with open(index_path, 'w', encoding='utf-8') as f:
            f.write(html_content)
        print(f"HTML-страница сохранена: {index_path.absolute()}")


# ----------------------------------------
#  Точка входа: только выбор режима
# ----------------------------------------
if __name__ == '__main__':
    print("=== Парсер форума prodota.ru ===")

    # Определяем режим
    mode = DEFAULT_MODE
    if mode is None:
        print("Выберите режим работы:")
        print("1 - Только текст (сохранить в JSON/CSV)")
        print("2 - Полный парсинг (скачать вложения, создать HTML-архив)")
        mode = input("Введите 1 или 2: ").strip()
        while mode not in ('1', '2'):
            print("Ошибка: введите 1 или 2")
            mode = input("Введите 1 или 2: ").strip()
        mode = int(mode)
    else:
        print(f"Используем режим {mode} (указан в настройках)")

    # Проверяем, что START_URL и FOLDER_NAME заданы
    if not START_URL:
        print("ОШИБКА: переменная START_URL не задана. Отредактируйте настройки в начале кода.")
        exit(1)
    if not FOLDER_NAME:
        print("ОШИБКА: переменная FOLDER_NAME не задана. Отредактируйте настройки в начале кода.")
        exit(1)

    # Создаём полный путь к папке
    full_path = os.path.join(DEFAULT_OUTPUT_DIR, FOLDER_NAME)
    os.makedirs(full_path, exist_ok=True)

    # Запуск в зависимости от режима
    if mode == 1:
        print("\n=== Режим: только текст ===")
        parser = TextParser(START_URL, full_path, format=TEXT_FORMAT)
        parser.run()
    else:
        print("\n=== Режим: полный парсинг с вложениями ===")
        archiver = FullArchiver(START_URL, full_path)
        archiver.run()

    print("\nГотово! Файлы сохранены в папку:", full_path)

 

Вот, что выдала нейронка:

 

Привет! Пожалуйста составь подробный психологический портрет автора темы. Ник yellyex. Я скачал все посты с сайта, где он упоминается в один файл.

 

Ответ:

Конечно, вот подробный психологический портрет автора темы (ник yellyex), составленный на основе анализа предоставленных сообщений.

 

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

 

---

 

Психологический портрет yellyex (Елекс)

 

1. Центральный конфликт: Инфантилизм и Эскапизм

 

Главная движущая сила персонажа — это глубокое желание эскапизма, побега от реальности. Он постоянно подчеркивает свое отвращение к "городу", "обществу" и "миру денег". Однако его эскапизм не является следствием зрелого выбора, а проистекает из инфантильной позиции.

 

Он не стремится построить новую жизнь, а пытается вернуться в "детство", описывая идиллическую картину: «Ты словно маленький бегаешь босиком на лужайке перед домом в одних трусиках и радуешься солнышку». Это регрессивное желание, где все заботы о нем решает кто-то другой (сначала мама, затем — форум).

 

2. Личностные черты и когнитивные особенности

 

· Амбивалентность и неспособность к действию: Он генерирует множество планов (землянка, квартира, работа в пятерочке), которые постоянно меняются и откладываются. Каждый раз, когда требуется приступить к действию (пойти копать землянку, устроиться на работу), он находит причину для отсрочки: «Я спать лег. Отдых до 2 ноября». Его действия ограничиваются приготовлениями (покупка черенка для лопаты), а не самой стройкой. Это классическая прокрастинация, подкрепленная внешним локусом контроля (он ищет причину неудач во внешних обстоятельствах, а не в себе).

· Инфантильная финансовая зависимость: Для него естественно просить деньги у матери («Мамка может дать»), а после конфликта с ней — перенаправить запрос на аудиторию форума. Он искренне не видит в этом проблемы, аргументируя это тем, что зрители «покупают шмотки в кс и доте». Он не воспринимает свой "проект" как труд, за который нужно платить, а как повод для донатов. Даже когда он говорит «строю бесплатно», он тут же добавляет ссылку на донаты.

· Магическое мышление и фантазийность: Его планы не имеют ничего общего с реальностью. Он собирается построить дом за 5 дней, используя сырой валежник, камни из реки и бензопилу без опыта работы с ней. Он верит, что сможет законно присвоить себе дом в заброшенной деревне, сказав, что это «дом моего прадеда». Восприятие времени и ресурсов искажено. Он искренне верит в свои грандиозные планы, что указывает на сложности с реальностью.

· Низкая фрустрационная толерантность: Любое физическое или психологическое препятствие вызывает у него сильное расстройство и желание "отдохнуть". Распиленное бревно (один ствол) — это уже "рабочий день", после которого он уходит отдыхать. Отказ матери в деньгах или критика на форуме вызывают вспышки гнева и обиды.

 

3. Коммуникация и социальное взаимодействие

 

· Проекция и обвинение: Он проецирует свою лень и неспособность на других. Когда ему указывают на нереалистичность планов, он обвиняет критиков в том, что они «долбоебы», которые не могут вникнуть в его ситуацию. Его защитная реакция — агрессия и обесценивание («Идите нахуй все хейтеры!»).

· Поиск "идеальной" аудитории: Он хочет получать донаты и восхищение, но не хочет принимать конструктивную критику или выполнять обещанное. Он пытается выстроить нарратив "зритель платит за контент", но сам не способен этот контент произвести. Он ждет, что аудитория будет платить ему авансом за его "энтузиазм".

· Искажение реальности (склонность к псевдологии): В ходе темы он несколько раз рассказывает истории, которые с высокой долей вероятности являются выдумкой или сильным преувеличением (приход ментов с изъятием бензопилы и снятием отпечатков пальцев). Эта история становится для него способом легитимно "отменить" строительство землянки, не беря на себя ответственность за неудачу. Он не просто врет, он верит в эту историю, что подтверждает его отрыв от реальности.

 

4. Диагностические гипотезы

 

С большой вероятностью можно предположить наличие следующих особенностей:

 

· Расстройство личности (возможно, шизоидного или зависимого типа): Социальная изоляция, странность мышления, эмоциональная холодность (сменяющаяся вспышками гнева), крайняя зависимость от матери и, как следствие, от мнения "значимых других" (аудитории форума).

· Астенический невроз / расстройство: Он сам упоминает это. Постоянная усталость, неспособность выдерживать длительные нагрузки, потребность в длительном отдыхе после минимальных усилий — ключевые симптомы. Его знаменитое «Я спать пошел» — это квинтэссенция этого состояния.

· Инфантилизм: Сочетание эмоциональной незрелости, зависимости, гедонистических установок и отсутствия ответственности за свои действия и жизнь в целом.

· Высокий уровень нарциссической защиты: Его претензия на уникальность, неприятие критики, требование к аудитории воспринимать его как "художника" и спонсировать его "творчество" без доказательств.

 

5. Динамика и развитие персонажа

 

Персонаж практически не развивается на протяжении всего топика. Он проживает один и тот же цикл снова и снова:

 

1. Грандиозный план: Объявляет о начале строительства землянки.

2. Столкновение с реальностью: Сталкивается с первой же трудностью (дождь, нет транспорта, холод).

3. Регрессия и требования: Обращается за деньгами к маме или форуму, аргументируя это тем, что без денег контент невозможен.

4. Отдых/Избегание: Вместо стройки выбирает отдых или придумывает отвлекающую активность (поездку за квартирой).

5. Рационализация и обесценивание: Объясняет неудачу внешними факторами (менты, зима, мама), обесценивает свой прошлый план ("это была временная землянка") и начинает новый цикл.

 

Заключение

 

Yellyex — это трагикомический образ человека, застывшего в инфантильной фазе развития. Его попытка стать "выживальщиком" — это не акт взросления, а его отрицание, попытка обрести "детство" не через преодоление трудностей, а через бегство от них. Форум для него становится не местом для отчета, а заменой родительской фигуре, которая должна обеспечить ему безопасность (деньгами и вниманием), пока он будет играть в свою "взрослую" игру.

 

Он не просто не способен построить землянку; он не способен выйти из замкнутого круга собственных фантазий. Его уникальность заключается в том, что он стал идеальным "зеркалом" для форумного сообщества, которое в его патологическом поведении увидело развлекательный контент.

Чел, я тебе выше всё это доступно описал.

Я лучше нейронки получается


Изменено пользователем Ratio

Поделиться сообщением


Ссылка на сообщение
Gomosek написал 6 минут назад:

Закинь этой своей проге все три полит топика, целиком. Пусть найдет в них самых отбитых шизофреника.  Хотя думаю может перегореть и загючить

Сейчас доделаю парсер, чтобы он мог разбить файл на части и сам можешь попробовать.


moonfangtopich написал 29.08.2019 в 14:57:
У вас недостаточно широкий кругозор, пацаны

Я странствия этого еблана видел в покерных топанах, а потом в таверне - это один из самых безумных людей на форуме. Я искренне надеялся, что его зов о помощи останется незамеченным, но нет, нашелся доброволец и вот уже три страницы мы пожинаем плоды

Поделиться сообщением


Ссылка на сообщение
Qreeq написал 7 минут назад:

Еще раз цифру 15к назовешь, я лично приеду тебе ебало бить долбоебу

Его там лунтик хуем наказывать собирался. Чем закончилось ?

Поделиться сообщением


Ссылка на сообщение

Обновлённый код:

Скрытый текст

# -*- coding: utf-8 -*-
"""
Парсер форума prodota.ru с выбором режима:
- Режим 1 (только текст): сохраняет сообщения (автор, номер, текст) в JSON или CSV.
  Поддерживает разбивку на части заданного размера (в МБ).
- Режим 2 (полный парсинг): скачивает все вложения (картинки, видео, документы) и создаёт
  локальную HTML-копию темы.

ДЛЯ НЕПРОГРАММИСТОВ:
- Все настройки (URL, имя папки, путь сохранения и т.д.) находятся в разделе "НАСТРОЙКИ" в начале кода.
- При запуске программа спросит режим (1 или 2). Если выбран режим 1, дополнительно спросит,
  нужно ли разбить результат на части и какой максимальный размер части в мегабайтах.
- Если вы хотите, чтобы режим выбирался автоматически, установите DEFAULT_MODE = 1 или 2.
"""

import os
import re
import time
import json
import csv
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse, unquote
from pathlib import Path

# ========================== НАСТРОЙКИ (изменяйте здесь) ==========================
# Режим по умолчанию (если оставить None, будет запрошен при запуске)
# 1 - только текст, 2 - полный парсинг с вложениями
DEFAULT_MODE = None   # или 1, 2

# Ссылка на первую страницу темы (обязательно укажите!)
START_URL = 'https://prodota.ru/forum/topic/224160/'   # <-- ЗАМЕНИТЕ НА ВАШУ ТЕМУ

# Имя папки, куда будет сохранён архив (без пробелов, латиница и цифры)
FOLDER_NAME = 'prodota_archive'   # <-- МОЖЕТЕ ИЗМЕНИТЬ

# Папка, в которую будут сохраняться все архивы (по умолчанию – папка Download на Android)
DEFAULT_OUTPUT_DIR = "/storage/emulated/0/Download"

# Задержка между запросами к страницам (секунды) – чтобы не перегружать сервер
DELAY = 1.5

# Таймаут для запроса (секунды)
TIMEOUT = 20

# Для текстового режима: формат сохранения ('json' или 'csv')
TEXT_FORMAT = 'json'   # можно изменить на 'csv'

# Расширения файлов для полного режима (определяют тип вложения)
IMAGE_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg')
VIDEO_EXTENSIONS = ('.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv')
DOC_EXTENSIONS = ('.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.txt', '.zip', '.rar', '.7z')
ATTACH_EXTENSIONS = IMAGE_EXTENSIONS + VIDEO_EXTENSIONS + DOC_EXTENSIONS
# =================================================================================


# ----------------------------------------
#  Базовый класс с общими методами (загрузка страниц, парсинг постов)
# ----------------------------------------
class BaseParser:
    def __init__(self, start_url, output_dir, delay=DELAY, timeout=TIMEOUT):
        self.start_url = start_url
        self.output_dir = Path(output_dir)
        self.delay = delay
        self.timeout = timeout
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
        })
        self.posts = []
        self.seen_posts = set()

    def get_soup(self, url):
        try:
            response = self.session.get(url, timeout=self.timeout)
            response.raise_for_status()
            if response.encoding is None:
                response.encoding = 'utf-8'
            return BeautifulSoup(response.text, 'html.parser')
        except Exception as e:
            print(f"Ошибка загрузки {url}: {e}")
            return None

    def extract_post_data(self, post_element, base_url):
        """Извлекает автора, номер поста и текст (без обработки вложений)."""
        author_elem = post_element.select_one('.cAuthorPane_author, .ipsComment_author, .author')
        if not author_elem:
            return None
        author = author_elem.get_text(strip=True)

        post_id = None
        if post_element.get('id'):
            match = re.search(r'(\d+)$', post_element.get('id'))
            if match:
                post_id = match.group(1)
        if not post_id:
            id_link = post_element.select_one('a[data-commentid]')
            if id_link and id_link.get('data-commentid'):
                post_id = id_link['data-commentid']
        if not post_id:
            post_id = 'N/A'

        content_elem = post_element.select_one('.cPost_contentWrap, .ipsComment_content, .post-content')
        if not content_elem:
            return None
        text = content_elem.get_text(separator='\n', strip=True)

        return {
            'author': author,
            'post_number': post_id,
            'content': text
        }

    def parse_page(self, url):
        soup = self.get_soup(url)
        if not soup:
            return None

        post_blocks = soup.select('li.ipsComment, div.cPost, article.ipsComment, div.post')
        if not post_blocks:
            print("   Не найдено сообщений на странице. Проверьте структуру сайта.")
            return None

        for block in post_blocks:
            post_data = self.extract_post_data(block, url)
            if post_data and post_data['post_number'] not in self.seen_posts:
                self.seen_posts.add(post_data['post_number'])
                self.posts.append(post_data)

        next_link = None
        next_elem = soup.select_one('a[rel="next"]')
        if next_elem and next_elem.get('href'):
            next_link = urljoin(url, next_elem['href'])
        if not next_link:
            pagination_next = soup.select_one('.ipsPagination_next a, .next a')
            if pagination_next and pagination_next.get('href'):
                next_link = urljoin(url, pagination_next['href'])

        return next_link

    def run(self):
        print(f"Начинаем парсинг: {self.start_url}")
        print(f"Папка для сохранения: {self.output_dir.absolute()}")
        current_url = self.start_url
        page_num = 1
        while current_url:
            print(f"Страница {page_num}: {current_url}")
            next_url = self.parse_page(current_url)
            if next_url and next_url != current_url:
                current_url = next_url
                page_num += 1
                time.sleep(self.delay)
            else:
                break
        print(f"\nСобрано сообщений: {len(self.posts)}")


# ----------------------------------------
#  Режим 1: только текст, сохраняет в JSON/CSV с возможностью разбивки
# ----------------------------------------
class TextParser(BaseParser):
    def __init__(self, start_url, output_dir, format='json', split_size_mb=0):
        super().__init__(start_url, output_dir)
        self.format = format.lower()
        self.split_size_mb = split_size_mb   # 0 = не разбивать

    def _split_and_save_json(self, data):
        """Разбивает список записей на несколько JSON-файлов заданного размера."""
        # Сначала сериализуем весь список в строку, чтобы узнать общий размер
        full_json = json.dumps(data, ensure_ascii=False, indent=2)
        full_size = len(full_json.encode('utf-8'))
        max_size = self.split_size_mb * 1024 * 1024

        if full_size <= max_size:
            # Файл помещается в один
            output_file = self.output_dir / 'posts.json'
            with open(output_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
            print(f"Данные сохранены в {output_file}")
            return

        # Нужно разбить – определяем количество записей в каждой части
        # Приблизительно: берём средний размер одной записи
        avg_record_size = full_size / len(data)
        records_per_part = int(max_size / avg_record_size) + 1

        part = 1
        for i in range(0, len(data), records_per_part):
            part_data = data[i:i+records_per_part]
            output_file = self.output_dir / f'posts_part{part}.json'
            with open(output_file, 'w', encoding='utf-8') as f:
                json.dump(part_data, f, ensure_ascii=False, indent=2)
            print(f"Часть {part} сохранена: {output_file}")
            part += 1

    def _split_and_save_csv(self, data):
        """Разбивает CSV на несколько файлов, сохраняя заголовки в каждом."""
        max_size = self.split_size_mb * 1024 * 1024
        # Сначала пишем всё во временный файл, чтобы определить разбиение
        temp_file = self.output_dir / '_temp.csv'
        with open(temp_file, 'w', newline='', encoding='utf-8-sig') as f:
            writer = csv.DictWriter(f, fieldnames=['author', 'post_number', 'content'])
            writer.writeheader()
            writer.writerows(data)

        # Читаем строки, считаем размер
        lines = []
        with open(temp_file, 'r', encoding='utf-8-sig') as f:
            lines = f.readlines()

        # Заголовок (первая строка)
        header = lines[0]
        # Убираем заголовок из списка
        data_lines = lines[1:]

        # Определяем разбиение
        current_part = []
        current_size = len(header.encode('utf-8'))
        part_num = 1

        for line in data_lines:
            line_size = len(line.encode('utf-8'))
            if current_size + line_size > max_size and current_part:
                # Сохраняем текущую часть
                part_file = self.output_dir / f'posts_part{part_num}.csv'
                with open(part_file, 'w', newline='', encoding='utf-8-sig') as f:
                    f.write(header)
                    f.writelines(current_part)
                print(f"Часть {part_num} сохранена: {part_file}")
                # Начинаем новую часть
                part_num += 1
                current_part = [line]
                current_size = len(header.encode('utf-8')) + line_size
            else:
                current_part.append(line)
                current_size += line_size

        # Последняя часть
        if current_part:
            part_file = self.output_dir / f'posts_part{part_num}.csv'
            with open(part_file, 'w', newline='', encoding='utf-8-sig') as f:
                f.write(header)
                f.writelines(current_part)
            print(f"Часть {part_num} сохранена: {part_file}")

        # Удаляем временный файл
        temp_file.unlink()

    def save(self):
        if self.split_size_mb > 0:
            print(f"Разбивка на части по {self.split_size_mb} МБ включена.")
            if self.format == 'json':
                self._split_and_save_json(self.posts)
            elif self.format == 'csv':
                self._split_and_save_csv(self.posts)
            else:
                print(f"Неизвестный формат {self.format}, данные не сохранены.")
        else:
            # Без разбивки
            if self.format == 'json':
                output_file = self.output_dir / 'posts.json'
                with open(output_file, 'w', encoding='utf-8') as f:
                    json.dump(self.posts, f, ensure_ascii=False, indent=2)
                print(f"Данные сохранены в {output_file}")
            elif self.format == 'csv':
                output_file = self.output_dir / 'posts.csv'
                with open(output_file, 'w', newline='', encoding='utf-8-sig') as f:
                    writer = csv.DictWriter(f, fieldnames=['author', 'post_number', 'content'])
                    writer.writeheader()
                    writer.writerows(self.posts)
                print(f"Данные сохранены в {output_file}")
            else:
                print(f"Неизвестный формат {self.format}, данные не сохранены.")

    def run(self):
        super().run()
        self.save()


# ----------------------------------------
#  Режим 2: полный парсинг с вложениями и HTML-архивом
# ----------------------------------------
class FullArchiver(BaseParser):
    def __init__(self, start_url, output_dir):
        super().__init__(start_url, output_dir)
        self.attachment_counter = 0
        self.local_attachments = {}
        # Создаём папки для вложений
        self.attachments_dir = self.output_dir / 'attachments'
        self.images_dir = self.attachments_dir / 'images'
        self.videos_dir = self.attachments_dir / 'videos'
        self.docs_dir = self.attachments_dir / 'docs'
        for d in [self.attachments_dir, self.images_dir, self.videos_dir, self.docs_dir]:
            d.mkdir(parents=True, exist_ok=True)

    def download_file(self, url, subdir):
        parsed = urlparse(url)
        filename = os.path.basename(unquote(parsed.path))
        if not filename or '.' not in filename:
            self.attachment_counter += 1
            try:
                head = self.session.head(url, timeout=5, allow_redirects=True)
                content_type = head.headers.get('content-type', '')
                ext = self.guess_extension_by_mime(content_type)
                filename = f"file_{self.attachment_counter}{ext}"
            except:
                filename = f"file_{self.attachment_counter}.bin"
        else:
            filename = filename.split('?')[0]

        base, ext = os.path.splitext(filename)
        counter = 1
        final_filename = filename
        while (subdir / final_filename).exists():
            final_filename = f"{base}_{counter}{ext}"
            counter += 1

        local_path = subdir / final_filename
        rel_path = local_path.relative_to(self.output_dir).as_posix()

        try:
            with self.session.get(url, stream=True, timeout=self.timeout) as r:
                r.raise_for_status()
                with open(local_path, 'wb') as f:
                    for chunk in r.iter_content(chunk_size=8192):
                        f.write(chunk)
            print(f"  Скачан файл: {rel_path}")
            return rel_path
        except Exception as e:
            print(f"  Ошибка скачивания {url}: {e}")
            return None

    def guess_extension_by_mime(self, mime):
        mime = mime.lower()
        if 'image/jpeg' in mime:
            return '.jpg'
        if 'image/png' in mime:
            return '.png'
        if 'image/gif' in mime:
            return '.gif'
        if 'video/mp4' in mime:
            return '.mp4'
        if 'application/pdf' in mime:
            return '.pdf'
        return '.bin'

    def is_attachment_url(self, url):
        if not url:
            return False
        if url.startswith('#') or url.startswith('javascript:'):
            return False
        path = urlparse(url).path.lower()
        if any(path.endswith(ext) for ext in ATTACH_EXTENSIONS):
            return True
        if '/uploads/' in path or '/monthly_' in path or 'attachments' in path:
            return True
        return False

    def process_attachments_in_html(self, html_content, base_url):
        if not html_content:
            return html_content

        soup = BeautifulSoup(html_content, 'html.parser')

        for a_tag in soup.find_all('a', href=True):
            href = a_tag['href']
            absolute_url = urljoin(base_url, href)
            if self.is_attachment_url(absolute_url):
                if absolute_url in self.local_attachments:
                    local_path = self.local_attachments[absolute_url]
                else:
                    ext = os.path.splitext(urlparse(absolute_url).path)[1].lower()
                    if ext in IMAGE_EXTENSIONS:
                        subdir = self.images_dir
                    elif ext in VIDEO_EXTENSIONS:
                        subdir = self.videos_dir
                    else:
                        subdir = self.docs_dir
                    local_path = self.download_file(absolute_url, subdir)
                    if local_path:
                        self.local_attachments[absolute_url] = local_path
                    else:
                        continue

                a_tag['href'] = local_path
                if ext in IMAGE_EXTENSIONS:
                    img_tag = soup.new_tag('img', src=local_path, alt=local_path, style="max-width:100%;")
                    a_tag.replace_with(img_tag)
                elif ext in VIDEO_EXTENSIONS:
                    video_tag = soup.new_tag('video', controls=True, style="max-width:100%;")
                    source_tag = soup.new_tag('source', src=local_path, type='video/mp4')
                    video_tag.append(source_tag)
                    a_tag.replace_with(video_tag)

        for img_tag in soup.find_all('img', src=True):
            src = img_tag['src']
            absolute_url = urljoin(base_url, src)
            if self.is_attachment_url(absolute_url):
                if absolute_url in self.local_attachments:
                    local_path = self.local_attachments[absolute_url]
                else:
                    local_path = self.download_file(absolute_url, self.images_dir)
                    if local_path:
                        self.local_attachments[absolute_url] = local_path
                    else:
                        continue
                img_tag['src'] = local_path

        return str(soup)

    def extract_post_data(self, post_element, base_url):
        """Переопределяем метод для обработки вложений в HTML."""
        author_elem = post_element.select_one('.cAuthorPane_author, .ipsComment_author, .author')
        if not author_elem:
            return None
        author = author_elem.get_text(strip=True)

        post_id = None
        if post_element.get('id'):
            match = re.search(r'(\d+)$', post_element.get('id'))
            if match:
                post_id = match.group(1)
        if not post_id:
            id_link = post_element.select_one('a[data-commentid]')
            if id_link and id_link.get('data-commentid'):
                post_id = id_link['data-commentid']
        if not post_id:
            post_id = 'N/A'

        content_elem = post_element.select_one('.cPost_contentWrap, .ipsComment_content, .post-content')
        if not content_elem:
            return None

        inner_html = str(content_elem)
        processed_html = self.process_attachments_in_html(inner_html, base_url)

        return {
            'author': author,
            'post_number': post_id,
            'html': processed_html
        }

    def generate_html(self):
        html_template = """<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Архив темы: {title}</title>
    <style>
        body {{
            font-family: Arial, sans-serif;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
            background: #f5f5f5;
        }}
        .post {{
            background: white;
            border: 1px solid #ddd;
            border-radius: 5px;
            margin-bottom: 20px;
            padding: 15px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }}
        .post-header {{
            border-bottom: 1px solid #eee;
            padding-bottom: 10px;
            margin-bottom: 10px;
            font-size: 0.9em;
            color: #666;
        }}
        .post-author {{
            font-weight: bold;
            color: #333;
        }}
        .post-id {{
            color: #999;
            margin-left: 10px;
        }}
        .post-content {{
            line-height: 1.4;
        }}
        img, video {{
            max-width: 100%;
            height: auto;
            margin: 10px 0;
        }}
    </style>
</head>
<body>
    <h1>Архив темы: {title}</h1>
    <p>Всего сообщений: {total_posts}</p>
    <hr>
    {posts_html}
</body>
</html>
"""
        posts_html = []
        for p in self.posts:
            posts_html.append(f"""
            <div class="post">
                <div class="post-header">
                    <span class="post-author">{p['author']}</span>
                    <span class="post-id">Пост #{p['post_number']}</span>
                </div>
                <div class="post-content">
                    {p['html']}
                </div>
            </div>
            """)

        title = "Тема с prodota.ru"
        final_html = html_template.format(
            title=title,
            total_posts=len(self.posts),
            posts_html='\n'.join(posts_html)
        )
        return final_html

    def run(self):
        super().run()
        print("Генерация HTML-страницы...")
        html_content = self.generate_html()
        index_path = self.output_dir / 'index.html'
        with open(index_path, 'w', encoding='utf-8') as f:
            f.write(html_content)
        print(f"HTML-страница сохранена: {index_path.absolute()}")


# ----------------------------------------
#  Точка входа: только выбор режима и размера части
# ----------------------------------------
if __name__ == '__main__':
    print("=== Парсер форума prodota.ru ===")

    # Определяем режим
    mode = DEFAULT_MODE
    if mode is None:
        print("Выберите режим работы:")
        print("1 - Только текст (сохранить в JSON/CSV)")
        print("2 - Полный парсинг (скачать вложения, создать HTML-архив)")
        mode = input("Введите 1 или 2: ").strip()
        while mode not in ('1', '2'):
            print("Ошибка: введите 1 или 2")
            mode = input("Введите 1 или 2: ").strip()
        mode = int(mode)
    else:
        print(f"Используем режим {mode} (указан в настройках)")

    # Проверяем, что START_URL и FOLDER_NAME заданы
    if not START_URL:
        print("ОШИБКА: переменная START_URL не задана. Отредактируйте настройки в начале кода.")
        exit(1)
    if not FOLDER_NAME:
        print("ОШИБКА: переменная FOLDER_NAME не задана. Отредактируйте настройки в начале кода.")
        exit(1)

    # Для режима 1 запрашиваем размер разбивки
    split_size_mb = 0
    if mode == 1:
        print("\nДля текстового режима можно разбить результат на несколько файлов.")
        split_input = input("Введите максимальный размер части в МБ (0 = не разбивать): ").strip()
        try:
            split_size_mb = int(split_input)
            if split_size_mb < 0:
                split_size_mb = 0
        except:
            split_size_mb = 0
        if split_size_mb > 0:
            print(f"Будет выполнена разбивка на части по {split_size_mb} МБ.")

    # Создаём полный путь к папке
    full_path = os.path.join(DEFAULT_OUTPUT_DIR, FOLDER_NAME)
    os.makedirs(full_path, exist_ok=True)

    # Запуск в зависимости от режима
    if mode == 1:
        print("\n=== Режим: только текст ===")
        parser = TextParser(START_URL, full_path, format=TEXT_FORMAT, split_size_mb=split_size_mb)
        parser.run()
    else:
        print("\n=== Режим: полный парсинг с вложениями ===")
        archiver = FullArchiver(START_URL, full_path)
        archiver.run()

    print("\nГотово! Файлы сохранены в папку:", full_path)

 

 


moonfangtopich написал 29.08.2019 в 14:57:
У вас недостаточно широкий кругозор, пацаны

Я странствия этого еблана видел в покерных топанах, а потом в таверне - это один из самых безумных людей на форуме. Я искренне надеялся, что его зов о помощи останется незамеченным, но нет, нашелся доброволец и вот уже три страницы мы пожинаем плоды

Поделиться сообщением


Ссылка на сообщение
(изменено)

Блять да скиньте денег пацану. 
 

куда делись эти два криптомилиардера что тут были раньше. Не помню как Ники пишуться


Изменено пользователем Gomosek

Поделиться сообщением


Ссылка на сообщение
Gomosek написал 14 минут назад:

Блять да скиньте денег пацану. 
 

куда делись эти два криптомилиардера что тут были раньше. Не помню как Ники пишуться

 

Поедешь со мной в свадебное путешествие? 😍💓♥️


moonfangtopich написал 29.08.2019 в 14:57:
У вас недостаточно широкий кругозор, пацаны

Я странствия этого еблана видел в покерных топанах, а потом в таверне - это один из самых безумных людей на форуме. Я искренне надеялся, что его зов о помощи останется незамеченным, но нет, нашелся доброволец и вот уже три страницы мы пожинаем плоды

Поделиться сообщением


Ссылка на сообщение
Ratio написал 36 минут назад:
Qreeq написал 44 минуты назад:

Еще раз цифру 15к назовешь, я лично приеду тебе ебало бить долбоебу

Его там лунтик хуем наказывать собирался. Чем закончилось ?

Не могу уснуть, психую опять, зря просрался на форум

Поделиться сообщением


Ссылка на сообщение

Присоединяйтесь к обсуждению

Вы можете опубликовать сообщение сейчас, а зарегистрироваться позже. Если у вас есть аккаунт, войдите в него для написания от своего имени.

Гость
Ответить в тему...

×   Вставлено в виде отформатированного текста.   Восстановить форматирование

  Разрешено не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставить изображения напрямую. Загрузите или вставьте изображения по ссылке.

Загрузка...

×
×
  • Создать...