问题描述
我尝试在Python Google Appengine上执行一些IP查找:
import pygeoip
gi = pygeoip.GeoIP('GeoIP.dat')
Location = gi.country_code_by_addr(self.request.remote_addr)
(pygeoip可以在这里找到:)
上面的代码在本地执行正常,但是当我将它推送到活动服务器时,出现以下错误:
< type'exceptions.ImportError'>:没有模块名为mmap
Traceback(最近一次调用最后一次):
文件/base/data/home/apps/tomcritchlow1/geoip.347423765058502279/main.py,第28行,在< module>
导入pygeoip
文件/base/data/home/apps/tomcritchlow1/geoip.347423765058502279/pygeoip/__init__.py,第34行,位于< module>
import mmap
我在本地使用Python 2.5,所以它应该与在线服务器我认为?
Pygeoip页面显示以下内容:
支持的标志:
STANDARD(从磁盘读取所有内容,最慢)
MEMORY_CACHE
MMAP_CACHE(将整个文件映射到内存,最快)
我不确定支持的标志是什么意思 - 如果mmap是问题,我可以使用Pygeoip而不是mmap使用内存缓存吗?我会怎么做?
谢谢!
Tom
PS - 仍然是一个n00b编码,所以请放心:)
$ b 编辑:
下面概述的解决方案的工作原理是,首先使用以下代码:
gi = pygeoip.GeoIP('GeoIP。 dat',flags = pygeoip.const.MEMORY_CACHE)
然后修改 init .py在pygeoip中的文件如下所示(警告!我刚刚注释掉了一些代码,没有做任何奇怪的事情!)
<$ c $这个API基于U {MaxMind的基于C的Python API< http://www.maxmind.com/app/python>},
,但是代码本身基于Jim Winstead和Hans Lellelid的U {pure PHP5 API< http://pear.php.net/package/Net_GeoIP/>}
。
它除了
C {new}和C {open}方法不在之外,您应该自己实例化L {GeoIP}类:
C { gi = GeoIP('/ path / to / GeoIP.dat',pygeoip.MEMORY_CACHE)}
@author:Jennifer Ennis< zaylea at gmail dot com>
@license:
版权所有(C)2004 MaxMind LLC
本程序是免费软件:您可以重新发布和/或修改
由自由软件基金会
发行的GNU宽通用公共许可证的条款,或许可证的第3版,或任何更新版本的b $ b(可选)。
这个程序的分发是希望它有用,
但是没有任何保证;甚至没有隐含的保证
的适销性或特定用途的适用性。有关更多详细信息,请参阅
GNU通用公共许可证。
您应该收到GNU较宽松通用公共许可证
的副本以及此程序。如果没有,请参阅< http://www.gnu.org/licenses/lgpl.txt> ;.
$ b $ from __future__ import with_statement
import os
进口数学
导入套接字
#import mmap
from const导入* from util
导入ip2long
class GeoIPError(例外):
pass
$ b $ class GeoIPMetaclass(type):
def __new __(cls,* args,** kwargs):
使用Singleton方法获取实例而不重新分析数据库。独特的
实例基于db的文件名实例化。标志是
被忽略,也就是说,如果你用STANDARD标志(默认)
初始化一个标志,然后尝试用MEMORY_CACHE初始化,它仍然会返回STANDARD标志。
如果不是hasattr(cls,'_instances'):
cls._instances = {}
如果len(args)> ; 0:
文件名= args [0]
elif'文件名'在kwargs中:
文件名= kwargs ['文件名']
如果文件名不是cls。 _instances:
cls._instances [filename] = type .__ new __(cls,* args,** kwargs)
return cls._instances [文件名]
GeoIPBase = GeoIPMetaclass('GeoIPBase',(object,),{})
class GeoIP(GeoIPBase):
def __init __(self,filename,flags = 0):
初始化班级。
@param文件名:geoip数据库的路径
@type文件名:str
@param flags:影响数据库处理方式的标志。
目前唯一支持的标志是STANDARD,MEMORY_CACHE和
MMAP_CACHE。
@type标志:int
self._filename =文件名
self._flags =标志
#tom:
#如果self._flags& MMAP_CACHE:
if'2'=='3':
with open(filename,'rb')as f:
self._filehandle = mmap.mmap(
$ b else:
self._filehandle = open :
#if self._flags& MEMORY_CACHE:
#self._memoryBuffer = self._filehandle.read()
self._setup_segments()
def _setup_segments(self):
解析数据库文件以确定正在使用哪种数据库并设置
段的大小和查找所使用的起点* ()方法。
self._databaseType = COUNTRY_EDITION
self._recordLength = STANDARD_RECORD_LENGTH
filepos = self._filehandle.tell()
self._filehandle。 seek(-3,os.SEEK_END)
(STRUCTURE_INFO_MAX_SIZE):
delim = self._filehandle.read(3)
if delim = =(chr(255)* 3):
self._databaseType = ord(self._filehandle.read(1))
if(self._databaseType> = 106):
#向后兼容于2003年4月及以前的数据库
self._databaseType - = 105
如果self._databaseType == REGION_EDITION_REV0:
self._databaseSegments = STATE_BEGIN_REV0
elif self._databaseType == REGION_EDITION_REV1:
self._databaseSegments = STATE_BEGIN_REV1
elif self._databaseTyp (CITY_EDITION_REV0,
CITY_EDITION_REV1,
ORG_EDITION,
ISP_EDITION,
ASNUM_EDITION):
self._databaseSegments = 0
buf = self._filehandle.read (SEGMENT_RECORD_LENGTH)
(SEGMENT_RECORD_LENGTH):
self._databaseSegments + =(ord(buf [j])<< (j * 8))
if(ORG_EDITION,ISP_EDITION)中的self._databaseType:
self._recordLength = ORG_RECORD_LENGTH
break
else:
self._filehandle.seek(-4,os.SEEK_CUR)
if self._databaseType == COUNTRY_EDITION:
self._databaseSegments = COUNTRY_BEGIN
self ._filehandle.seek(filepos,os.SEEK_SET)
def _lookup_country_id(self,addr):
获取国家/地区索引
这个方法由_lookupCountryCode和_lookupCountryName
方法调用,它查找国家的索引('id'),它是代码和名称的关键
@param addr:IP地址
@type addr:str
@return:网络字节顺序32位整数
@rtype:int
ipnum = ip2long(地址)
如果不是ipnum:
raise valueError(无效的IP地址:%s%addr)
如果self._databaseType!= COUNTRY_EDITION:
raise GeoIPError('Invalid database type; country_ * methods expect'\
'Country database')
return self._seek_country(ipnum) - COUNTRY_BEGIN
def _seek_country(self,ipnum):
使用记录长度和适当的起始点,寻找对应于转换的IP地址整数的
国家。
@param ipnum:结果ip2long转换
@type ipnum:int
@return:开始记录的偏移
@rtype:int
offset = 0
表示深度范围(31,-1,-1):
#tom:
#if self._flags& MEMORY_CACHE:
if'2'=='3':
startIndex = 2 * self._recordLength * offset
length = 2 * self._recordLength
endIndex = startIndex + length
buf = self._memoryBuffer [startIndex:endIndex]
else:
self._filehandle.seek(2 * self._recordLength * offset,os.SEEK_SET)
buf = self._filehandle .read(2 * self._recordLength)
x = [0,0]
为范围内的我(2):
为范围内的j(自我。 _recordLength):
x [i] + = ord(buf [self._recordLength * i + j])< (j * 8)
if ipnum& (1
如果x [1]> = self._databaseSegments:
return x [1]
offset = x [ 1]
else:
if x [0]> = self._databaseSegments:
return x [0]
offset = x [0]
异常('错误遍历数据库 - 可能已损坏?')
$ b $ def _get_org(self,ipnum):
寻找并返回转换IP地址的组织(或ISP)名称
@param ipnum:转换的IP地址
@type ipnum:int
@return: org / isp name
@rtype:str
seek_org = self._seek_country(ipnum)
if seek_org == self._databaseSegments:
返回None
record_pointer = seek_org +(2 * self._recordLength - 1)* self._databaseSegments
self._filehandle .seek(record_pointer,os.SEEK_SET)
org_buf = self._filehandle.read(MAX_ORG_RECORD_LENGTH)
return org_buf [:org_buf.index(chr(0))]
def _get_region(self,ipnum):
寻找并返回区域信息(包含country_code和region_name的字典)。
@param ipnum:转换的IP地址
@type ipnum:int
@return:dict包含country_code和region_name
@rtype:dict
country_code =''
region =''
if self._databaseType == REGION_EDITION_REV0:
seek_country = self._seek_country(ipnum)
seek_region = seek_country - STATE_BEGIN_REV0
if seek_region> = 1000:
country_code ='US'
region =''.join([chr((seek_region / 1000)/ 26 + 65),chr ((seek_region / 1000)%26 + 65)])
else:
country_code = COUNTRY_CODES [seek_region]
region =''
elif self._databaseType == REGION_EDITION_REV1:
seek_country = self._seek_country(ipnum)
seek_region = seek_country - STATE_BEGIN_REV1
如果seek_region country_code ='';
region =''
elif seek_region< CANADA_OFFSET:
country_code ='US'
region =''.join([chr((seek_region - US_OFFSET)/ 26 + 65),chr((seek_region - US_OFFSET)%26 + 65)])
elif seek_region< WORLD_OFFSET:
country_code ='CA'
region =''.join([chr((seek_region - CANADA_OFFSET)/ 26 + 65),chr((seek_region - CANADA_OFFSET)%26 + 65)])
else:
i =(seek_region - WORLD_OFFSET)/ FIPS_RANGE
如果我在COUNTRY_CODES:
country_code = COUNTRY_CODES [(seek_region - WORLD_OFFSET)/ FIPS_RANGE]
else:
country_code =''
region =''
elif self._databaseType in(CITY_EDITION_REV0,CITY_EDITION_REV1):
rec = self._get_record(ipnum)
country_code = rec ['country_code']
region = rec ['region_name']
return {'country_code':country_code,'region_name':region}
def _get_record(self,ipnum):
填充已转换IP的位置字典
@param ipnum:转换后的IP地址
@type ipnum:int
@return:dict with country_code,country_code3,country_name,
region,city,postal_code,latitude,longitude,
dma_code ,metro_code,area_code,region_name,time_zone
@rtype:dict
seek_country = self._seek_country(ipnum)
if seek_country == self._databaseSegments:
返回None
record_pointer = seek_country +(2 * self._recordLength - 1)* self._databaseSegments
self._filehandle.seek(record_pointer,os.SEEK_SET)
record_buf = self._filehandle.read(FULL_RECORD_LENGTH)
record = {}
$ b record_buf_pos = 0
char = ord(record_buf [record_buf_pos])
record ['country_code'] = COUNTRY_CODES [char]
record ['country_code3'] = COUNTRY_CODES3 [char]
record ['country_name'] = COUN TRY_NAMES [char]
record_buf_pos + = 1
str_length = 0
#get region
char = ord(record_buf [record_buf_pos + str_length])
while (char!= 0):
str_length + = 1
char = ord(record_buf [record_buf_pos + str_length])
if str_length> 0:
record ['region_name'] = record_buf [record_buf_pos:record_buf_pos + str_length]
record_buf_pos + = str_length + 1
str_length = 0
#get city
char = ord(record_buf [record_buf_pos + str_length])
while(char!= 0):
str_length + = 1
char = ord(record_buf [record_buf_pos + str_length])
如果str_length> 0:
record ['city'] = record_buf [record_buf_pos:record_buf_pos + str_length]
record_buf_pos + = str_length + 1
str_length = 0
#获取邮政编码
char = ord(record_buf [record_buf_pos + str_length])
while(char!= 0):
str_length + = 1
char = ord(record_buf [ record_buf_pos + str_length])
if str_length> 0:
record ['postal_code'] = record_buf [record_buf_pos:record_buf_pos + str_length]
else:
record ['postal_code'] =无
record_buf_pos + =对于范围(3)中的j,str_length + 1
str_length = 0
$ b纬度= 0
经度= 0
:
char = ord(record_buf [记录[buf_pos])
record_buf_pos + = 1
latitude + =(char
记录['latitude'] =(纬度/ 10000.0) (3):180.0
:
char = ord(record_buf [record_buf_pos])
record_buf_pos + = 1
longitude + =(char< <(j * 8))
record ['longitude'] =(longitude / 10000.0) - 180.0
如果self._databaseType == CITY_EDITION_REV1:
dmaarea_combo = 0
如果记录['country_code'] =='US':
for j在范围(3)中:
char = ord(record_buf [record_buf_pos])
record_buf_pos + = 1
dmaarea_combo + =(char< (j * 8))
record ['dma_code'] = int(math.floor(dmaarea_combo / 1000))
record ['area_code'] = dmaarea_combo%1000
else:
record ['dma_code'] = 0
record ['area_code'] = 0
返回记录
def country_code_by_addr(self,addr ):
返回指定IP地址的2个字母的国家代码(例如'US')
如果您有国家,地区或城市数据库,请使用此方法。 b
$ b @param addr:IP地址
@type addr:str
@return:双字母国家代码
@rtype:str
try:
if self._databaseType == COUNTRY_EDITION:
country_id = self._lookup_country_id(addr)
COUNTRY_CODES [country_id]
elif self._databaseType in(REGION_EDITION_REV0, REGION_EDITION_REV1,
CITY_EDITION_REV0,CITY_EDITION_REV1):
返回self.region_by_addr(addr)['country_code']
else:
raise GeoIPError('Invalid database type; country_ * methods expect'\
'Country,City,or Region database')
ValueError:
raise GeoIPError('* _ by_addr methods only only IP addresses。Use * (地址:%s)'%addr)
def country_code_by_name(self,hostname):
返回两个字母的国家代码(例如'美国')指定主机名
如果您有国家,地区或城市数据库,请使用此方法
@param主机名:主机名
@type主机名:str
@return:双字母国家代码
@rtype:str
addr = socket.gethostbyname(主机名)
返回self.country_code_by_addr (addr)
def country_name_by_addr(self,addr):
返回指定IP地址的完整国家名称
如果您有国家或城市数据库。
@param addr:IP地址
@type addr:str
@return:国家名称
@rtype:str
试试:
如果self._databaseType == COUNTRY_EDITION:
country_id = self._lookup_country_id(addr)
return COUNTRY_NAMES [country_id]
elif self._databaseType in(CITY_EDITION_REV0,CITY_EDITION_REV1) :
return self.record_by_addr(addr)['country_name']
else:
raise GeoIPError('Invalid database type; country_ *方法期望'\
'国家或城市数据库')
除了ValueError:
引发GeoIPError('* _ by_addr方法只接受IP地址,使用* _by_name作为主机名(地址: %s)'%addr)
def country_name_by_name(self,hostname):
返回指定主机名的完整国家名称
使用此方法if你有一个国家数据库
@param主机名:主机名
@type主机名:str
@return:国家名
@rtype:str
addr = socket.gethostbyname(主机名)
返回self.country_name_by_addr(addr)
def org_by_addr(self,addr):
查找给定IP地址的组织(或ISP)
如果你有组织/ ISP数据库,请使用此方法
@param addr:IP地址
@键入addr:str
@return:组织或ISP名称
@rtype:str
尝试:
ipnum = ip2long(地址)
如果不是ipnum:
增加ValueError(无效IP地址:%s%addr)
如果self._databaseType不在(ORG_EDITION,ISP_EDITION)中:
raise GeoIPError('无效的数据库类型; org_ *方法期望'\
'组织/ ISP数据库')
返回self._get_org(ipnum)
除ValueError外:
提升GeoIPError('* _ by_addr (地址:%s)'%addr)
def org_by_name(self,hostname):
查找如果你有一个Organization / ISP数据库,使用这个方法
@param主机名:主机名
@type主机名:str
@return:组织或ISP名称
@rtype:str
addr = socket.gethostbyname(主机名)
返回self.org_by_addr(addr )
def record_by_addr(self,addr):
查询给定IP地址的记录
如果您有城市数据库
@param addr:IP地址
@type addr:str
@return:dict with country_code,country_code3,country_name,
region,city,postal_code,latitude,longitude,
dma_code,metro_code,area_code,region_name,time_zone
@rtype:dict
try:
ipnum = ip2long(addr)
如果不是ipnum:
提高ValueError(Invalid IP地址:%s%addr)
如果不是(CITY_EDITION_REV0,CITY_EDITION_REV1)中的self._databaseType:
raise GeoIPError('Invalid database type; record_ *方法期望城市数据库')
返回self._get_record(ipnum)
除了ValueError:
引发GeoIPError('* _ by_addr方法只接受IP地址,使用* _by_name hostnames。(地址:%s)'%addr)
def record_by_name(self,hostname):
查找给定主机名的记录。 $ b使用这个方法,如果你有一个City数据库
@param主机名:主机名
@type主机名:str
@return:dict with country_code,country_code3,country_name,
region,city,postal_code,纬度,经度,
dma_code,metro_code,area_code,region_name,time_zone
@rtype:dict
addr = socket。 gethostbyname(主机名)
返回self.record_by_addr(addr)
$ b $ def region_by_addr(self,addr):
查找区域for给定IP地址
如果您有Region数据库,请使用此方法。
@param addr:IP地址
@type addr:str
@return:dict包含country_code,region,
和region_name
@rtype:dict
尝试:
ipnum = ip2long(addr)
如果不是ipnum:
提高ValueError(无效的IP地址:%s %addr)
如果不是self._databaseType(REGION_EDITION_REV0,REGION_EDITION_REV1,
CITY_EDITION_REV0,CITY_EDITION_REV1):
raise GeoIPError('Invalid database type; region_ * methods expect'\
'Region或City数据库')
返回self._get_region(ipnum)
除了ValueError:
引发GeoIPError('* _ by_addr方法只接受IP地址。 (地址:%s)'%addr)
def region_by_name(self,hostname):
查找给定主机名的区域。
如果您有Region数据库,请使用此方法。
@param主机名:主机名
@type主机名:str
@return:包含country_code,region,
和region_name
的字典@rtype:dict
addr = socket.gethostbyname(主机名)
返回self.region_by_addr(addr)
看起来App Engine不支持 mmap
,这并不奇怪。尝试使用其他标志之一,如下所示:
import pygeoip
gi = pygeoip.GeoIP('GeoIP.dat ',flags = pygeoip.const.MEMORY_CACHE)
Location = gi.country_code_by_addr(self.request.remote_addr)
尝试用 STANDARD
替换 MEMORY_CACHE
,如果这样也行不通。
I'm trying to do some IP-lookup on Python Google Appengine like this:
import pygeoip
gi = pygeoip.GeoIP('GeoIP.dat')
Location = gi.country_code_by_addr(self.request.remote_addr)
(pygeoip can be found here: http://code.google.com/p/pygeoip/)
The above code executes fine locally but when I push it to the live server I get the following error:
<type 'exceptions.ImportError'>: No module named mmap
Traceback (most recent call last):
File "/base/data/home/apps/tomcritchlow1/geoip.347423765058502279/main.py", line 28, in <module>
import pygeoip
File "/base/data/home/apps/tomcritchlow1/geoip.347423765058502279/pygeoip/__init__.py", line 34, in <module>
import mmap
I'm using Python 2.5 locally so it should be the same as the live server I think?
The Pygeoip page says the following:
Supported Flags:
STANDARD (do all reads from disk, slowest)
MEMORY_CACHE
MMAP_CACHE (map the whole file to memory, fastest)
I'm not sure exactly what "supported flags" means - if mmap is the problem could I use Pygeoip using memory cache instead of mmap? How would I do that?
Thanks!
Tom
PS - still a coding a n00b so please go easy :)
EDIT:
The solution outlined below works, first use this code:
gi = pygeoip.GeoIP('GeoIP.dat', flags=pygeoip.const.MEMORY_CACHE)
Then modify the init.py file within pygeoip like follows (warning! I've just commented out bits of code, not done anything fancy!!)
"""
Pure Python GeoIP API. The API is based off of U{MaxMind's C-based Python API<http://www.maxmind.com/app/python>},
but the code itself is based on the U{pure PHP5 API<http://pear.php.net/package/Net_GeoIP/>}
by Jim Winstead and Hans Lellelid.
It is mostly a drop-in replacement, except the
C{new} and C{open} methods are gone. You should instantiate the L{GeoIP} class yourself:
C{gi = GeoIP('/path/to/GeoIP.dat', pygeoip.MEMORY_CACHE)}
@author: Jennifer Ennis <zaylea at gmail dot com>
@license:
Copyright(C) 2004 MaxMind LLC
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/lgpl.txt>.
"""
from __future__ import with_statement
import os
import math
import socket
#import mmap
from const import *
from util import ip2long
class GeoIPError(Exception):
pass
class GeoIPMetaclass(type):
def __new__(cls, *args, **kwargs):
"""
Singleton method to gets an instance without reparsing the db. Unique
instances are instantiated based on the filename of the db. Flags are
ignored for this, i.e. if you initialize one with STANDARD flag (default)
and then try later to initialize with MEMORY_CACHE, it will still
return the STANDARD one.
"""
if not hasattr(cls, '_instances'):
cls._instances = {}
if len(args) > 0:
filename = args[0]
elif 'filename' in kwargs:
filename = kwargs['filename']
if not filename in cls._instances:
cls._instances[filename] = type.__new__(cls, *args, **kwargs)
return cls._instances[filename]
GeoIPBase = GeoIPMetaclass('GeoIPBase', (object,), {})
class GeoIP(GeoIPBase):
def __init__(self, filename, flags=0):
"""
Initialize the class.
@param filename: path to a geoip database
@type filename: str
@param flags: flags that affect how the database is processed.
Currently the only supported flags are STANDARD, MEMORY_CACHE, and
MMAP_CACHE.
@type flags: int
"""
self._filename = filename
self._flags = flags
#tom:
#if self._flags & MMAP_CACHE:
if '2' == '3':
with open(filename, 'rb') as f:
self._filehandle = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
else:
self._filehandle = open(filename, 'rb')
#tom:
#if self._flags & MEMORY_CACHE:
# self._memoryBuffer = self._filehandle.read()
self._setup_segments()
def _setup_segments(self):
"""
Parses the database file to determine what kind of database is being used and setup
segment sizes and start points that will be used by the seek*() methods later.
"""
self._databaseType = COUNTRY_EDITION
self._recordLength = STANDARD_RECORD_LENGTH
filepos = self._filehandle.tell()
self._filehandle.seek(-3, os.SEEK_END)
for i in range(STRUCTURE_INFO_MAX_SIZE):
delim = self._filehandle.read(3)
if delim == (chr(255) * 3):
self._databaseType = ord(self._filehandle.read(1))
if (self._databaseType >= 106):
# backwards compatibility with databases from April 2003 and earlier
self._databaseType -= 105
if self._databaseType == REGION_EDITION_REV0:
self._databaseSegments = STATE_BEGIN_REV0
elif self._databaseType == REGION_EDITION_REV1:
self._databaseSegments = STATE_BEGIN_REV1
elif self._databaseType in (CITY_EDITION_REV0,
CITY_EDITION_REV1,
ORG_EDITION,
ISP_EDITION,
ASNUM_EDITION):
self._databaseSegments = 0
buf = self._filehandle.read(SEGMENT_RECORD_LENGTH)
for j in range(SEGMENT_RECORD_LENGTH):
self._databaseSegments += (ord(buf[j]) << (j * 8))
if self._databaseType in (ORG_EDITION, ISP_EDITION):
self._recordLength = ORG_RECORD_LENGTH
break
else:
self._filehandle.seek(-4, os.SEEK_CUR)
if self._databaseType == COUNTRY_EDITION:
self._databaseSegments = COUNTRY_BEGIN
self._filehandle.seek(filepos, os.SEEK_SET)
def _lookup_country_id(self, addr):
"""
Get the country index.
This method is called by the _lookupCountryCode and _lookupCountryName
methods. It looks up the index ('id') for the country which is the key
for the code and name.
@param addr: The IP address
@type addr: str
@return: network byte order 32-bit integer
@rtype: int
"""
ipnum = ip2long(addr)
if not ipnum:
raise ValueError("Invalid IP address: %s" % addr)
if self._databaseType != COUNTRY_EDITION:
raise GeoIPError('Invalid database type; country_* methods expect '\
'Country database')
return self._seek_country(ipnum) - COUNTRY_BEGIN
def _seek_country(self, ipnum):
"""
Using the record length and appropriate start points, seek to the
country that corresponds to the converted IP address integer.
@param ipnum: result of ip2long conversion
@type ipnum: int
@return: offset of start of record
@rtype: int
"""
offset = 0
for depth in range(31, -1, -1):
#tom:
#if self._flags & MEMORY_CACHE:
if '2' == '3':
startIndex = 2 * self._recordLength * offset
length = 2 * self._recordLength
endIndex = startIndex + length
buf = self._memoryBuffer[startIndex:endIndex]
else:
self._filehandle.seek(2 * self._recordLength * offset, os.SEEK_SET)
buf = self._filehandle.read(2 * self._recordLength)
x = [0,0]
for i in range(2):
for j in range(self._recordLength):
x[i] += ord(buf[self._recordLength * i + j]) << (j * 8)
if ipnum & (1 << depth):
if x[1] >= self._databaseSegments:
return x[1]
offset = x[1]
else:
if x[0] >= self._databaseSegments:
return x[0]
offset = x[0]
raise Exception('Error traversing database - perhaps it is corrupt?')
def _get_org(self, ipnum):
"""
Seek and return organization (or ISP) name for converted IP addr.
@param ipnum: Converted IP address
@type ipnum: int
@return: org/isp name
@rtype: str
"""
seek_org = self._seek_country(ipnum)
if seek_org == self._databaseSegments:
return None
record_pointer = seek_org + (2 * self._recordLength - 1) * self._databaseSegments
self._filehandle.seek(record_pointer, os.SEEK_SET)
org_buf = self._filehandle.read(MAX_ORG_RECORD_LENGTH)
return org_buf[:org_buf.index(chr(0))]
def _get_region(self, ipnum):
"""
Seek and return the region info (dict containing country_code and region_name).
@param ipnum: converted IP address
@type ipnum: int
@return: dict containing country_code and region_name
@rtype: dict
"""
country_code = ''
region = ''
if self._databaseType == REGION_EDITION_REV0:
seek_country = self._seek_country(ipnum)
seek_region = seek_country - STATE_BEGIN_REV0
if seek_region >= 1000:
country_code = 'US'
region = ''.join([chr((seek_region / 1000) / 26 + 65), chr((seek_region / 1000) % 26 + 65)])
else:
country_code = COUNTRY_CODES[seek_region]
region = ''
elif self._databaseType == REGION_EDITION_REV1:
seek_country = self._seek_country(ipnum)
seek_region = seek_country - STATE_BEGIN_REV1
if seek_region < US_OFFSET:
country_code = '';
region = ''
elif seek_region < CANADA_OFFSET:
country_code = 'US'
region = ''.join([chr((seek_region - US_OFFSET) / 26 + 65), chr((seek_region - US_OFFSET) % 26 + 65)])
elif seek_region < WORLD_OFFSET:
country_code = 'CA'
region = ''.join([chr((seek_region - CANADA_OFFSET) / 26 + 65), chr((seek_region - CANADA_OFFSET) % 26 + 65)])
else:
i = (seek_region - WORLD_OFFSET) / FIPS_RANGE
if i in COUNTRY_CODES:
country_code = COUNTRY_CODES[(seek_region - WORLD_OFFSET) / FIPS_RANGE]
else:
country_code = ''
region = ''
elif self._databaseType in (CITY_EDITION_REV0, CITY_EDITION_REV1):
rec = self._get_record(ipnum)
country_code = rec['country_code']
region = rec['region_name']
return {'country_code' : country_code, 'region_name' : region }
def _get_record(self, ipnum):
"""
Populate location dict for converted IP.
@param ipnum: converted IP address
@type ipnum: int
@return: dict with country_code, country_code3, country_name,
region, city, postal_code, latitude, longitude,
dma_code, metro_code, area_code, region_name, time_zone
@rtype: dict
"""
seek_country = self._seek_country(ipnum)
if seek_country == self._databaseSegments:
return None
record_pointer = seek_country + (2 * self._recordLength - 1) * self._databaseSegments
self._filehandle.seek(record_pointer, os.SEEK_SET)
record_buf = self._filehandle.read(FULL_RECORD_LENGTH)
record = {}
record_buf_pos = 0
char = ord(record_buf[record_buf_pos])
record['country_code'] = COUNTRY_CODES[char]
record['country_code3'] = COUNTRY_CODES3[char]
record['country_name'] = COUNTRY_NAMES[char]
record_buf_pos += 1
str_length = 0
# get region
char = ord(record_buf[record_buf_pos+str_length])
while (char != 0):
str_length += 1
char = ord(record_buf[record_buf_pos+str_length])
if str_length > 0:
record['region_name'] = record_buf[record_buf_pos:record_buf_pos+str_length]
record_buf_pos += str_length + 1
str_length = 0
# get city
char = ord(record_buf[record_buf_pos+str_length])
while (char != 0):
str_length += 1
char = ord(record_buf[record_buf_pos+str_length])
if str_length > 0:
record['city'] = record_buf[record_buf_pos:record_buf_pos+str_length]
record_buf_pos += str_length + 1
str_length = 0
# get the postal code
char = ord(record_buf[record_buf_pos+str_length])
while (char != 0):
str_length += 1
char = ord(record_buf[record_buf_pos+str_length])
if str_length > 0:
record['postal_code'] = record_buf[record_buf_pos:record_buf_pos+str_length]
else:
record['postal_code'] = None
record_buf_pos += str_length + 1
str_length = 0
latitude = 0
longitude = 0
for j in range(3):
char = ord(record_buf[record_buf_pos])
record_buf_pos += 1
latitude += (char << (j * 8))
record['latitude'] = (latitude/10000.0) - 180.0
for j in range(3):
char = ord(record_buf[record_buf_pos])
record_buf_pos += 1
longitude += (char << (j * 8))
record['longitude'] = (longitude/10000.0) - 180.0
if self._databaseType == CITY_EDITION_REV1:
dmaarea_combo = 0
if record['country_code'] == 'US':
for j in range(3):
char = ord(record_buf[record_buf_pos])
record_buf_pos += 1
dmaarea_combo += (char << (j*8))
record['dma_code'] = int(math.floor(dmaarea_combo/1000))
record['area_code'] = dmaarea_combo%1000
else:
record['dma_code'] = 0
record['area_code'] = 0
return record
def country_code_by_addr(self, addr):
"""
Returns 2-letter country code (e.g. 'US') for specified IP address.
Use this method if you have a Country, Region, or City database.
@param addr: IP address
@type addr: str
@return: 2-letter country code
@rtype: str
"""
try:
if self._databaseType == COUNTRY_EDITION:
country_id = self._lookup_country_id(addr)
return COUNTRY_CODES[country_id]
elif self._databaseType in (REGION_EDITION_REV0, REGION_EDITION_REV1,
CITY_EDITION_REV0, CITY_EDITION_REV1):
return self.region_by_addr(addr)['country_code']
else:
raise GeoIPError('Invalid database type; country_* methods expect '\
'Country, City, or Region database')
except ValueError:
raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (Address: %s)' % addr)
def country_code_by_name(self, hostname):
"""
Returns 2-letter country code (e.g. 'US') for specified hostname.
Use this method if you have a Country, Region, or City database.
@param hostname: host name
@type hostname: str
@return: 2-letter country code
@rtype: str
"""
addr = socket.gethostbyname(hostname)
return self.country_code_by_addr(addr)
def country_name_by_addr(self, addr):
"""
Returns full country name for specified IP address.
Use this method if you have a Country or City database.
@param addr: IP address
@type addr: str
@return: country name
@rtype: str
"""
try:
if self._databaseType == COUNTRY_EDITION:
country_id = self._lookup_country_id(addr)
return COUNTRY_NAMES[country_id]
elif self._databaseType in (CITY_EDITION_REV0, CITY_EDITION_REV1):
return self.record_by_addr(addr)['country_name']
else:
raise GeoIPError('Invalid database type; country_* methods expect '\
'Country or City database')
except ValueError:
raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (Address: %s)' % addr)
def country_name_by_name(self, hostname):
"""
Returns full country name for specified hostname.
Use this method if you have a Country database.
@param hostname: host name
@type hostname: str
@return: country name
@rtype: str
"""
addr = socket.gethostbyname(hostname)
return self.country_name_by_addr(addr)
def org_by_addr(self, addr):
"""
Lookup the organization (or ISP) for given IP address.
Use this method if you have an Organization/ISP database.
@param addr: IP address
@type addr: str
@return: organization or ISP name
@rtype: str
"""
try:
ipnum = ip2long(addr)
if not ipnum:
raise ValueError("Invalid IP address: %s" % addr)
if self._databaseType not in (ORG_EDITION, ISP_EDITION):
raise GeoIPError('Invalid database type; org_* methods expect '\
'Org/ISP database')
return self._get_org(ipnum)
except ValueError:
raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (Address: %s)' % addr)
def org_by_name(self, hostname):
"""
Lookup the organization (or ISP) for hostname.
Use this method if you have an Organization/ISP database.
@param hostname: host name
@type hostname: str
@return: organization or ISP name
@rtype: str
"""
addr = socket.gethostbyname(hostname)
return self.org_by_addr(addr)
def record_by_addr(self, addr):
"""
Look up the record for a given IP address.
Use this method if you have a City database.
@param addr: IP address
@type addr: str
@return: dict with country_code, country_code3, country_name,
region, city, postal_code, latitude, longitude,
dma_code, metro_code, area_code, region_name, time_zone
@rtype: dict
"""
try:
ipnum = ip2long(addr)
if not ipnum:
raise ValueError("Invalid IP address: %s" % addr)
if not self._databaseType in (CITY_EDITION_REV0, CITY_EDITION_REV1):
raise GeoIPError('Invalid database type; record_* methods expect City database')
return self._get_record(ipnum)
except ValueError:
raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (Address: %s)' % addr)
def record_by_name(self, hostname):
"""
Look up the record for a given hostname.
Use this method if you have a City database.
@param hostname: host name
@type hostname: str
@return: dict with country_code, country_code3, country_name,
region, city, postal_code, latitude, longitude,
dma_code, metro_code, area_code, region_name, time_zone
@rtype: dict
"""
addr = socket.gethostbyname(hostname)
return self.record_by_addr(addr)
def region_by_addr(self, addr):
"""
Lookup the region for given IP address.
Use this method if you have a Region database.
@param addr: IP address
@type addr: str
@return: dict containing country_code, region,
and region_name
@rtype: dict
"""
try:
ipnum = ip2long(addr)
if not ipnum:
raise ValueError("Invalid IP address: %s" % addr)
if not self._databaseType in (REGION_EDITION_REV0, REGION_EDITION_REV1,
CITY_EDITION_REV0, CITY_EDITION_REV1):
raise GeoIPError('Invalid database type; region_* methods expect '\
'Region or City database')
return self._get_region(ipnum)
except ValueError:
raise GeoIPError('*_by_addr methods only accept IP addresses. Use *_by_name for hostnames. (Address: %s)' % addr)
def region_by_name(self, hostname):
"""
Lookup the region for given hostname.
Use this method if you have a Region database.
@param hostname: host name
@type hostname: str
@return: dict containing country_code, region,
and region_name
@rtype: dict
"""
addr = socket.gethostbyname(hostname)
return self.region_by_addr(addr)
Looks like App Engine doesn't support mmap
, which isn't surprising. You can try using one of the other flags as follows:
import pygeoip
gi = pygeoip.GeoIP('GeoIP.dat', flags=pygeoip.const.MEMORY_CACHE)
Location = gi.country_code_by_addr(self.request.remote_addr)
Try replacing MEMORY_CACHE
with STANDARD
if that also doesn't work.
这篇关于在Appengine上使用Pygeoip - 没有名为mmap的模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!