First commit
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Miloš Mileusnić
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Go/Golang package for parsing user agent strings [](https://godoc.org/github.com/mileusna/useragent)
|
||||
|
||||
Parse browser's and bot's user agents strings and determin user agent name, version, operating system name etc.
|
||||
|
||||
## Status
|
||||
|
||||
Still need some work on detecting Andorid device name.
|
||||
|
||||
## Installation <a id="installation"></a>
|
||||
```
|
||||
go get github.com/mileusna/useragent
|
||||
```
|
||||
|
||||
## Example<a id="example"></a>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/mileusna/useragent"
|
||||
)
|
||||
|
||||
func main() {
|
||||
userAgents := []string{
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.0 Mobile/14F89 Safari/602.1",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) FxiOS/8.1.1b4948 Mobile/14F89 Safari/603.2.4",
|
||||
"Mozilla/5.0 (iPad; CPU OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.0 Mobile/14F89 Safari/602.1",
|
||||
"Mozilla/5.0 (Linux; Android 4.3; GT-I9300 Build/JSS15J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Android 4.3; Mobile; rv:54.0) Gecko/54.0 Firefox/54.0",
|
||||
"Mozilla/5.0 (Linux; Android 4.3; GT-I9300 Build/JSS15J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36 OPR/42.9.2246.119956",
|
||||
"Opera/9.80 (Android; Opera Mini/28.0.2254/66.318; U; en) Presto/2.12.423 Version/12.16",
|
||||
}
|
||||
|
||||
for _, s := range userAgents {
|
||||
ua := ua.Parse(s)
|
||||
fmt.Println()
|
||||
fmt.Println(ua.String)
|
||||
fmt.Println(strings.Repeat("=", len(ua.String)))
|
||||
fmt.Println("Name:", ua.Name, "v", ua.Version)
|
||||
fmt.Println("OS:", ua.OS, "v", ua.OSVersion)
|
||||
fmt.Println("Device:", ua.Device)
|
||||
if ua.Mobile {
|
||||
fmt.Println("(Mobile)")
|
||||
}
|
||||
if ua.Tablet {
|
||||
fmt.Println("(Tablet)")
|
||||
}
|
||||
if ua.Desktop {
|
||||
fmt.Println("(Desktop)")
|
||||
}
|
||||
if ua.Bot {
|
||||
fmt.Println("(Bot)")
|
||||
}
|
||||
if ua.URL != "" {
|
||||
fmt.Println(ua.URL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Notice
|
||||
|
||||
+ Opera and Opera Mini are two browsers, since they operate on very differenta way.
|
||||
+ If Googlebot (or any other bot) is detected and it is using its mobile crawler, both `bot` and `mobile` flags will be set to `true`.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package ua
|
||||
|
||||
// IsWindows shorthand function to check if OS == Windows
|
||||
func (ua UserAgent) IsWindows() bool {
|
||||
return ua.OS == Windows
|
||||
}
|
||||
|
||||
// IsAndroid shorthand function to check if OS == Android
|
||||
func (ua UserAgent) IsAndroid() bool {
|
||||
return ua.OS == Android
|
||||
}
|
||||
|
||||
// IsMacOS shorthand function to check if OS == MacOS
|
||||
func (ua UserAgent) IsMacOS() bool {
|
||||
return ua.OS == MacOS
|
||||
}
|
||||
|
||||
// IsIOS shorthand function to check if OS == IOS
|
||||
func (ua UserAgent) IsIOS() bool {
|
||||
return ua.OS == IOS
|
||||
}
|
||||
|
||||
// IsLinux shorthand function to check if OS == Linux
|
||||
func (ua UserAgent) IsLinux() bool {
|
||||
return ua.OS == Linux
|
||||
}
|
||||
|
||||
// IsOpera shorthand function to check if Name == Opera
|
||||
func (ua UserAgent) IsOpera() bool {
|
||||
return ua.Name == Opera
|
||||
}
|
||||
|
||||
// IsOperaMini shorthand function to check if Name == Opera Mini
|
||||
func (ua UserAgent) IsOperaMini() bool {
|
||||
return ua.Name == OperaMini
|
||||
}
|
||||
|
||||
// IsChrome shorthand function to check if Name == Chrome
|
||||
func (ua UserAgent) IsChrome() bool {
|
||||
return ua.Name == Chrome
|
||||
}
|
||||
|
||||
// IsFirefox shorthand function to check if Name == Firefox
|
||||
func (ua UserAgent) IsFirefox() bool {
|
||||
return ua.Name == Firefox
|
||||
}
|
||||
|
||||
// IsInternetExplorer shorthand function to check if Name == Internet Explorer
|
||||
func (ua UserAgent) IsInternetExplorer() bool {
|
||||
return ua.Name == InternetExplorer
|
||||
}
|
||||
|
||||
// IsSafari shorthand function to check if Name == Safari
|
||||
func (ua UserAgent) IsSafari() bool {
|
||||
return ua.Name == Safari
|
||||
}
|
||||
|
||||
// IsEdge shorthand function to check if Name == Edge
|
||||
func (ua UserAgent) IsEdge() bool {
|
||||
return ua.Name == Edge
|
||||
}
|
||||
|
||||
// IsGooglebot shorthand function to check if Name == Googlebot
|
||||
func (ua UserAgent) IsGooglebot() bool {
|
||||
return ua.Name == Googlebot
|
||||
}
|
||||
|
||||
// IsTwitterbot shorthand function to check if Name == Twitterbot
|
||||
func (ua UserAgent) IsTwitterbot() bool {
|
||||
return ua.Name == Twitterbot
|
||||
}
|
||||
|
||||
// IsFacebookbot shorthand function to check if Name == FacebookExternalHit
|
||||
func (ua UserAgent) IsFacebookbot() bool {
|
||||
return ua.Name == FacebookExternalHit
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
package ua
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UserAgent struct containg all determined datra from parsed user-agent string
|
||||
type UserAgent struct {
|
||||
Name string
|
||||
Version string
|
||||
OS string
|
||||
OSVersion string
|
||||
Device string
|
||||
|
||||
Mobile bool
|
||||
Tablet bool
|
||||
Desktop bool
|
||||
Bot bool
|
||||
URL string
|
||||
|
||||
String string
|
||||
}
|
||||
|
||||
var ignore = map[string]struct{}{
|
||||
"KHTML, like Gecko": struct{}{},
|
||||
"U": struct{}{},
|
||||
"compatible": struct{}{},
|
||||
"Mozilla": struct{}{},
|
||||
"WOW64": struct{}{},
|
||||
}
|
||||
|
||||
// Constants for browsers and operating systems for easier comparation
|
||||
const (
|
||||
Windows = "Windows"
|
||||
WindowsPhone = "Windows Phone"
|
||||
Android = "Android"
|
||||
MacOS = "macOS"
|
||||
IOS = "iOS"
|
||||
Linux = "Linux"
|
||||
|
||||
Opera = "Opera"
|
||||
OperaMini = "Opera Mini"
|
||||
Chrome = "Chrome"
|
||||
Firefox = "Firefox"
|
||||
InternetExplorer = "Internet Explorer"
|
||||
Safari = "Safari"
|
||||
Edge = "Edge"
|
||||
Vivaldi = "Vivaldi"
|
||||
|
||||
Googlebot = "Googlebot"
|
||||
Twitterbot = "Twitterbot"
|
||||
FacebookExternalHit = "facebookexternalhit"
|
||||
)
|
||||
|
||||
// Parse user agent string returning UserAgent struct
|
||||
func Parse(userAgent string) UserAgent {
|
||||
ua := UserAgent{
|
||||
String: userAgent,
|
||||
}
|
||||
|
||||
tokens := parse(userAgent)
|
||||
|
||||
// check is there URL
|
||||
for k := range tokens {
|
||||
if strings.HasPrefix(k, "http://") || strings.HasPrefix(k, "https://") {
|
||||
ua.URL = k
|
||||
delete(tokens, k)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// OS lookup
|
||||
switch {
|
||||
case tokens.exists("Android"):
|
||||
ua.OS = Android
|
||||
ua.OSVersion = tokens[Android]
|
||||
for s := range tokens {
|
||||
if strings.HasSuffix(s, "Build") {
|
||||
ua.Device = strings.TrimSpace(s[:len(s)-5])
|
||||
ua.Tablet = strings.Contains(strings.ToLower(ua.Device), "tablet")
|
||||
}
|
||||
}
|
||||
|
||||
case tokens.exists("iPhone"):
|
||||
ua.OS = IOS
|
||||
ua.OSVersion = tokens.findMacOSVersion()
|
||||
ua.Device = "iPhone"
|
||||
ua.Mobile = true
|
||||
|
||||
case tokens.exists("iPad"):
|
||||
ua.OS = IOS
|
||||
ua.OSVersion = tokens.findMacOSVersion()
|
||||
ua.Device = "iPad"
|
||||
ua.Tablet = true
|
||||
|
||||
case tokens.exists("Windows NT"):
|
||||
ua.OS = Windows
|
||||
ua.OSVersion = tokens["Windows NT"]
|
||||
ua.Desktop = true
|
||||
|
||||
case tokens.exists("Windows Phone OS"):
|
||||
ua.OS = WindowsPhone
|
||||
ua.OSVersion = tokens["Windows Phone OS"]
|
||||
ua.Mobile = true
|
||||
|
||||
case tokens.exists("Macintosh"):
|
||||
ua.OS = MacOS
|
||||
ua.OSVersion = tokens.findMacOSVersion()
|
||||
ua.Desktop = true
|
||||
|
||||
case tokens.exists("Linux"):
|
||||
ua.OS = Linux
|
||||
ua.OSVersion = tokens[Linux]
|
||||
ua.Desktop = true
|
||||
|
||||
}
|
||||
|
||||
// for s, val := range sys {
|
||||
// fmt.Println(s, "--", val)
|
||||
// }
|
||||
|
||||
switch {
|
||||
|
||||
case tokens.exists("Googlebot"):
|
||||
ua.Name = Googlebot
|
||||
ua.Version = tokens[Googlebot]
|
||||
ua.Bot = true
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
case tokens["Opera Mini"] != "":
|
||||
ua.Name = OperaMini
|
||||
ua.Version = tokens[OperaMini]
|
||||
ua.Mobile = true
|
||||
|
||||
case tokens["OPR"] != "":
|
||||
ua.Name = Opera
|
||||
ua.Version = tokens["OPR"]
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
// Opera on iOS
|
||||
case tokens["OPiOS"] != "":
|
||||
ua.Name = Opera
|
||||
ua.Version = tokens["OPiOS"]
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
// Chrome on iOS
|
||||
case tokens["CriOS"] != "":
|
||||
ua.Name = Chrome
|
||||
ua.Version = tokens["CriOS"]
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
// Firefox on iOS
|
||||
case tokens["FxiOS"] != "":
|
||||
ua.Name = Firefox
|
||||
ua.Version = tokens["FxiOS"]
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
case tokens["Firefox"] != "":
|
||||
ua.Name = Firefox
|
||||
ua.Version = tokens[Firefox]
|
||||
_, ua.Mobile = tokens["Mobile"]
|
||||
_, ua.Tablet = tokens["Tablet"]
|
||||
|
||||
case tokens["Vivaldi"] != "":
|
||||
ua.Name = Vivaldi
|
||||
ua.Version = tokens[Vivaldi]
|
||||
|
||||
case tokens.exists("MSIE"):
|
||||
ua.Name = InternetExplorer
|
||||
ua.Version = tokens["MSIE"]
|
||||
|
||||
case tokens["Edge"] != "":
|
||||
ua.Name = Edge
|
||||
ua.Version = tokens["Edge"]
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
case tokens["bingbot"] != "":
|
||||
ua.Name = "Bingbot"
|
||||
ua.Version = tokens["bingbot"]
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
case tokens["SamsungBrowser"] != "":
|
||||
ua.Name = "Samsung Browser"
|
||||
ua.Version = tokens["SamsungBrowser"]
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
// if chrome and Safari defined, find any other tokensent descr
|
||||
case tokens.exists(Chrome) && tokens.exists(Safari):
|
||||
name := tokens.findBestMatch(true)
|
||||
if name != "" {
|
||||
ua.Name = name
|
||||
ua.Version = tokens[name]
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case tokens.exists("Chrome"):
|
||||
ua.Name = Chrome
|
||||
ua.Version = tokens["Chrome"]
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
case tokens.exists("Safari"):
|
||||
ua.Name = Safari
|
||||
if v, ok := tokens["Version"]; ok {
|
||||
ua.Version = v
|
||||
} else {
|
||||
ua.Version = tokens["Safari"]
|
||||
}
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
|
||||
default:
|
||||
if ua.OS == "Android" && tokens["Version"] != "" {
|
||||
ua.Name = "Android browser"
|
||||
ua.Version = tokens["Version"]
|
||||
ua.Mobile = true
|
||||
} else {
|
||||
if name := tokens.findBestMatch(false); name != "" {
|
||||
ua.Name = name
|
||||
ua.Version = tokens[name]
|
||||
} else {
|
||||
ua.Name = ua.String
|
||||
}
|
||||
ua.Bot = strings.Contains(strings.ToLower(ua.Name), "bot")
|
||||
ua.Mobile = tokens.existsAny("Mobile", "Mobile Safari")
|
||||
}
|
||||
}
|
||||
|
||||
// if tabler, switch mobile to off
|
||||
if ua.Tablet {
|
||||
ua.Mobile = false
|
||||
}
|
||||
|
||||
// if not already bot, check some popular bots and weather URL is set
|
||||
if !ua.Bot {
|
||||
ua.Bot = ua.URL != ""
|
||||
}
|
||||
|
||||
if !ua.Bot {
|
||||
switch ua.Name {
|
||||
case Twitterbot, FacebookExternalHit:
|
||||
ua.Bot = true
|
||||
}
|
||||
}
|
||||
|
||||
return ua
|
||||
}
|
||||
|
||||
func parse(userAgent string) (clients properties) {
|
||||
clients = make(map[string]string, 0)
|
||||
slash := false
|
||||
isURL := false
|
||||
var buff, val bytes.Buffer
|
||||
addToken := func() {
|
||||
if buff.Len() != 0 {
|
||||
s := strings.TrimSpace(buff.String())
|
||||
if _, ign := ignore[s]; !ign {
|
||||
if isURL {
|
||||
s = strings.TrimPrefix(s, "+")
|
||||
}
|
||||
|
||||
if val.Len() == 0 { // only if value don't exists
|
||||
var ver string
|
||||
s, ver = checkVer(s) // determin version string and split
|
||||
clients[s] = ver
|
||||
} else {
|
||||
clients[s] = strings.TrimSpace(val.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
buff.Reset()
|
||||
val.Reset()
|
||||
slash = false
|
||||
isURL = false
|
||||
}
|
||||
|
||||
parOpen := false
|
||||
|
||||
bua := []byte(userAgent)
|
||||
for i, c := range bua {
|
||||
|
||||
//fmt.Println(string(c), c)
|
||||
switch {
|
||||
case c == 41: // )
|
||||
addToken()
|
||||
parOpen = false
|
||||
|
||||
case parOpen && c == 59: // ;
|
||||
addToken()
|
||||
|
||||
case c == 40: // (
|
||||
addToken()
|
||||
parOpen = true
|
||||
|
||||
case slash && c == 32:
|
||||
addToken()
|
||||
|
||||
case slash:
|
||||
val.WriteByte(c)
|
||||
|
||||
case c == 47 && !isURL: // /
|
||||
if i != len(bua)-1 && bua[i+1] == 47 && (bytes.HasSuffix(buff.Bytes(), []byte("http:")) || bytes.HasSuffix(buff.Bytes(), []byte("https:"))) {
|
||||
buff.WriteByte(c)
|
||||
isURL = true
|
||||
} else {
|
||||
slash = true
|
||||
}
|
||||
|
||||
default:
|
||||
buff.WriteByte(c)
|
||||
}
|
||||
}
|
||||
addToken()
|
||||
|
||||
return clients
|
||||
}
|
||||
|
||||
func checkVer(s string) (name, v string) {
|
||||
i := strings.LastIndex(s, " ")
|
||||
if i == -1 {
|
||||
return s, ""
|
||||
}
|
||||
|
||||
//v = s[i+1:]
|
||||
|
||||
switch s[:i] {
|
||||
case "Linux", "Windows NT", "Windows Phone OS", "MSIE", "Android":
|
||||
return s[:i], s[i+1:]
|
||||
default:
|
||||
return s, ""
|
||||
}
|
||||
|
||||
// for _, c := range v {
|
||||
// if (c >= 48 && c <= 57) || c == 46 {
|
||||
// } else {
|
||||
// return s, ""
|
||||
// }
|
||||
// }
|
||||
|
||||
// return s[:i], s[i+1:]
|
||||
|
||||
}
|
||||
|
||||
type properties map[string]string
|
||||
|
||||
func (p properties) exists(key string) bool {
|
||||
_, ok := p[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p properties) existsAny(keys ...string) bool {
|
||||
for _, k := range keys {
|
||||
if _, ok := p[k]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p properties) findMacOSVersion() string {
|
||||
for k, v := range p {
|
||||
if strings.Contains(k, "OS") {
|
||||
if ver := findVersion(v); ver != "" {
|
||||
return ver
|
||||
} else if ver = findVersion(k); ver != "" {
|
||||
return ver
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// findBestMatch from the rest of the bunch
|
||||
// in first cycle only return key vith version value
|
||||
// if withVerValue is false, do another cycle and return any token
|
||||
func (p properties) findBestMatch(withVerOnly bool) string {
|
||||
n := 2
|
||||
if withVerOnly {
|
||||
n = 1
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
for k, v := range p {
|
||||
switch k {
|
||||
case Chrome, Firefox, Safari, "Version", "Mobile", "Mobile Safari", "Mozilla", "AppleWebKit", "Windows NT", "Windows Phone OS", Android, "Macintosh", Linux:
|
||||
default:
|
||||
if i == 0 {
|
||||
if v != "" { // in first check, only return keys with value
|
||||
return k
|
||||
}
|
||||
} else {
|
||||
return k
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var rxMacOSVer = regexp.MustCompile("[_\\d\\.]+")
|
||||
|
||||
func findVersion(s string) string {
|
||||
if ver := rxMacOSVer.FindString(s); ver != "" {
|
||||
return strings.Replace(ver, "_", ".", -1)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
package ua_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mileusna/useragent"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
var testTable = [][]string{
|
||||
// Mac
|
||||
{"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8", ua.Safari, "10.1.2", "desktop", "macOS"},
|
||||
{"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36", ua.Chrome, "60.0.3112.90", "desktop", "macOS"},
|
||||
{"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0", ua.Firefox, "54.0", "desktop", "macOS"},
|
||||
{"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2597.57", ua.Opera, "46.0.2597.57", "desktop", "macOS"},
|
||||
{"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.91 Safari/537.36 Vivaldi/1.92.917.39", "Vivaldi", "1.92.917.39", "desktop", "macOS"},
|
||||
|
||||
// Windows
|
||||
{"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", ua.Chrome, "59.0.3071.115", "desktop", "Windows"},
|
||||
{"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2; GWX:RED)", ua.InternetExplorer, "8.0", "desktop", "Windows"},
|
||||
{"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322) NS8/0.9.6", ua.InternetExplorer, "6.0", "desktop", "Windows"},
|
||||
|
||||
// iPhone
|
||||
{"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.0 Mobile/14F89 Safari/602.1", ua.Safari, "10.0", "mobile", "iOS"},
|
||||
{"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) CriOS/60.0.3112.89 Mobile/14F89 Safari/602.1", ua.Chrome, "60.0.3112.89", "mobile", "iOS"},
|
||||
{"Mozilla/5.0 (iPhone; CPU iPhone OS 9_3 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) OPiOS/14.0.0.104835 Mobile/13E233 Safari/9537.53", ua.Opera, "14.0.0.104835", "mobile", "iOS"},
|
||||
{"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) FxiOS/8.1.1b4948 Mobile/14F89 Safari/603.2.4", ua.Firefox, "8.1.1b4948", "mobile", "iOS"},
|
||||
|
||||
// iPad
|
||||
{"Mozilla/5.0 (iPad; CPU OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.0 Mobile/14F89 Safari/602.1", ua.Safari, "10.0", "tablet", "iOS"},
|
||||
{"Mozilla/5.0 (iPad; CPU OS 10_3_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/58.0.3029.113 Mobile/14F89 Safari/602.1", ua.Chrome, "58.0.3029.113", "tablet", "iOS"},
|
||||
{"Mozilla/5.0 (iPad; CPU OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) FxiOS/8.1.1b4948 Mobile/14F89 Safari/603.2.4", ua.Firefox, "8.1.1b4948", "tablet", "iOS"},
|
||||
|
||||
// Andorid
|
||||
{"Mozilla/5.0 (Linux; Android 4.3; GT-I9300 Build/JSS15J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36", ua.Chrome, "59.0.3071.125", "mobile", "Android"},
|
||||
{"Mozilla/5.0 (Android 4.3; Mobile; rv:54.0) Gecko/54.0 Firefox/54.0", ua.Firefox, "54.0", "mobile", "Android"},
|
||||
{"Mozilla/5.0 (Linux; Android 4.3; GT-I9300 Build/JSS15J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36 OPR/42.9.2246.119956", ua.Opera, "42.9.2246.119956", "mobile", ua.Android},
|
||||
{"Opera/9.80 (Android; Opera Mini/28.0.2254/66.318; U; en) Presto/2.12.423 Version/12.16", ua.OperaMini, "28.0.2254/66.318", "mobile", "Android"},
|
||||
{"Mozilla/5.0 (Linux; U; Android 4.3; en-us; GT-I9300 Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", "Android browser", "4.0", "mobile", "Android"},
|
||||
|
||||
{"Mozilla/5.0 (Linux; Android 6.0.1; SAMSUNG SM-A310F/A310FXXU2BQB1 Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/5.4 Chrome/51.0.2704.106 Mobile Safari/537.36", "Samsung Browser", "5.4", "mobile", "Android"},
|
||||
|
||||
// Windows phone
|
||||
{"Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; NOKIA; Lumia 630)", ua.InternetExplorer, "7.0", "mobile", ua.WindowsPhone},
|
||||
|
||||
// Bots
|
||||
{"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", ua.Googlebot, "2.1", "mobile", "Android"},
|
||||
{"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", ua.Googlebot, "2.1", "bot", ""},
|
||||
{"Twitterbot/1.0", ua.Twitterbot, "1.0", "bot", ""},
|
||||
{"facebookexternalhit/1.1", ua.FacebookExternalHit, "1.1", "bot", ""},
|
||||
|
||||
// other
|
||||
{"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 Google (+https://developers.google.com/+/web/snippet/)", ua.Chrome, "56.0.2924.87", "bot", ua.Linux}, // Google+ fetch
|
||||
|
||||
// tools
|
||||
{"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.6.0 Chrome/45.0.2454.101 Safari/537.36", "QtWebEngine", "5.6.0", "", "macOS"},
|
||||
{"Go-http-client/1.1", "Go-http-client", "1.1", "", ""},
|
||||
{"Wget/1.12 (linux-gnu)", "Wget", "1.12", "", ""},
|
||||
{"Wget/1.17.1 (darwin15.2.0)", "Wget", "1.17.1", "", ""},
|
||||
|
||||
// unstandard stuff
|
||||
{"BUbiNG (+http://law.di.unimi.it/BUbiNG.html)", "BUbiNG", "", "", ""},
|
||||
|
||||
//GooglePlus "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 Google (+https://developers.google.com/+/web/snippet/)"
|
||||
//Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5 (Applebot/0.1; +http://www.apple.com/go/applebot)
|
||||
//Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.6.0 Chrome/45.0.2454.101 Safari/537.36
|
||||
}
|
||||
|
||||
for _, test := range testTable {
|
||||
ua := ua.Parse(test[0])
|
||||
if ua.Name != test[1] {
|
||||
t.Error("\n", test[0], "\nName should be", test[1], "not", ua.Name)
|
||||
}
|
||||
if ua.Version != test[2] {
|
||||
t.Error("\nVersion should be", test[2], "not", ua.Version)
|
||||
}
|
||||
|
||||
if len(test) > 3 {
|
||||
if test[3] == "desktop" && ua.Mobile {
|
||||
t.Error("\n", ua.String, "should be desktop type not mobile")
|
||||
}
|
||||
|
||||
if test[3] == "mobile" && !ua.Mobile {
|
||||
t.Error("\n", ua.String, "should be mobile")
|
||||
}
|
||||
if test[3] == "tablet" && !ua.Tablet {
|
||||
t.Error("\n", ua.String, "should be tablet")
|
||||
}
|
||||
}
|
||||
|
||||
if len(test) > 4 && test[4] != ua.OS {
|
||||
t.Error("\n", test[0], "OS should", test[4], "not", ua.OS)
|
||||
}
|
||||
//fmt.Println(ua.OS, ua.OSVersion, ua.Device)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func ExampleParse() {
|
||||
userAgents := []string{
|
||||
// Mac
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2597.57",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.91 Safari/537.36 Vivaldi/1.92.917.39",
|
||||
|
||||
// Windows
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
|
||||
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2; GWX:RED)",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322) NS8/0.9.6",
|
||||
|
||||
// iPhone
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.0 Mobile/14F89 Safari/602.1",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) CriOS/60.0.3112.89 Mobile/14F89 Safari/602.1",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 9_3 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) OPiOS/14.0.0.104835 Mobile/13E233 Safari/9537.53",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) FxiOS/8.1.1b4948 Mobile/14F89 Safari/603.2.4",
|
||||
|
||||
// iPad
|
||||
"Mozilla/5.0 (iPad; CPU OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.0 Mobile/14F89 Safari/602.1",
|
||||
"Mozilla/5.0 (iPad; CPU OS 10_3_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/58.0.3029.113 Mobile/14F89 Safari/602.1",
|
||||
"Mozilla/5.0 (iPad; CPU OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) FxiOS/8.1.1b4948 Mobile/14F89 Safari/603.2.4",
|
||||
|
||||
// Andorid
|
||||
"Mozilla/5.0 (Linux; Android 4.3; GT-I9300 Build/JSS15J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Android 4.3; Mobile; rv:54.0) Gecko/54.0 Firefox/54.0",
|
||||
"Mozilla/5.0 (Linux; Android 4.3; GT-I9300 Build/JSS15J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36 OPR/42.9.2246.119956",
|
||||
"Opera/9.80 (Android; Opera Mini/28.0.2254/66.318; U; en) Presto/2.12.423 Version/12.16",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.3; en-us; GT-I9300 Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
|
||||
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; SAMSUNG SM-A310F/A310FXXU2BQB1 Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/5.4 Chrome/51.0.2704.106 Mobile Safari/537.36",
|
||||
}
|
||||
|
||||
for _, s := range userAgents {
|
||||
ua := ua.Parse(s)
|
||||
fmt.Println()
|
||||
fmt.Println(ua.String)
|
||||
fmt.Println(strings.Repeat("=", len(ua.String)))
|
||||
fmt.Println("Name:", ua.Name, "v", ua.Version)
|
||||
fmt.Println("OS:", ua.OS, "v", ua.OSVersion)
|
||||
fmt.Println("Device:", ua.Device)
|
||||
if ua.Mobile {
|
||||
fmt.Println("(Mobile)")
|
||||
}
|
||||
if ua.Tablet {
|
||||
fmt.Println("(Tablet)")
|
||||
}
|
||||
if ua.Desktop {
|
||||
fmt.Println("(Desktop)")
|
||||
}
|
||||
if ua.Bot {
|
||||
fmt.Println("(Bot)")
|
||||
}
|
||||
if ua.URL != "" {
|
||||
fmt.Println(ua.URL)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user