Определение страны по IP-адресу Python
Общие вопросы python ip, страна пользователя python, узнать ip python
При сборе статистики посещения web-страниц часто собирается информация о количестве посетителей из разных стран. Как правило, страну определяют по домену первого уровня. Но такая информация не всегда соответствует действительности, особенное учитывая нынешнюю тендецию использовать национальные домены co, tv не по назначению. Кроме того, как быть с доменами общего пользования net, org, com и др.? С IP-адресами, для которых нет записей в реверсной зоне? Ну и, наконец, определение доменного имени отнимает заметное количество времени.
Приведенный в статье код распространяется под лицензией в стиле Python, то есть может быть использован для любых (в том числе коммерческих целей) при условии сохранения замечания об авторском праве Copyright © 2002, Denis S. Otkidach
Данные о регистрации диаппазонов IP-адресов хранятся в базах данных whois. Чтобы предоставить возможность общественности анализировать трафик, RIPE NCC, ARIN и APNIC не реже, чем раз в месяц, делают сокращенные "снимки" своих баз данных. Именно из этих данных мы и составим локальную базу.
Но сначала нужно эффективно оргазовать хранение данных для диаппазонов IP-адресов, чтобы обеспечить к ним быстрый доступ. За основу возмем BTree базу BerkleyDB, доступ к которой обеспечивает функция btopen() из стандартного модуля bsddb. В качестве ключей будем использовать начало диаппазона IP-адресов, а в качестве значений — его конец и дополнительную информацию. Ключи и значения в bsddb должны быть строками. Кроме того, необходимо обеспечить упорядоченность ключей. Для этого очень хорошо подходит функции inet_aton и inet_ntoa из модуля socket.
from bsddb import btopen from socket import inet_aton, inet_ntoa class IPRangeDB: def __init__(self, filename, mode='r'): self.__db = btopen(filename, mode) def close(self): self.__db.close() def _locate(self, ip): db = self.__db try: first, record = db.set_location(ip) except KeyError: try: first, record = db.last() except KeyError: raise KeyError(inet_ntoa(ip)) else: if first!=ip: first, record = db.previous() assert first<=ip return first, record def __getitem__(self, ip_str): ip = inet_aton(ip_str) first, record = self._locate(ip) last = record[:4] assert last>=first if ip<=last: return self.unpack(record[4:]) else: raise KeyError(ip_str) def add(self, first_str, last_str, info): first = inet_aton(first_str) last = inet_aton(last_str) try: db_first, record = self._locate(last) except KeyError: pass else: db_last = record[:4] if first<=db_last: raise ValueError( 'Range %s-%s intersects ' % (first_str, last_str) + 'with existing entry %s-%s' % (inet_ntoa(db_first), inet_ntoa(db_last))) self.__db[first] = last+self.pack(info) def pack(self, info): return info def unpack(self, info): return info
Метод _locate() ищет запись с максимальной нижней границей, меньшей или равной IP-адресу, переданному в качестве аргумента. Метод __getitem__() позволяет использовать экземпляры класса IPRangeDB аналогично словарям: db[ip] вернет информацию о диаппазоне, в который входит адрес ip. Использовать интерфейс словаря для записи врядли будет хорошей идеей, так как запись создается одна для всего диаппазона. Чтобы избежать путаницы, добавление записей реализовано через метод add(). И, наконец, пара методов pack() и unpack() определены, чтобы производный класс можно было легко адоптировать для хранения произвольной информации, метод pack() должен преобразовывать объект в строку.
Возникли проблемы с картриджом? Советуем заправить картридж Киев по самым выгодным ценам на рынке.
>>> db = IPRangeDB('test.db', 'c') >>> db.add('10.0.0.0', '10.255.255.255', 'Наша локальная сеть') >>> print db['10.1.2.3'] Наша локальная сеть >>> print db['123.45.67.89'] Traceback (most recent call last): File "", line 1, in ? File "ip2cc.py", line 38, in __getitem__ raise KeyError(ip_str) KeyError: 123.45.67.89
Осталось дело за малым: определить методы для наполнения базы данных.
from urllib import urlopen from xreadlines import xreadlines from time import strptime import struct class CountryByIP(IPRangeDB): sources = { 'arin' : ('ftp://ftp.arin.net/pub/stats/arin/', 'arin.%Y%m%d'), 'ripencc': ('ftp://ftp.ripe.net/ripe/stats/', 'ripencc.%Y%m%d'), 'apnic' : ('ftp://ftp.apnic.net/pub/stats/apnic/', 'apnic-%Y-%m-%d') } def fetch(self): for name in self.sources: fp = self.__openRecent(name) for line in xreadlines(fp): parts = line.strip().split('|') if len(parts)==7 and parts[2]=='ipv4' and parts[6] in ('allocated', 'assigned') and name==parts[0]: first = parts[3] first_int = struct.unpack('!i', inet_aton(first))[0] last_int = first_int+int(parts[4])-1 last = inet_ntoa(struct.pack('!i', last_int)) try: self.add(first, last, parts[1].upper()) except ValueError: pass def __openRecent(self, name): uri, format = self.sources[name] files = [] for line in xreadlines(urlopen(uri)): file = line.split()[-1] try: dt = strptime(file, format) except ValueError: pass else: files.append((dt, file)) files.sort() return urlopen(uri+files[-1][1])
Метод __openRecent находит самый свежий "снимок" и возвращает файловый объект. Дата "снимка" определяется по имени файла по шаблону из словаря источников sources. Метод fetch анализирует данные, выбирает необходимое и добавляет в базу. Использование модуля xreadlines позволяет анализировать данные по мере поступления.
Теперь можно наполнить базу
>>> db = CountryByIP('test.db', 'n') >>> db.fetch()
и использовать
>>> from socket import gethostbyname >>> db[gethostbyname('python-3.ru')] 'NL'Определить страну можно и для сайтов, лучше знать заранее для пользователей каких стран нацелен сайт и для какой страны он будет работать быстрее. Определить пользователей легко, сложнее будет их получить и удержать, в этом вам поможет сайт по ссылке.
Преобразование кода A2 в название страны по таблице ISO3166 пусть останется вам в качестве упражнения.
- Увлекательный мир клуба Вулкан Delux
- Игровой автомат Valley of The Gods - в казино Вулкан Вегас играй в слоты от Yggdrasil
- Игровой автомат Big Bad Wolf - в казино Вулкан играть в слоты от Quickspin
- Профессиональный ремонт ноутбуков и мобильной техники. Нюансы и тонкости
- Игровой автомат Lucky Haunter - в казино Плей Фортуна по крупному выиграй
- Игровой автомат Steam Tower - большие выигрыши ждут в Вулкан Вегас казино
- Игровой автомат Viking Gods Thor and Loki - в мобильное казино Вулкан 24 поймай удачу