Pull to refresh
16
0.4
Кашлак Андрей @andreymal

User

Send message
нужна обратная зона

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


и оплачивайте его

Криптой, что ли? Я наоборот был вынужден в марте-апреле 2022-го перенести всё своё из-за границы обратно в РФ из-за невозможности оплачивать

В общем случае N может оказаться недостаточно, вдруг меня посадят в тюрьму на N+1 лет 🤔

Со своей почтой есть две огромные проблемы.


Первая — IP-адрес. Если запускать почтовый сервер дома с домашним интернет-провайдером, то некоторые почтовые серверы отклоняют все письма с ошибкой в стиле "у вас домашний ISP, с него нельзя отправлять почту" — даже если есть корректные SPF и DKIM. Я уже пытался.


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

Вам уже несколько человек говорят, что такое случается, на практике.

Могу лишь ещё раз повторить:


Всё ещё интересно посмотреть на примеры перехода на буквы



Поехать в ту фирму и набить морду разработчику?

Да

uuid

Мне опять нужно повторять, что это 128-битное число?


Или хотят добавить туда больше информации для наглядности

Зачем это делать именно в id, а не в каком-нибудь дополнительном поле типа slug?

Какова мотивация тех, кто меняет id с интов на строки? Им становится мало приключений в жизни и однажды они решают угробить свою собственную БД и усложнить жизнь всем партнёрам?

"на голове стоять" это хранить id в виде строк ¯\_(ツ)_/¯

Но всё-таки — если внешние id представимы в виде инта, значит их можно хранить в виде инта (как минимум в БД, ограничения браузеров это совсем другая история)


Всё ещё интересно посмотреть на примеры перехода на буквы

Ну, одно дело хранить, а другое дело работать. Можно в базе хранить инты, а в браузер отдавать строки. Я в одном своём проекте использую 64-битные snowflake-числа в качестве первичных ключей в БД, а наружу выставляю строго строки, содержащие base58-представление числа, таким образом косплея идентификаторы из одной запрещённой соцсети

guid это 128-битное число, буквы это всего лишь один из способов его представления


Есть какие-нибудь более реальные примеры перехода на буквы?

хорошо бы обрабатывать ответы с кодом отличным от 200. Добавляем проверку, строчку будет лучше поместить в функцию.

Во-первых, зачем ради одной самоочевидной строки заводить целую функцию? Во-вторых, в requests и других подобных библиотеках уже есть встроенная функция raise_for_status, которая выбрасывает исключение при статусах 4xx и 5xx


return False

Пхпшники покусали? Во-первых, выбрасывать исключения при ошибках в питоне абсолютно нормально, та же raise_for_status так делает. Во-вторых, если есть аллергия на исключения, то можно хотя бы None вернуть, это всё ещё менее странно чем False


Логгирование, контроль исполнения и отладка

Всю эту информацию можно достать напрямую из объекта response или из возникшего исключения, функция response_res в показанном здесь виде не имеет никакого практического смысла. Кроме того, response_res ломает статическую типизацию, потому что заменяет объекты с аннотациями типов на нетипизированный словарь — для производства и будущей поддержки кода это ОЧЕНЬ плохо


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

Бред, RSS по сути своей предназначен именно для автоматизированного сбора данных


словит капчу на второй-третий раз

Капча на RSS — опять бред


fake_useragent.UserAgent().random

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


не хорошо слать запросы с пустыми cookies, referer и так далее

Ничего из перечисленного быть в принципе не должно, это же RSS


except:

… и получаем невозможность прервать работу программы из-за перехваченных KeyboardInterrupt и SystemExit. К счастью, в следующем примере кода это уже исправлено, хоть и без пояснений


а вот с исключением возникает, вопрос, как его правильно обработать.

Точно не подменой на фейковый Response. Опять аллергия на исключения?


Для одновременных запросов многопоточность вообще хороша

Для одновременных запросов многопоточность вообще не подходит, потому что наплодит кучу жрущих ресурсы потоков, которые при этом будут простаивать без дела в ожидании ответа от сервера. Для io-bound задач специально изобрели асинхронность, которая замечательно работает в одном потоке


logging.info(f"многопоточность сломалась {ex}")

info для критических ошибок, скрытие информации о стеке, скрытие информации о типе ошибки, ужас… Есть же специальный logging.exception для такого


Если запустить программу в режиме планировщика (например каждые 30 секунд)

Нормальный планировщик должен дожидаться, когда предыдущий запуск завершится


Как выглядит итоговый код

feedparser.parse зачем-то вызывается два раза, пустая трата процессора. Причём во второй раз не перехватываются возможные исключения (а вдруг внутри feedparser баги есть)


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


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


Как на самом деле выглядит итоговый код
import asyncio
import logging

import aiohttp
import feedparser

async def harvest_all(url: str) -> None:
    try:
        timeout = aiohttp.ClientTimeout(total=10)
        async with aiohttp.ClientSession(timeout=timeout) as session:
            async with session.get(url) as response:
                response.raise_for_status()
                raw_feed = await response.text()
    except Exception:
        logging.exception("Failed to get feed %s", url)
        return

    try:
        feed = feedparser.parse(raw_feed)
        process_feed(url, feed)
    except Exception:
        logging.exception("Failed to process feed %s", url)

def process_feed(url: str, feed: feedparser.FeedParserDict) -> None:
    if not feed["entries"]:
        logging.info("Feed %s has no entries", url)
        return

    for entry in feed["entries"]:
        logging.info("%s", entry["title"])

async def main() -> None:
    logging.basicConfig(level=logging.INFO)

    rss_list = [
        "https://lenta.ru/rss",
        "https://habr.com/ru/rss/all/",
    ]

    while True:
        tasks = {asyncio.create_task(harvest_all(url)) for url in rss_list}
        await asyncio.wait(tasks)
        await asyncio.sleep(60)

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

Чем это принципиально лучше какого-нибудь KeePass?

Тем не менее это полностью удовлетворяет поставленному вами условию «сразу показывая клиенту неверный ввод, до отправки формы без JS»

Не понимаю, почему в этой ветке до сих пор не упомянули это А нет, упомянули, но так мелко, что я не заметил. Но раз уж я уже отправил комментарий, то:


input:invalid {
  border: 1px solid red;
}

Текст ошибки конечно не выведет, но по крайней мере это даст юзеру возможность увидеть красную рамочку вокруг ошибочного инпута сразу в момент ввода

Как это можно сделать без помощи JS

Заманиваем пользователя на свой сайт, а на своём сайте пихаем:


<form action="https://сайт-жертва.рф/admin/delete-everything" method="POST">
  <button type="submit">Скачать бесплатно без регистрации и смс</button>
</form>

Браузер, отправляя такую форму, без вопросов отошлёт все куки, отправка которых не запрещена через SameSite

Очевидно, датаклассы. Когда счёт объектов идёт на тысячи — уже начинает быть заметно, что манипуляции с датаклассами в несколько раз медленнее манипуляций с кортежами. А ещё вы не использовали slots, на чём потеряли ещё несколько процентов производительности


UPD: а впрочем, есть же typing.NamedTuple. Если задача не требует мутабельных объектов, то, наверно, можно попробовать позаменять датаклассы на NamedTuple, совместив таким образом удобство датаклассов и скорость кортежей. А если мутабельность всё-таки нужна, то там же рядышком есть typing.TypedDict — в рантайме обычный dict, немножко медленнее чем кортежи, но всё ещё быстрее датаклассов

1) От одного пользователя запускаем ровно один сервис, и проблемы нет


2) Не получает, кто ж его к ним пустит-то? Фраза «чисто локальные» подразумевает, что они будут слушать Unix-сокет, и, во-первых, взломщик в принципе не сможет узнать, где эти Unix-сокеты вообще лежат, а во-вторых, даже если узнает или догадается — система отлупит его с Permission denied, потому что пользователь не тот. Даже файрвол не нужен, всё делается банальными chown/chmod

Чем это принципиально отличается от банального запуска разных сервисов под разными юзерами?

В первом варианте pattern2.match не будет вызываться лишний раз. Во втором варианте он будет вызываться всегда, что потенциально может замедлить программу (иногда сильно)

Во-первых, распарсить xml и html может любой школьник за полчаса, это не какая-то сверхсложная наука.


Во-вторых, я не очень понял, почему вы вообще решили, что Stackoverflow будет что-то за кого-то парсить? В том бесплатном API, который я нашёл, отдаётся тот же самый html. Вы где-то вычитали, что платный API будет чем-то принципиально отличаться, или о чём речь?

Information

Rating
1,722-nd
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Date of birth
Registered
Activity