Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
This repository was archived by the owner on Dec 26, 2023. It is now read-only.

Commit 46aa2ba

Browse filesBrowse files
committed
Initial commit.
0 parents  commit 46aa2ba
Copy full SHA for 46aa2ba

File tree

Expand file treeCollapse file tree

9 files changed

+478
-0
lines changed
Filter options
Expand file treeCollapse file tree

9 files changed

+478
-0
lines changed

‎.travis.yml

Copy file name to clipboard
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
language: python
2+
python:
3+
- "2.6"
4+
- "2.7"
5+
install:
6+
- python setup.py install
7+
script:
8+
- python -m unittest discover

‎AUTHORS.txt

Copy file name to clipboard
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Author:
2+
3+
* Selwin Ong (https://github.com/selwin)

‎LICENSE.txt

Copy file name to clipboard
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright (c) 2013 Selwin Ong
2+
3+
Permission is hereby granted, free of charge, to any person obtaining
4+
a copy of this software and associated documentation files (the
5+
"Software"), to deal in the Software without restriction, including
6+
without limitation the rights to use, copy, modify, merge, publish,
7+
distribute, sublicense, and/or sell copies of the Software, and to
8+
permit persons to whom the Software is furnished to do so, subject to
9+
the following conditions:
10+
11+
The above copyright notice and this permission notice shall be
12+
included in all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

‎MANIFEST.in

Copy file name to clipboard
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
include LICENSE.txt
2+
include README.rst

‎README.rst

Copy file name to clipboard
+111Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
Python User Agents
2+
==================
3+
4+
`user_agents` is a Python library that provides an easy way to identify devices like mobile phones,
5+
tablets and their capabilities by parsing (browser) user agent strings. The goal is to reliably
6+
detect whether:
7+
8+
* User agent is a mobile, tablet or PC based device
9+
* User agent has touch capabilities (has touch screen)
10+
11+
`user_agents` relies on the excellent `ua-parser <https://github.com/tobie/ua-parser>`_ to do the
12+
actual parsing of the raw user agent string.
13+
14+
This library should be considered "alpha". Please post feature suggestions, bug or pull requests to
15+
identify more devices on Github.
16+
17+
18+
Installation
19+
============
20+
21+
WARNING: This library should be considered "alpha". Use this in production at your own risk.
22+
23+
Usage
24+
=====
25+
26+
Various basic information that can help you identify visitors can be accessed `browser`, `device`
27+
and `os` attributes. For example::
28+
29+
import user_agents
30+
31+
# iPhone's user agent string
32+
ua_string = 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B179 Safari/7534.48.3'
33+
34+
# Accessing user agent's browser attributes
35+
user_agent.browser # returns Browser(family=u'Mobile Safari', version=(5, 1), version_string='5.1')
36+
user_agent.browser.family # returns 'Mobile Safari'
37+
user_agent.browser.version # returns (5, 1)
38+
user_agent.browser.version_string # returns '5.1'
39+
40+
# Accessing user agent's operating system properties
41+
user_agent.os # returns OperatingSystem(family=u'iOS', version=(5, 1), version_string='5.1')
42+
user_agent.os.family # returns 'iOS'
43+
user_agent.os.version # returns (5, 1)
44+
user_agent.os.version_string # returns '5.1'
45+
46+
# Accessing user agent's device properties
47+
user_agent.device # returns Device(family='iPhone')
48+
user_agent.device.family # returns 'iPhone'
49+
50+
51+
`user_agents` also expose a few other more "sophisticated" attributes that are derived from one or
52+
more basic attributes defined above. As for now, these attributes should correctly identify
53+
popular platforms/devices, pull requests to support smaller ones are always welcome.
54+
55+
Currently these attributes are supported:
56+
57+
* `is_mobile` - whether user agent is identified as a mobile phone (iPhone, Android phones, Blackberry, Windows Phone devices etc)
58+
* `is_tablet` - whether user agent is identified as a tablet device (iPad, Kindle Fire, Nexus 7 etc)
59+
* `is_pc` - whether user agent is identified to be running a traditional "desktop" OS (Windows, OS X, Linux)
60+
* `is_touch_capable` - whether user agent has touch capabilities
61+
62+
63+
For example::
64+
65+
import user_agents
66+
67+
# Let's start from an old, non touch Blackberry device
68+
ua_string = 'BlackBerry9700/5.0.0.862 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/331 UNTRUSTED/1.0 3gpp-gba'
69+
user_agent = user_agents.parse(ua_string)
70+
user_agent.is_mobile # returns True
71+
user_agent.is_tablet # returns False
72+
user_agent.is_touch_capable # returns False
73+
user_agent.is_pc # returns False
74+
75+
# Now a Samsung Galaxy S3
76+
ua_string = 'Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
77+
user_agent = user_agents.parse(ua_string)
78+
user_agent.is_mobile # returns True
79+
user_agent.is_tablet # returns False
80+
user_agent.is_touch_capable # returns True
81+
user_agent.is_pc # returns False
82+
83+
# iPad's user agent string
84+
ua_string = 'Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10'
85+
user_agent = user_agents.parse(ua_string)
86+
user_agent.is_mobile # returns False
87+
user_agent.is_tablet # returns True
88+
user_agent.is_touch_capable # returns True
89+
user_agent.is_pc # returns False
90+
91+
# Kindle Fire's user agent string
92+
ua_string = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.1.0-80) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=true'
93+
user_agent = user_agents.parse(ua_string)
94+
user_agent.is_mobile # returns False
95+
user_agent.is_tablet # returns True
96+
user_agent.is_touch_capable # returns True
97+
user_agent.is_pc # returns False
98+
99+
# Touch capable Windows 8 device
100+
ua_string = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Touch)'
101+
user_agent = user_agents.parse(ua_string)
102+
user_agent.is_mobile # returns False
103+
user_agent.is_tablet # returns False
104+
user_agent.is_touch_capable # returns True
105+
user_agent.is_pc # returns True
106+
107+
108+
Running Tests
109+
=============
110+
111+
python -m unittest discover

‎setup.py

Copy file name to clipboard
+28Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# -*- coding: utf-8 -*-
2+
from setuptools import setup
3+
4+
setup(
5+
name='user-agents',
6+
version='0.1',
7+
author='Selwin Ong',
8+
author_email='selwin.ong@gmail.com',
9+
packages=['user_agents'],
10+
url='https://github.com/selwin/python-user-agents',
11+
license='MIT',
12+
description='A library to identify devices (phones, tablets) and their capabilities by parsing (browser) user agent strings',
13+
long_description=open('README.rst').read(),
14+
zip_safe=False,
15+
include_package_data=True,
16+
package_data = { '': ['README.rst'] },
17+
install_requires=['ua-parser'],
18+
classifiers=[
19+
'Development Status :: 3 - Alpha',
20+
'Environment :: Web Environment',
21+
'Intended Audience :: Developers',
22+
'License :: OSI Approved :: MIT License',
23+
'Operating System :: OS Independent',
24+
'Programming Language :: Python',
25+
'Topic :: Internet :: WWW/HTTP',
26+
'Topic :: Software Development :: Libraries :: Python Modules',
27+
]
28+
)

‎user_agents/__init__.py

Copy file name to clipboard
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .parsers import parse

‎user_agents/parsers.py

Copy file name to clipboard
+165Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
from collections import namedtuple
2+
3+
from ua_parser import user_agent_parser
4+
5+
6+
MOBILE_DEVICE_FAMILIES = (
7+
'iPhone',
8+
'iPod',
9+
'Generic Smartphone',
10+
'Generic Feature Phone',
11+
)
12+
13+
TABLET_DEVICE_FAMILIES = (
14+
'iPad',
15+
'Blackberry Playbook',
16+
'Kindle',
17+
'Kindle Fire',
18+
)
19+
20+
TOUCH_CAPABLE_OS_FAMILIES = (
21+
'iOS',
22+
'Android',
23+
'Windows Phone OS',
24+
'Windows RT',
25+
)
26+
27+
TOUCH_CAPABLE_DEVICE_FAMILIES = (
28+
'Blackberry Playbook',
29+
'Kindle Fire',
30+
)
31+
32+
33+
def parse_version(major=None, minor=None, patch=None, patch_minor=None):
34+
# Returns version number tuple, attributes will be integer if they're numbers
35+
if major is not None and isinstance(major, basestring):
36+
major = int(major) if major.isdigit() else major
37+
if minor is not None and isinstance(minor, basestring):
38+
minor = int(minor) if minor.isdigit() else minor
39+
if patch is not None and isinstance(patch, basestring):
40+
patch = int(patch) if patch.isdigit() else patch
41+
if patch_minor is not None and isinstance(patch_minor, basestring):
42+
patch_minor = int(patch_minor) if patch_minor.isdigit() else patch_minor
43+
if patch_minor:
44+
return (major, minor, patch, patch_minor)
45+
elif patch:
46+
return (major, minor, patch)
47+
elif minor:
48+
return (major, minor)
49+
elif major:
50+
return (major,)
51+
else:
52+
return tuple()
53+
54+
55+
Browser = namedtuple('Browser', ['family', 'version', 'version_string'])
56+
57+
58+
def parse_browser(family, major=None, minor=None, patch=None, patch_minor=None):
59+
# Returns a browser object
60+
version = parse_version(major, minor, patch)
61+
version_string = '.'.join([str(v) for v in version])
62+
return Browser(family, version, version_string)
63+
64+
65+
OperatingSystem = namedtuple('OperatingSystem', ['family', 'version', 'version_string'])
66+
67+
68+
def parse_operating_system(family, major=None, minor=None, patch=None, patch_minor=None):
69+
version = parse_version(major, minor, patch)
70+
version_string = '.'.join([str(v) for v in version])
71+
return OperatingSystem(family, version, version_string)
72+
73+
74+
Device = namedtuple('Device', ['family'])
75+
76+
77+
def parse_device(family):
78+
return Device(family)
79+
80+
81+
class UserAgent(object):
82+
83+
def __init__(self, user_agent_string):
84+
ua_dict = user_agent_parser.Parse(user_agent_string)
85+
self.ua_string = user_agent_string
86+
self.os = parse_operating_system(**ua_dict['os'])
87+
self.browser = parse_browser(**ua_dict['user_agent'])
88+
self.device = parse_device(**ua_dict['device'])
89+
90+
91+
def _is_android_tablet(self):
92+
# Newer Android tablets don't have "Mobile" in their user agent string,
93+
# older ones like Galaxy Tab still have "Mobile" though they're not
94+
if 'Mobile Safari' not in self.ua_string:
95+
return True
96+
if 'SCH-' in self.ua_string:
97+
return True
98+
return False
99+
100+
def _is_blackberry_touch_capable_device(self):
101+
# A helper to determine whether a BB phone has touch capabilities
102+
# Blackberry Bold Touch series begins with 99XX
103+
if 'Blackberry 99' in self.device.family:
104+
return True
105+
if 'Blackberry 95' in self.device.family: # BB Storm devices
106+
return True
107+
if 'Blackberry 95' in self.device.family: # BB Torch devices
108+
return True
109+
return False
110+
111+
@property
112+
def is_tablet(self):
113+
if self.device.family in TABLET_DEVICE_FAMILIES:
114+
return True
115+
if (self.os.family == 'Android' and self._is_android_tablet()):
116+
return True
117+
if self.os.family == 'Windows RT':
118+
return True
119+
return False
120+
121+
@property
122+
def is_mobile(self):
123+
# First check for mobile device families
124+
if self.device.family in MOBILE_DEVICE_FAMILIES:
125+
return True
126+
# Device is considered Mobile OS is Android and not tablet
127+
# This is not fool proof but would have to suffice for now
128+
if self.os.family == 'Android' and not self.is_tablet:
129+
return True
130+
if self.os.family == 'BlackBerry OS' and self.device.family != 'Blackberry Playbook':
131+
return True
132+
if self.os.family == 'Windows Phone OS':
133+
return True
134+
# TODO: remove after https://github.com/tobie/ua-parser/issues/126 is closed
135+
if 'J2ME' in self.ua_string or 'MIDP' in self.ua_string:
136+
return True
137+
return False
138+
139+
@property
140+
def is_touch_capable(self):
141+
if self.os.family in TOUCH_CAPABLE_OS_FAMILIES:
142+
return True
143+
if self.device.family in TOUCH_CAPABLE_DEVICE_FAMILIES:
144+
return True
145+
if self.os.family == 'Windows 8' and 'Touch' in self.ua_string:
146+
return True
147+
if 'BlackBerry' in self.os.family and self._is_blackberry_touch_capable_device():
148+
return True
149+
return False
150+
151+
@property
152+
def is_pc(self):
153+
# Returns True for "PC" devices (Windows, Mac and Linux)
154+
if 'Windows NT' in self.ua_string:
155+
return True
156+
# TODO: remove after https://github.com/tobie/ua-parser/issues/127 is closed
157+
if self.os.family == 'Mac OS X' and 'Silk' not in self.ua_string:
158+
return True
159+
if 'Linux' in self.ua_string and 'X11' in self.ua_string:
160+
return True
161+
return False
162+
163+
164+
def parse(user_agent_string):
165+
return UserAgent(user_agent_string)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.