Проект, который не отнимет много времени, но даст опыт, да и положительные эмоции.

Описание

С помощью скрипта и IP-адреса вычисляем местоположение. Определить точную геолокацию по IP-адресу невозможно: все сервисы, позволяющие находить информацию по IP, могут определить местоположение только на уровне города. Невозможно вычислить ваш или любой другой точный домашний адрес по IP. Это могут сделать правоохранительные органы только в том случае, если они обратятся к Интернет-провайдеру в случае нарушения вами закона.

Код

Создадим 2 файла, которые будут иметь разный функционал:

  • 1 main.py обрабатывает IP-адрес

  • 2 database.py создаёт базу данных и добавляет данные

Начнём с 1-го файла. Всё ещё импортируем то, что потребуется для реализации проекта.

import requests
import database

Напоминаем, что database - это 2-й файл.

Первая функция - main().

Функция main() используется для разделения блоков кода в программе. Использование функции main() обязательно в таких языках, как Java, потому что это упрощает понимание того, в каком порядке код запускается в программе. В Python функцию main() писать необязательно, но это улучшает читаемость кода.

Функция принимает в виде аргумента строку, которая просит ввести IP-адрес. Затем main() передаёт функции location() данные.

def main(start: str):
    print(start)
    ip = input("IP address: ")
    try:
        new_data = location(ip)
        database.base(new_data)
    except ValueError:
        pass


if __name__ == "__main__":
    main("Enter the IP address")

Вторая функция location() принимает в виде аргумента строку с IP-адресом. Отправляем запрос с помощью метода get.

GET является одним из самых популярных HTTP методов. Метод GET указывает на то, что происходит попытка извлечь данные из определенного ресурса. Для того, чтобы выполнить запрос GET, используется requests.get().

Используя .status_code, можно увидеть код состояния, который возвращается с сервера.

Если будет выведено 404, то значит что-то пошло не так.

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

Enter the IP address
IP address: 185.101.203.42
[Status]: success
[Country]: Болгария
[Countrycode]: BG
[Region]: 22
[Regionname]: Sofia-Capital
[City]: София
[Zip]: 1000
[Lat]: 42.6951
[Lon]: 23.325
[Timezone]: Europe/Sofia
[Isp]: SIA "Singularity Telecom"
[Org]: SIA "Singularity Telecom"
[As]: AS209372 SIA "Singularity Telecom"
[Query]: 185.101.203.42

Возвращаем кортеж с данными.

def location(ip: str):
    response = requests.get(f"http://ip-api.com/json/{ip}?lang=ru")
    if response.status_code == 404:
        print("Oops")
    result = response.json()
    if result["status"] == "fail":
        return main("Enter the correct IP address")

    record = []

    for key, value in result.items():
        record.append(value)
        print(f"[{key.title()}]: {value}")
    return tuple(record)

Теперь переходим ко второму файлу database.py, где будет создана база данных с добавлением новых данных.

Импорт, как всегда, с нами. Для базы данных потребуется библиотека sqlite3.

import sqlite3

Создаём функцию, которая принимает аргумент в виде кортежа.

Во-первых, нам нужно создать новую базу данных и открыть подключение к базе данных, чтобы разрешить sqlite3 работать с ней. Вызов sqlite3.connect()поможет нам в том. Если базы данных database.db не существует, то будет неявно создана.

Чтобы выполнять инструкции SQL и извлекать результаты из SQL-запросов, нам нужно будет использовать курсор базы данных. Вызов con.cursor() в деле.

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

Если в базе данных уже присутствует какой-либо IP-адрес, то выводим "Duplicate ". Если нет, то добавляем в базу новые данные. Вызов conn.commit() зафиксирует транзакцию, скажем так. Так будет выглядеть база данных.

Таблица
Таблица
def base(data: tuple):
    conn = sqlite3.connect("database.db")
    cur = conn.cursor()
    cur.execute("""CREATE TABLE IF NOT EXISTS location(
    Status TEXT,
    Country TEXT,
    Countrycode TEXT,
    Region TEXT,
    Regionname TEXT,
    City TEXT,
    Zip INT,
    Lat REAL,
    Lon REAL,
    Timezone TEXT,
    Isp TEXT,
    Org TEXT,
    Auto_system TEXT,
    Query TEXT);
    """)
    try:
        check = cur.execute(f"SELECT * FROM location WHERE Query=?", (data[-1],))
        if len(list(*check)) == 0:
            cur.execute("INSERT INTO location VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?);", data)
            conn.commit()
        else:
            print("Duplicate")
    except TypeError:
        pass

Сценарий

main.py

# Location by IP
# Location search by IP address using Python
import requests
import database


def location(ip: str):
    response = requests.get(f"http://ip-api.com/json/{ip}?lang=ru")
    if response.status_code == 404:
        print("Oops")
    result = response.json()
    if result["status"] == "fail":
        return main("Enter the correct IP address")

    record = []

    for key, value in result.items():
        record.append(value)
        print(f"[{key.title()}]: {value}")
    return tuple(record)


def main(start: str):
    print(start)
    ip = input("IP address: ")
    try:
        new_data = location(ip)
        database.base(new_data)
    except ValueError:
        pass


if __name__ == "__main__":
    main("Enter the IP address")

database.py

import sqlite3


def base(data: tuple):
    conn = sqlite3.connect("database.db")
    cur = conn.cursor()
    cur.execute("""CREATE TABLE IF NOT EXISTS location(
    Status TEXT,
    Country TEXT,
    Countrycode TEXT,
    Region TEXT,
    Regionname TEXT,
    City TEXT,
    Zip INT,
    Lat REAL,
    Lon REAL,
    Timezone TEXT,
    Isp TEXT,
    Org TEXT,
    Auto_system TEXT,
    Query TEXT);
    """)
    try:
        check = cur.execute(f"SELECT * FROM location WHERE Query=?", (data[-1],))
        if len(list(*check)) == 0:
            cur.execute("INSERT INTO location VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?);", data)
            conn.commit()
        else:
            print("Duplicate")
    except TypeError:
        pass

Заключение

Некоторые компании специализируются на сборе информации о диапазоне IP-адресов со всего мира. Они продают эту информацию в виде консолидированных баз данных, которые легко интегрируются в любой веб-сервер с целью быстрого поиска информации о стране, регионе, городе или Интернет-провайдере. Точность этих баз данных колеблется от 80 до 99,8%, согласно их собственным утверждениям. Данный проект показал, как можно легко написать маленький скрипт, который будет собирать необходимую информацию с помощью таких баз.

Ссылка на GitHub

Комментарии (36)


  1. aborouhin
    07.04.2023 18:01
    +5

    Вы бы хоть введённый IP на валидность regexp'ом проверяли, прежде чем скармливать что попало стороннему API. Да и коды ошибок в HTTP бывают другие, кроме 404...

    P.S. Мне казалось, что статья про написание очередного hello world интересна разве что если это hello world на каком-то экзотическом языке :) А тут ещё и уровень "средний" - что же тогда "лёгкий"?..


    1. makarov2000 Автор
      07.04.2023 18:01
      -1

      Не имею желания расписывать подробный ответ, но могу сказать точно, что ваш комментарий полезен для меня. Так как я являюсь начинающим программистом, комментарии, которые указывают на недочёты, ошибки - это топ. Если говорить про ориентиры для разных уровней сложностей, то, возможно, мне нужно будет пересмотреть их.

      Дорогу осилит идущий, а программирование освоит решающий!


      1. grobitto
        07.04.2023 18:01
        +1

        Это все прекрасно, но зачем на Хабр? 99% из прочитавших статью просто потеряли свое время


        1. makarov2000 Автор
          07.04.2023 18:01
          -2

          1. Русскоязычный;

          2. Блог на Хабре будет плюсом, когда начну устраиваться на работу;

          3. Нравится сама площадка.


          1. sshikov
            07.04.2023 18:01

            Блог на Хабре будет плюсом, когда начну устраиваться на работу;

            Почему вы так решили? Вы разве нанимаете? Конкретно такой текст будет скорее минусом (и да, я нанимаю).


            1. makarov2000 Автор
              07.04.2023 18:01

              Не нанимаю, но читаю разных работодателей. Это не последний текст, проект, который собираюсь писать. Если в будущем работодатель будет смотреть на самую первую статью, один из самых первых проектов и считать это как за минус, то для меня это покажется странным


              1. sshikov
                07.04.2023 18:01
                +1

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


                Ну вот и тут примерно так же — выделиться из массы подобный текст не позволит. Ну во всяком случае — как по мне. Но пробовать дальше конечно стоит.


                1. makarov2000 Автор
                  07.04.2023 18:01

                  Если читать ваш комментарий, то сразу появляется вопрос. А как выделиться из массы?


                  1. whoisking
                    07.04.2023 18:01
                    +1

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

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


                    1. makarov2000 Автор
                      07.04.2023 18:01
                      +1

                      Спасибо за развернутый ответ!


                  1. sshikov
                    07.04.2023 18:01

                    А вы думаете я знаю? На моем уровне (а я 45 лет программирую) это точно так же сложно. Все иначе — но выделиться все равно проблема.


              1. HemulGM
                07.04.2023 18:01

                Работодателю будет достаточно GitHub, а статья на хабре уйдёт в минус, потому что не представляет из себя ничего полезного. Код на гите вы сможете совершенствовать (а здесь есть что совершенствовать). Здесь же в нём нет никакого смысла


              1. Rinsewind
                07.04.2023 18:01

                Будущий работодатель явно сочтёт вас упорным


          1. grobitto
            07.04.2023 18:01

            Прежде чем начать писать нужно очень, очень, очень много читать :)


            1. user18383
              07.04.2023 18:01

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


    1. RinatMambetov
      07.04.2023 18:01

      Я новичок и мне понравилось! Давайте ещё! )


  1. sden77
    07.04.2023 18:01
    +2

    Мануал по работе с локальной бд maxmind на python был бы более полезным


    1. makarov2000 Автор
      07.04.2023 18:01

      Не очень понятно для меня данное утверждение, но ладно


      1. sshikov
        07.04.2023 18:01

        А загуглить maxmind? )


        1. makarov2000 Автор
          07.04.2023 18:01

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


          1. sshikov
            07.04.2023 18:01

            Потому что это автономно. Не уверен, полезнее ли это или нет — у каждого варианта есть свои плюсы и минусы. Но то что это другое решение — точно.


        1. Dynasaur
          07.04.2023 18:01

          А загуглить maxmind туториал ?


          1. makarov2000 Автор
            07.04.2023 18:01

            Дошёл до той статьи, где моя "Не понятность" отпала


      1. sden77
        07.04.2023 18:01

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


    1. Sadok
      07.04.2023 18:01

      питон не в тренде. на питоше только на смузи хватит. даешь раст.

      а так mmdblookup и grep в руки


  1. Dynasaur
    07.04.2023 18:01

    А мне понравилось. Простенькое не напряжное упражнение :-)


    1. OLD66
      07.04.2023 18:01

      А какой смысл вообще искать город)))? разве что от нечего делать? Причем в начале статьи написать что, точность очень низкая))) Вот чисто практически?

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

      Немного не в тему )

      Прочитал кучу статей про NFC метки и так не нашел реально полезного применения, что бы замудриться с покупкой программатора!!! У кого есть идеи с удовольствием почитаю ))

      Про гостевой ВФ можете сразу не писать))


      1. Sadok
        07.04.2023 18:01

        Прочитал кучу статей про NFC метки и так не нашел реально полезного применения

        загран паспорт новый, который на 10 лет. (про старого образца уже не скажу). даже flipper zero какие-то циферки оттуда читает. шесть групп по два hex-символа, кажется.


  1. iosuslov
    07.04.2023 18:01
    +3

    Целая кучка bad practises, во некоторые из них:

    1. Нейминг функций/методов - location нужно переименовать в get_location, например. Чтобы было понятно, что это не какой-то объект, а запрос получения данных. database.base - нужно переименовать в database.write (например)

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

    3. Не совсем понятно, зачем json ответа стороннего сервиса превращается в какую-то структуру из ф-строк и таплов - внутри сервиса работаем с жсоном напрямую, в базейку раскладываем по полям. Для удобства можно использовать dataclass, и раскладывать в него жсон

    4. Обработка ошибок - print('Oops'), нужно заменить на что то более вменяемое. Хотя бы 'requested ip adress not found'(если 404 внешнего сервиса означает это). В идеале, написать свои кастомные Exceptions, которые будут логировать различные ответы стороннего сервиса

    5. Простановка тайпхинтов - нужно проставлять возвращаемые типы


    1. makarov2000 Автор
      07.04.2023 18:01

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


  1. sden77
    07.04.2023 18:01

    Как мне кажется, достаточно кешировать только первые 3 байта ip адреса (сеть /24)


    1. makarov2000 Автор
      07.04.2023 18:01

      Возможно. Можно проверить


  1. HemulGM
    07.04.2023 18:01
    +3

    Представлю вашему вниманию следующее. Исключительно для размышления.

    Вот что я написал руками:

    procedure TForm5.ButtonQueryClick(Sender: TObject);
    begin
      RESTRequest1.Resource := Edit1.Text;
      RESTRequest1.Execute;  //<- тут выполняется запрос к серверу ip-api
    end;
    
    procedure TForm5.FormCreate(Sender: TObject);
    begin
      FDQuery1.ExecSQL; //<- тут выполняется запрос на создание таблицы, если её нет
    end;

    Ни строчки более. Вот что мы имеем на выходе:

    Все складывается бд, а при старте видим то, что уже запросили

    P.S. Zip - это не интовое число, поле может быть текстом


    1. HemulGM
      07.04.2023 18:01

      Поправка. Эта строчка не нужна) RESTRequest1.Resource := Edit1.Text;


  1. randomsimplenumber
    07.04.2023 18:01

    Это project 2. А где project 1? ;)


    1. makarov2000 Автор
      07.04.2023 18:01

      Был съеден Песочницей