LINUX.ORG.RU

Сообщения rtxtxtrx

 

Поиск в Google больше не будет доступен без JavaScript

Новости — Интернет
Группа Интернет

Компания Google теперь уведомляет пользователей, что для пользования поиском нужно включить JavaScript в браузере. Ранее иностранный ресурс TechCrunch связался с сотрудниками Google для выяснения причин такого решения. Данная мера обусловлена борьбой с ботами, а также тем, что большинство пользователей пользуются поисковиком с включенным JavaScript, и многие даже не знают как его отключить. Только 0.1% пользователей поиска Google отключали его.

>>> Подробности

 ,

rtxtxtrx
()

Запущен Qwen Chat

Новости — Интернет
Группа Интернет

Команда разработчиков языковых моделей Qwen выпустила веб-версию чата, где можно опробовать их модели, которые ранее можно было скачать и использовать любой желающий, например, с сайта Qwen AI или же с Hugging Face. Там, например, есть модель с 72 миллиардами параметров. Qwen является разработкой китайской Alibaba Cloud.

( читать дальше... )

>>> Попробовать

 , , , ,

rtxtxtrx
()

Ghostty 1.0 👻

Новости — Open Source
Ghostty 1.0 👻
Группа Open Source

На днях вышла первая версия Ghostty, одного из самых быстрых эмуляторов терминала из существующих. Reddit и многие тематические каналы на YouTube уже успели окрестить его лучшим. На Github проект за короткий период собрал >15k ⭐.

( читать дальше... )

>>> Официальный сайт

 , , ,

rtxtxtrx
()

Как скрыть от собеседника окно при шаринге экрана

Форум — Desktop

Набросал скрипт https://github.com/s3rgeym/interview-helper/blob/main/main.py

Он распознает голос, и отправляет текст дегенеративной сети. Хочу алгоритмдристику проходить на какого-нить рахитектора.

Ну в общем мне понятно теперь как скрыть окно при шаринге. Я понимаю что это скорее всего возможно, но нагуглить не могу.

 

rtxtxtrx
()

Kitty не дружит с Vim: странные символы в строке статуса

Форум — Development

Поставил себе на днях это чудо и сразу вылезли артефакты в виде непонятных символов в строке статуса.

Баг с прокруткой решился элементарно:

let &t_ut=''

Потому что это решение описано в документации. А вот непонятные символы удалось победить только с помощью set t_RV=. Я, конечно, не понял причем тут видео, но перестало работать перемещение курсора кликом мыши. Тогда я модифицировал эту строку:

set t_RV= ttymouse=xterm2

Так работает вроде все, но у меня вопрос к знатокам: «А как правильно?»

В konsole все, естественно, и без этих костылей работало

" Прочие настройки
if $TERM ==# 'xterm-kitty'
  " Сброс настроек курсора, чтобы избежать артефактов при скроллинге
  " https://sw.kovidgoyal.net/kitty/faq/#using-a-color-theme-with-a-background-color-does-not-work-well-in-vim
  let &t_ut = ''
  " Какой-то мусор в tabline/statusline
  set t_RV= ttymouse=xterm2

  " Перезапуск Kitty при изменении его настроек
  autocmd BufWritePost ~/.config/kitty/kitty.conf silent !pgrep kitty && kill -SIGUSR1 $(pgrep kitty)
endif

 ,

rtxtxtrx
()

Как вы относитесь к тестовым заданиям?

Голосования — Голосования(не подтверждено)
Добрый день, %YOUR_NAME%!

Пару дней назад отправляла Вам тестовую задачу на вакансию Senior JSON Developer, но не получила ответ.

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

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

 

rtxtxtrx
()

Дедупликация в Btrfs

Статьи — Администрирование
Дедупликация в Btrfs

Btrfs — это замечательная файловая система, главными фишками которой являются легковесные снапшоты, реализуемые через Copy-On-Write (COW), и сжатие. Она показала высокую надежность и стабильность, и именно поэтому она включена в ядро Linux. Однако, даже наличие COW не позволяет избежать избыточности данных на уровне блоков.

( читать дальше... )

 ,

rtxtxtrx
()

Изучение запросов на сервер от ботов

Форум — Admin

В обшем я написал такую вещь, которая среди посетителей сервака отсеивает лысых обезьян по юзер-агенту:

http {
  map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
  }

  # Юзер-агенты
  # curl/8.10.1
  # python-requests/2.32.3
  # python-httpx/0.27.2
  # Python/3.12 aiohttp/3.9.5
  # Go-http-client/2.0

  # Фильтруем запросы от ботов
  map $http_user_agent $is_bot {
    default 0;
    ~*curl 1;
    ~*Wget 1;
    ~*python-requests 1;
    ~*python-httpx 1;
    ~*aiohttp 1;
    ~*Go-http-client 1;
  }

  # удобно с помощью jq искать нужные данные
  # TODO: добавить cookie, все заголовки
  log_format json_combined escape=json
    '{'
      '"local_time":"$time_iso8601",'
      '"ip_addr":"$remote_addr",'
      '"user_agent":"$http_user_agent",'
      '"http_cookie":"$http_cookie",'
      '"http_authorization":"$http_authorization",'
      '"referrer":"$http_referer",'
      '"content_type":"$http_content_type",'
      '"request_method":"$request_method",'
      '"request_uri":"$request_uri",'
      # '"request_url":"$scheme://$host$request_uri",'
      # '"body_bytes_sent":"$body_bytes_sent",'
      '"request_body":"$request_body",'
      '"response_status":$status,'
      '"response_content_type":"$sent_http_content_type"'
    '}';

  access_log /var/log/bot_access.log.json json_combined if=$is_bot;
  # ...
}

Что-то такое в логе имеем:

{"local_time":"2024-11-07T13:22:47+00:00","ip_addr":"45.155.91.226","user_agent":"python-requests/2.27.1","http_cookie":"","http_authorization":"Basic QURNSU46QzFzYzA=","referrer":"","content_type":"","request_method":"GET","request_uri":"/level/15/exec/-/sh/run/CR","request_body":"","response_status":404,"response_content_type":"text/html; charset=UTF-8"}

За айпи этот можете не переживать, он принадлежит говнохостингу для дорвеев из Бахрейна (dedires точка com чтобы рекламу не создавать). И в месяц он пару тысяч запросов делает. Я этих подонков не баню, хотя часто они флудят какими-то уязвимостями, которые известны с 2017 и ранее… Это скорее всего просто чей-то сайт на вротпрессе взломали через какой-нибудь плагин типа ЦЗ Super Cache…

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

И вот вопрос: а по каким юзерагентам их еще можно палить? Или есть надежнее способ?

 

rtxtxtrx
()

qBittorrent 14 лет был уязвим атаке man in the middle на HTTPS

Новости — Безопасность
qBittorrent 14  лет был уязвим атаке man in the middle на HTTPS
Группа Безопасность

В qBittorrent была закрыта 14-летняя уязвимость, связанная с некорректной проверкой SSL/TLS-сертификатов. Обновление до версии 5.0.1 устранило эту уязвтмость, которая существовала с 2010 года.

В течение этого времени программа принимала любые сертификаты, включая поддельные, что делало её уязвимой к атаке типа «человек посередине» (MitM). Это позволяло злоумышленникам незаметно изменять сетевой трафик, потенциально подвергая пользователей риску скачивания и выполнения вредоносного кода при обновлении продукта по ссылке из уведомления о выходе новой версии, а так же при загрузке бинарников Python на Windows. Уязвимость была не только теоретической, но и реализуемой на практике.

Так же отсутствие валидации сертификатов позволяло MitM подменять содержимое RSS и базы данных MaxMind Geo IP.

>>> Подробности

 , ,

rtxtxtrx
()

Делаем из Vim IDE

Статьи — Разработка
Делаем из Vim IDE

В стародавние времена когда деревья были большими, трава зеленее, а мороженное стоило по 10 копеек, на Земле жили динозавры, и эти динозавры программировали в Vim.

( читать дальше... )

 , ,

rtxtxtrx
()

Тру стори о «сдохшем» SSD

Форум — Talks

Есть у меня SATA SSD (так уж исторически сложилось, что в русском языке NVMe тоже называют SSD, и требуется уточнение) замечательной китайской фирмы Apacer. Так вот, в один момент он перестал определяться. Понял я это по долгой загрузке Arch’а. Он что-то искал по UUID 1.5 минуты. Диск этот у меня был зашифрован через LUKS и использовался для хранения моих голых фото (шучу). Открывался он при старте системы по ключу из /etc/keys. Я перепроверил /etc/crypttab. Там все было правильно. Правильными были и права на файле-ключе — 600. «Смерть!» — подумал я. Диск ничего не ответил, ведь он же диск и говорить не может… Я попробовал передернуть. Я пихал в разные дырки, но ничего не помогало. Спалил что-то на материнке, потрогав ее? Возможно, но маловероятно, и проверить я это никак не мог. Тогда я вспомнил, что в Discover долго висело предупреждение об устаревшей версии прошивки чего-то… И в один день я, по всей видимости, случайно нажал на «обновить». Discover через fwupd обновил мне прошивку диска, и тот перестал определяться… Когда это произошло, я не помню, так как компьютер у меня не выключался/перегружался неделю… «Нужен программатор!» — подумал я, а потом решил попробовать обновить прошивку на материнке… Для этого нужно просто файл кинуть на флешку. И после обновления прошивки материнки, диск начал снова определяться. Я посмотрел его SMART, там было записано всего 2 терабайта. fwupd я удалил:

yay -Rns fwupd

 , ,

rtxtxtrx
()

Настройка VS Code для работы с Python

Статьи — Разработка
Настройка VS Code для работы с Python

VS Code — это мощный инструмент для разработки на Python, который легко настроить для работы с такими полезными утилитами, как pylint, black и isort. Эти инструменты помогут поддерживать чистоту кода, единый стиль и упорядоченность импортов. Для поиска и устранения ошибок пригодится встроенный отладчик debugpy, обеспечивающий удобный процесс дебага. В дополнение ко всему можно подключить искусственного помощника для ускорения написания кода. В этой статье мы рассмотрим установку и настройку этих инструментов, а также их интеграцию с VS Code.

( читать дальше... )

 , , , ,

rtxtxtrx
()

Что такое Flatpak и какие проблемы он решает

Статьи — Desktop

Flatpak - это универсальная система упаковки приложений для Linux, которая стремится решить проблему множества форматов пакетов. Она позволяет разработчикам создавать единый пакет приложения, который может быть установлен на различных дистрибутивах Linux без необходимости перекомпиляции.

( читать дальше... )

 , , , ,

rtxtxtrx
()

Настройка Visual Studio Code для работы с LLama

Статьи — Разработка
Настройка Visual Studio Code для работы с LLama

В этой статье я расскажу как развернуть локальные аналог Chat-GPT и настроить VS Code для работы с ним.

( читать дальше... )

 , , ,

rtxtxtrx
()

Зацените и протестриуйте бота, которого написал ChatGPT

Форум — Development

Прочитал я намедни, такую новость 8-летний ребёнок без опыта программирования создал чат-бота за 45 минут, ролик стал вирусным. Ясно, что кликбейт, ЛПП и прочие нехорошие слова… Но я решил проверить на что же способен ChatGPT. Признаюсь, я его использую для написания простых скриптов на 300-500 строчек кода. Сегодня я решил проверить правда или миф сия кликбейтная новость… и она оказалась правдой (частично).

Я до этого не писал ботов для Телеграм. Да, я знаю Python, знаю какие библиотеки для это используются, так как сидя в чате про Python с соевыми омега-обрыганами и школьниками, постоянно вижу вопросы про aiogram.

Началось все с такого задания:

Напиши мне на Python чат-бота. Алгоритм работы такой. Сторонний пользователь пишет сообщение и оно отправляется в чат к владельцу бота. Если владелец бота отвечает на конкретное сообщение то бот его пересылает лицу которое его отправило, если просто отвечает то сообщение отправляется последнему написавшему

На что он мне родил такой код:

import telebot
from collections import defaultdict

# Токен вашего бота
API_TOKEN = 'YOUR_API_TOKEN_HERE'

bot = telebot.TeleBot(API_TOKEN)

# Хранилище сообщений (user_id -> last_message)
user_last_message = {}
# Хранилище последних сообщений каждого пользователя, отправленных владельцу
message_store = defaultdict(list)

# ID владельца бота
OWNER_ID = 'YOUR_OWNER_ID_HERE'

@bot.message_handler(func=lambda message: True)
def handle_message(message):
    user_id = message.from_user.id
    if user_id == OWNER_ID:
        # Ответ от владельца
        if message.reply_to_message:
            # Если владелец отвечает на конкретное сообщение
            original_message_id = message.reply_to_message.message_id
            for uid, msg_list in message_store.items():
                for msg in msg_list:
                    if msg.message_id == original_message_id:
                        bot.send_message(uid, f"Ответ на ваше сообщение: {message.text}")
                        return
        else:
            # Если владелец отвечает без привязки
            if user_last_message:
                last_user_id = max(user_last_message, key=user_last_message.get)
                bot.send_message(last_user_id, f"Ответ от владельца: {message.text}")
    else:
        # Сообщение от пользователя
        bot.send_message(OWNER_ID, f"Сообщение от {message.from_user.username} (id: {user_id}): {message.text}")
        user_last_message[user_id] = message.message_id
        message_store[user_id].append(message)

bot.polling()

Устроил ли он меня? - Нет. Я люблю асинхронщину:

Используй какую нибудь асинхронную библиотек, храни данные в sqlite. Владельцу добавь кнопки типа заблокировать, очистить диалог (есть ли у бота возможность очищать диалог?). Напиши Dockerfile и docker-compose для запуска. 

Был высран такой код:

import asyncio
import aiosqlite
from aiogram import Bot, Dispatcher, types
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
from aiogram.filters import Text, Command
from aiogram.fsm.storage.memory import MemoryStorage

# Токен вашего бота
API_TOKEN = 'YOUR_API_TOKEN_HERE'

# ID владельца бота
OWNER_ID = 'YOUR_OWNER_ID_HERE'

# Инициализация бота и диспетчера
bot = Bot(token=API_TOKEN)
dp = Dispatcher(storage=MemoryStorage())

# Подключение к базе данных
async def init_db():
    async with aiosqlite.connect('bot.db') as db:
        await db.execute('''CREATE TABLE IF NOT EXISTS users
                            (user_id INTEGER PRIMARY KEY, blocked INTEGER DEFAULT 0)''')
        await db.execute('''CREATE TABLE IF NOT EXISTS messages
                            (user_id INTEGER, message_id INTEGER, content TEXT)''')
        await db.commit()

# Кнопки управления
def owner_keyboard(user_id):
    builder = InlineKeyboardBuilder()
    builder.add(
        InlineKeyboardButton(text="Заблокировать", callback_data=f"block_{user_id}"),
        InlineKeyboardButton(text="Очистить диалог", callback_data=f"clear_{user_id}")
    )
    return builder.as_markup()

# Обработчик сообщений от пользователя
@dp.message(Command(commands=['start']))
async def start(message: types.Message):
    await message.answer("Привет! Отправь мне сообщение, и я перешлю его владельцу.")

@dp.message()
async def forward_to_owner(message: types.Message):
    user_id = message.from_user.id
    async with aiosqlite.connect('bot.db') as db:
        async with db.execute("SELECT blocked FROM users WHERE user_id = ?", (user_id,)) as cursor:
            row = await cursor.fetchone()
            if row and row[0] == 1:
                await message.answer("Вы заблокированы.")
                return
        
        await db.execute("INSERT INTO messages (user_id, message_id, content) VALUES (?, ?, ?)", 
                         (user_id, message.message_id, message.text))
        await db.commit()

    # Пересылка сообщения владельцу
    keyboard = owner_keyboard(user_id)
    await bot.send_message(OWNER_ID, f"Сообщение от {message.from_user.username} (id: {user_id}): {message.text}", reply_markup=keyboard)

# Обработчик сообщений от владельца
@dp.message(lambda message: message.from_user.id == int(OWNER_ID))
async def handle_owner_reply(message: types.Message):
    if message.reply_to_message:
        # Если ответ на конкретное сообщение
        original_message = message.reply_to_message.text.split("(id: ")[-1].rstrip('):')
        target_user_id = int(original_message)
        await bot.send_message(target_user_id, f"Ответ от владельца: {message.text}")
    else:
        async with aiosqlite.connect('bot.db') as db:
            async with db.execute("SELECT user_id FROM messages ORDER BY rowid DESC LIMIT 1") as cursor:
                row = await cursor.fetchone()
                if row:
                    last_user_id = row[0]
                    await bot.send_message(last_user_id, f"Ответ от владельца: {message.text}")

# Обработчик кнопок
@dp.callback_query(Text(startswith="block_"))
async def block_user(callback: types.CallbackQuery):
    user_id = int(callback.data.split("_")[1])
    async with aiosqlite.connect('bot.db') as db:
        await db.execute("UPDATE users SET blocked = 1 WHERE user_id = ?", (user_id,))
        await db.commit()
    await callback.message.answer(f"Пользователь {user_id} заблокирован.")

@dp.callback_query(Text(startswith="clear_"))
async def clear_dialog(callback: types.CallbackQuery):
    user_id = int(callback.data.split("_")[1])
    async with aiosqlite.connect('bot.db') as db:
        await db.execute("DELETE FROM messages WHERE user_id = ?", (user_id,))
        await db.commit()
    await callback.message.answer(f"Диалог с пользователем {user_id} очищен.")

async def main():
    await init_db()
    await dp.start_polling(bot)

if __name__ == "__main__":
    asyncio.run(main())

В целом как основа я подошел. Композ и докерфайл были правильными. Докерфайл я попросил переписать для использования alpine.

На это у меня ушло 6 запросов. После чего я переписал творчество бота, добавил логгирование, argparse и тп. Я скинул боту код с исправлениями:

<код>

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

То что он родил меня опять не устроило:

Убери это. await bot.send_message(args.owner_id, forward_text). Айди отправившего сообщения в базе. Когда мы отвечаем на сообщение, то нужно смотреть в базе кто его отправитель. Те нужно создать еще одну таблицу.

Но бот меня не понял:

Эта таблица должна выглядеть так:

CREATE TABLE IF NOT EXISTS messages (
            message_id INTEGER PRIMARY KEY,
            sender_id INTEGER,
        )

Так же нужно добавить таблицу user_info где хранить записи типа user_id, fullname, username. Эти данные каждый раз обновляются когда пишет пользователь. Из этой же таблицы берутся данные, когда нажимаешь на кнопку Кто отправитель?

После еще пары перепиываний кода, я окончательно забил на интеллект этот и переписал уже все сам…

Из грубых ошибок в коде отмечу это:

def owner_keyboard(message_id: int) -> InlineKeyboardMarkup:
    """Создает клавиатуру с кнопками управления для владельца."""
    keyboard = InlineKeyboardMarkup()
    keyboard.add(
        InlineKeyboardButton(
            text="👁️ Whois",
            callback_data=f"view_user_{message_id}",
        ),
        InlineKeyboardButton(
            text="🚫 Ban", callback_data=f"block_{message_id}"
        ),
    )
    return keyboard

Это какой-то неправильный счинтаксис из-за которого бот, написанный ChatGPT падал. Я это фрагмент переписал так:

def owner_keyboard(user_id: int) -> InlineKeyboardMarkup:
    """Создает клавиатуру с кнопками управления для владельца."""
    return InlineKeyboardMarkup(
        inline_keyboard=[
            [
                InlineKeyboardButton(
                    text="👁️ Кто это?",
                    callback_data=f"whois_{user_id}",
                ),
                InlineKeyboardButton(
                    text="🚫 Бан",
                    callback_data=f"block_{user_id}",
                ),
            ],
        ]
    )

Так же мне пришлось добавить обработку ошибок:

# https://docs.aiogram.dev/en/latest/dispatcher/errors.html
@dp.error()
async def error_handler(event: ErrorEvent):
    logger.error("Error caused by %s", event.exception, exc_info=True)

Сделать закрытие курсоров после использования:

# Такое
    cursor = await db_connection.execute(
        "SELECT sender_id FROM messages ORDER BY ROWID DESC LIMIT 1"
    )
    result = await cursor.fetchone()
    return result[0] if result else None

# Заменять на это
    async with connection.execute(
        "SELECT sender_id FROM message_senders WHERE message_id = ?",
        (message_id,),
    ) as cursor:
        result = await cursor.fetchone()
        return result[0] if result else None

# Что аналогично
cursor = await execute()
result = await cursor.fetchone()

try:
    return result[0] if result else None
finally:
    await cursor.close()

Я переписал создание таблиц и часть запросов.

Меня порадовало то, что ChatGPT может генерировать довольно таки грамотные запросы:

    await connection.execute(
        """
        INSERT INTO user_info (user_id, full_name, username)
        VALUES (?, ?, ?)
        ON CONFLICT(user_id) DO UPDATE SET
            full_name=excluded.full_name,
            username=excluded.username,
            updated_at=CURRENT_TIMESTAMP
        """,
        (user_id, full_name, username),
    )

И вот что вышло:

https://github.com/s3rgeym/feedback-tgbot/tree/main

Протестировать можно тут:

https://t.me/feedback_s3rgeym_bot

 , ,

rtxtxtrx
()

Использование обратных туннелей для доступа к устройствам за NAT

Статьи — Администрирование
Использование обратных туннелей для доступа к устройствам за NAT

frp — это утилита для создания обратных туннелей. Она позволяет получить доступ к локальному ресурсу за NAT через промежуточный сервер.

Многие знают про существование сервисов типа ngrok и localtunnel, которые позволяют делать нечто подобное. У них есть бесплатные тарифные планы, которые имеют ограничения по ежемесячному трафику и количеству подключений. Поэтому (и не только) в ряде случаев использование self-hosted-решений, таких как frp, оказывается более предпочтительным.

( читать дальше... )

 ,

rtxtxtrx
()

Приложения и утилиты, которые стоит попробовать

Статьи — Администрирование
Приложения и утилиты, которые стоит попробовать

Многие пользователи Linux с большим стажем даже не подозревают о существовании этих замечательных инструментов, которые способны облегчить им жизнь…

( читать дальше... )

 , ,

rtxtxtrx
()

Яблоконь LOR Codewars (продолжение): задача на комбинаторику или сраный Сбер теперь FAANG

Форум — Development

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

Напишите, пожалуйста, программу на любом языке программирования, которая поместит + (2+3), - (3-2), или ничего ( ) в промежутках между цифрами от 9 до 0 (в таком порядке) так, чтобы в результате получилось 200. Например: 98+76-5+43-2-10=200.

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

Ну и как полагается решение:

( читать дальше... )

Какой язык победит в сией битве? На каком решение будем самым коротким и лаконичным?

Тема посвещается памяти @kompspec’а, нашедшего работу за 20 рублей в час, и поэтому прекратившему посещать сий сайт. Любим пони, скайрим.

 ,

rtxtxtrx
()

Используем socks для доступа к ChatGPT и ускорения YouTube

Статьи — Администрирование
Используем socks для доступа к ChatGPT и ускорения YouTube

Эта статья не содержит описания способов обхода блокировок для доступа к противоправому контенту. Socks-прокси нужны исключительно для того чтобы смотреть YouTube и просить ChatGPT писать за тебя домашние задания!!! У первого какие-то проблемы со скоростью последнее время, говорят, что из-за отключения кеширующих серверов, а второй сам заблокировал жителей из России, Беларуси и Китая…

( читать дальше... )

 ,

rtxtxtrx
()

Подскажите алгоритм для генерации чего-то похожего на отпечаток пальца

Форум — Development

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

Нужно описание алгоритма чего-то типа такого:

Я не знаю как эту штуку назвать. Гугель, возможно забанит если двигаться спиралью… У меня пока идей нет

 

rtxtxtrx
()

RSS подписка на новые темы