Проект, который не отнимет много времени, но даст опыт, да и положительные эмоции.
Описание
С помощью скрипта и 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%, согласно их собственным утверждениям. Данный проект показал, как можно легко написать маленький скрипт, который будет собирать необходимую информацию с помощью таких баз.
Комментарии (36)
sden77
07.04.2023 18:01+2Мануал по работе с локальной бд maxmind на python был бы более полезным
makarov2000 Автор
07.04.2023 18:01Не очень понятно для меня данное утверждение, но ладно
sshikov
07.04.2023 18:01А загуглить maxmind? )
makarov2000 Автор
07.04.2023 18:01"Не понятность" тут заключается в том, почему это будет более полезным
sshikov
07.04.2023 18:01Потому что это автономно. Не уверен, полезнее ли это или нет — у каждого варианта есть свои плюсы и минусы. Но то что это другое решение — точно.
sden77
07.04.2023 18:01Не понятно, зачем дёргать внешний апи, если всё это можно хранить локально и получать информацию по любому айпи на порядки быстрее. Также отпадает необходимость в дополнительных костылях в виде кеша в БД.
Sadok
07.04.2023 18:01питон не в тренде. на питоше только на смузи хватит. даешь раст.
а так mmdblookup и grep в руки
Dynasaur
07.04.2023 18:01А мне понравилось. Простенькое не напряжное упражнение :-)
OLD66
07.04.2023 18:01А какой смысл вообще искать город)))? разве что от нечего делать? Причем в начале статьи написать что, точность очень низкая))) Вот чисто практически?
Я вообще ни разу не программист но должно быть практическое применение, что бы на это тратить время)
Немного не в тему )
Прочитал кучу статей про NFC метки и так не нашел реально полезного применения, что бы замудриться с покупкой программатора!!! У кого есть идеи с удовольствием почитаю ))
Про гостевой ВФ можете сразу не писать))
Sadok
07.04.2023 18:01Прочитал кучу статей про NFC метки и так не нашел реально полезного применения
загран паспорт новый, который на 10 лет. (про старого образца уже не скажу). даже flipper zero какие-то циферки оттуда читает. шесть групп по два hex-символа, кажется.
iosuslov
07.04.2023 18:01+3Целая кучка bad practises, во некоторые из них:
Нейминг функций/методов - location нужно переименовать в get_location, например. Чтобы было понятно, что это не какой-то объект, а запрос получения данных. database.base - нужно переименовать в database.write (например)
Создание таблицы должно происходить отдельно от запросов в нее. Например, при старте приложения создаётся таблица, далее создаётся объект базы, в котором есть методы подключения к базе, отключения от базы (или контекстный менеджер) и метод для осуществления запроса.
Не совсем понятно, зачем json ответа стороннего сервиса превращается в какую-то структуру из ф-строк и таплов - внутри сервиса работаем с жсоном напрямую, в базейку раскладываем по полям. Для удобства можно использовать dataclass, и раскладывать в него жсон
Обработка ошибок - print('Oops'), нужно заменить на что то более вменяемое. Хотя бы 'requested ip adress not found'(если 404 внешнего сервиса означает это). В идеале, написать свои кастомные Exceptions, которые будут логировать различные ответы стороннего сервиса
Простановка тайпхинтов - нужно проставлять возвращаемые типы
makarov2000 Автор
07.04.2023 18:01Ваш комментарий уж точно заставит меня снова залезть в код и пересмотреть его. Благодарю за такие комментарии!
sden77
07.04.2023 18:01Как мне кажется, достаточно кешировать только первые 3 байта ip адреса (сеть /24)
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 - это не интовое число, поле может быть текстом
aborouhin
Вы бы хоть введённый IP на валидность regexp'ом проверяли, прежде чем скармливать что попало стороннему API. Да и коды ошибок в HTTP бывают другие, кроме 404...
P.S. Мне казалось, что статья про написание очередного hello world интересна разве что если это hello world на каком-то экзотическом языке :) А тут ещё и уровень "средний" - что же тогда "лёгкий"?..
makarov2000 Автор
Не имею желания расписывать подробный ответ, но могу сказать точно, что ваш комментарий полезен для меня. Так как я являюсь начинающим программистом, комментарии, которые указывают на недочёты, ошибки - это топ. Если говорить про ориентиры для разных уровней сложностей, то, возможно, мне нужно будет пересмотреть их.
Дорогу осилит идущий, а программирование освоит решающий!
grobitto
Это все прекрасно, но зачем на Хабр? 99% из прочитавших статью просто потеряли свое время
makarov2000 Автор
Русскоязычный;
Блог на Хабре будет плюсом, когда начну устраиваться на работу;
Нравится сама площадка.
sshikov
Почему вы так решили? Вы разве нанимаете? Конкретно такой текст будет скорее минусом (и да, я нанимаю).
makarov2000 Автор
Не нанимаю, но читаю разных работодателей. Это не последний текст, проект, который собираюсь писать. Если в будущем работодатель будет смотреть на самую первую статью, один из самых первых проектов и считать это как за минус, то для меня это покажется странным
sshikov
Вам может показаться что угодно — но работодателя это не волнует. Не, ну минусом это вряд ли будет — я преувеличиваю, но плюсом тоже вряд ли. В чем плюс-то? Что вы решили тривиальную задачу, которая должна решаться на работе по штуке в день в нормальном рабочем режиме? Ну да, вы продемонстрировали, что вы что-то умеете (в том числе — как-то описывать свою работу). Ну не минус конечно… но вот знаете, мы уже с месяц примерно ищем разработчика. И проблема в том, что все попадающиеся — на одно лицо. Их как будто выпускала одна автоматическая линия — они знают все одно и тоже, и в резюме пишут это же. Только названия компаний отличаются. При этом они все люди с каким-то но опытом, иногда даже большим, но никто из них не выделяется ровным счетом ничем.
Ну вот и тут примерно так же — выделиться из массы подобный текст не позволит. Ну во всяком случае — как по мне. Но пробовать дальше конечно стоит.
makarov2000 Автор
Если читать ваш комментарий, то сразу появляется вопрос. А как выделиться из массы?
whoisking
Это очень хороший вопрос. Я, будучи джуном, несколько раз хотел написать статью на хабр и даже начинал. Мне везло, на нормальную статью материала не хватало и условно через месяц я понимал, что мой текст очень слабый, что мне не о чем на самом деле рассказывать.
Я не знаю идеальных решений, но предложу 3 варианта - 1) брать околоновостные темы - например, вышла новая библиотека/фреймворк/технология и разобраться в ней, описать важные моменты, можно сделать это в виде грамотной компиляции из нескольких источников 2) перевод или компиляция переводов 3) на форумах/в чатах описать свой бэкграунд, желаемую область развития и попросить более опытных коллег набросать тем, которые можно разобрать, изучить и по которым можно написать статью.
makarov2000 Автор
Спасибо за развернутый ответ!
sshikov
А вы думаете я знаю? На моем уровне (а я 45 лет программирую) это точно так же сложно. Все иначе — но выделиться все равно проблема.
HemulGM
Работодателю будет достаточно GitHub, а статья на хабре уйдёт в минус, потому что не представляет из себя ничего полезного. Код на гите вы сможете совершенствовать (а здесь есть что совершенствовать). Здесь же в нём нет никакого смысла
Rinsewind
Будущий работодатель явно сочтёт вас упорным
grobitto
Прежде чем начать писать нужно очень, очень, очень много читать :)
user18383
Не обязательно. Самое главное что мотивы для написания были не деньги а интерес и желание рассказать что нибудь интересное.
RinatMambetov
Я новичок и мне понравилось! Давайте ещё! )