Sometimes you need to know if a visitor is a bot or if the browser supports certain features or if it is a mobile device.
The easiest way to do so would be to lookup the user agent in a capabilities database. Fortunately there is already such a database called browscap.ini which is widely known among PHP users.
I found the file accidently on my harddrive because it is also used by Mono: /etc/mono/browscap.ini
Read http://browsers.garykeith.com/index.asp for more information.
Before importing the module you need to call the script from commandline to retrieve the browscap.ini file. Look at the test function to see how to use it.
You can also create a file called "bupdate.ini" which can contain fixes for wrong or incomplete entries, e.g:
[Konqueror] javaapplets=True
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | from ConfigParser import SafeConfigParser as ConfigParser
import re
import os
__all__ = [
"BrowserCapabilities"
]
BC_PATH = os.path.abspath(os.path.dirname(__file__ or os.getcwd()))
class Browser(object):
def __init__(self, capabilities):
self.lazy_flag = True
self.cap = capabilities
def parse(self):
for name, value in self.cap.items():
if name in ["tables", "aol", "javaapplets",
"activexcontrols", "backgroundsounds",
"vbscript", "win16", "javascript", "cdf",
"wap", "crawler", "netclr", "beta",
"iframes", "frames", "stripper", "wap"]:
self.cap[name] = (value.strip().lower() == "true")
elif name in ["ecmascriptversion", "w3cdomversion"]:
self.cap[name] = float(value)
elif name in ["css"]:
self.cap[name] = int(value)
else:
self.cap[name] = value
self.lazy_flag = False
def __repr__(self):
if self.lazy_flag: self.parse()
return repr(self.cap)
def get(self, name, default=None):
if self.lazy_flag: self.parse()
try:
return self[name]
except KeyError:
return default
def __getitem__(self, name):
if self.lazy_flag: self.parse()
return self.cap[name.lower()]
def keys(self):
return self.cap.keys()
def items(self):
if self.lazy_flag: self.parse()
return self.cap.items()
def values(self):
if self.lazy_flag: self.parse()
return self.cap.values()
def __len__(self):
return len(self.cap)
def supports(self, feature):
value = self.cap.get(feature)
if value == None:
return False
return value
def features(self):
l = []
for f in ["tables", "frames", "iframes", "javascript",
"cookies", "w3cdomversion", "wap"]:
if self.supports(f):
l.append(f)
if self.supports_java():
l.append("java")
if self.supports_activex():
l.append("activex")
css = self.css_version()
if css > 0:
l.append("css1")
if css > 1:
l.append("css2")
return l
def supports_tables(self):
return self.supports("frames")
def supports_iframes(self):
return self.supports("iframes")
def supports_frames(self):
return self.supports("frames")
def supports_java(self):
return self.supports("javaapplets")
def supports_javascript(self):
return self.supports("javascript")
def supports_vbscript(self):
return self.supports("vbscript")
def supports_activex(self):
return self.supports("activexcontrols")
def supports_cookies(self):
return self.supports("cookies")
def supports_wap(self):
return self.supports("wap")
def css_version(self):
return self.get("css", 0)
def version(self):
major = self.get("majorver")
minor = self.get("minorver")
if major and minor:
return (major, minor)
elif major:
return (major, None)
elif minor:
return (None, minor)
else:
ver = self.get("version")
if ver and "." in ver:
return tuple(ver.split(".", 1))
elif ver:
return (ver, None)
else:
return (None, None)
def dom_version(self):
return self.get("w3cdomversion", 0)
def is_bot(self):
return self.get("crawler") == True
def is_mobile(self):
return self.get("ismobiledevice") == True
def name(self):
return self.get("browser")
class BrowserCapabilities(object):
def __new__(cls, *args, **kwargs):
# Only create one instance of this clas
if "instance" not in cls.__dict__:
cls.instance = object.__new__(cls, *args, **kwargs)
return cls.instance
def __init__(self):
self.cache = {}
self.parse()
def parse(self):
cfg = ConfigParser()
files = ("browscap.ini", "bupdate.ini")
read_ok = cfg.read([os.path.join(BC_PATH, name) for name in files])
if len(read_ok) == 0:
raise IOError, "Could not read browscap.ini, " + \
"please get it from http://www.GaryKeith.com"
self.sections = []
self.items = {}
self.browsers = {}
parents = set()
for name in cfg.sections():
qname = name
for unsafe in list("^$()[].-"):
qname = qname.replace(unsafe, "\%s" % unsafe)
qname = qname.replace("?", ".").replace("*", ".*?")
qname = "^%s$" % qname
sec_re = re.compile(qname)
sec = dict(regex=qname)
sec.update(cfg.items(name))
p = sec.get("parent")
if p: parents.add(p)
self.browsers[name] = sec
if name not in parents:
self.sections.append(sec_re)
self.items[sec_re] = sec
def query(self, useragent):
b = self.cache.get(useragent)
if b: return b
for sec_pat in self.sections:
if sec_pat.match(useragent):
browser = dict(agent=useragent)
browser.update(self.items[sec_pat])
parent = browser.get("parent")
while parent:
items = self.browsers[parent]
for key, value in items.items():
if key not in browser.keys():
browser[key] = value
elif key == "browser" and value != "DefaultProperties":
browser["category"] = value # Wget, Godzilla -> Download Managers
parent = items.get("parent")
if browser.get("browser") != "Default Browser":
b = Browser(browser)
self.cache[useragent] = b
return b
self.cache[useragent] = None
__call__ = query
def test():
bc = BrowserCapabilities()
for agent in [
"Mozilla/5.0 (compatible; Konqueror/3.5; Linux; X11; de) KHTML/3.5.2 (like Gecko) Kubuntu 6.06 Dapper",
"Mozilla/5.0 (X11; U; Linux i686; de; rv:1.8.0.5) Gecko/20060731 Ubuntu/dapper-security Firefox/1.5.0.5",
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.12) Gecko/20060216 Debian/1.7.12-1.1ubuntu2",
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060731 Ubuntu/dapper-security Epiphany/2.14 Firefox/1.5.0.5",
"Opera/9.00 (X11; Linux i686; U; en)",
"Wget/1.10.2",
"Mozilla/5.0 (X11; Linux i686; U;) Gecko/20051128 Kazehakase/0.3.3 Debian/0.3.3-1",
"Mozilla/5.0 (X11; U; Linux i386) Gecko/20063102 Galeon/1.3test",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)" # Tested under Wine
]:
b = bc(agent)
if not b:
print "!", agent
else:
print b.name(), b.version(), b.get("category", ""), b.features()
def update():
import urllib
urllib.urlretrieve("http://browsers.garykeith.com/stream.asp?BrowsCapINI",
"browscap.ini")
if __name__ == "__main__":
import sys, os
bc_filename = os.path.join(BC_PATH, "browscap.ini")
if not os.path.exists(bc_filename) or "-update" in sys.argv[1:]:
print "Downloading browser database to %r..." % bc_filename,
update()
print "done"
test()
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.