uaparser

package module
v0.0.0-...-9d90180 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2025 License: MIT Imports: 12 Imported by: 0

README

uaparser-go

English | 简体中文

Go Version MIT License Go Report Card GoDoc

🚀 A fast, reliable User Agent string parser written in Go

A high-performance User Agent parsing library with support for custom logging, caching optimization, and thread-safe operations.

View Documentation · Report Bug · Request Feature

Table of Contents

About the Project

UA Parser is a high-performance User Agent string parsing library designed specifically for Go. It can quickly and accurately extract browser information from User Agent strings, including browser type and version numbers.

This project is inspired by and builds upon the excellent work of ua-parser/uap-go, with significant enhancements in version parsing capabilities and additional utility features.

Key Improvements
  • Enhanced Version Parsing: Support for arbitrary-length version numbers (e.g., "1.2.3.4.5.6")
  • Version Comparison Tools: Built-in utilities for version range matching and comparison
  • Performance Optimizations: Advanced caching and dynamic sorting mechanisms
  • Custom Logging: Flexible logging interface for debugging and monitoring
Design Goals
  • High Performance: Built-in LRU caching and dynamic sorting optimization
  • Easy to Use: Clean API design, ready to use out of the box
  • Highly Configurable: Support for custom logging and performance tuning
  • Thread Safe: Support for high-concurrency scenarios
  • Extended Functionality: Version comparison and range matching utilities

Key Features

  • Fast Parsing: Extract browser family and version from User Agent strings
  • Enhanced Version Support: Parse arbitrary-length version numbers (e.g., "1.2.3.4.5.6")
  • Version Comparison: Built-in version comparison and range matching utilities
  • High-Performance Caching: Built-in LRU cache mechanism to avoid repeated parsing
  • Custom Logging: Support for any logger implementing the Logger interface
  • Dynamic Optimization: Automatically optimize regex order based on usage frequency
  • Thread Safe: Full support for concurrent access
  • Wide Compatibility: Support for hundreds of browsers and device identification

Getting Started

Prerequisites
  • Go 1.16 or higher
Installation
go get github.com/BaoziCDR/uaparser-go
Quick Start
package main

import (
    "fmt"
    "log"
    
    "github.com/BaoziCDR/uaparser-go"
)

func main() {
    // Create a new parser
    parser, err := uaparser.NewFromSaved()
    if err != nil {
        log.Fatal(err)
    }

    // Parse a user agent string
    ua := parser.Parse("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
    
    fmt.Printf("Family: %s\n", ua.Family)     // Chrome
    fmt.Printf("Version: %s\n", ua.Version)   // 91.0.4472.124
    fmt.Printf("String: %s\n", ua.ToString()) // Chrome 91.0.4472.124
}
Version Comparison

The library includes powerful version comparison utilities:

package main

import (
    "fmt"
    "github.com/BaoziCDR/uaparser-go"
)

func main() {
    // Create version comparables
    version1 := uaparser.VersionComparable("91.0.4472.124")
    version2 := uaparser.VersionComparable("91.0.4472.125")
    
    // Compare versions
    result := version1.Compare(version2)
    if result < 0 {
        fmt.Println("version1 is older than version2")
    } else if result > 0 {
        fmt.Println("version1 is newer than version2")
    } else {
        fmt.Println("versions are equal")
    }
    
    // Range matching
    chromeVersion := uaparser.VersionComparable("91.0.4472.124")
    
    // Check if version is in range [90.0.0.0, 92.0.0.0)
    inRange := uaparser.MatchRange("[90.0.0.0,92.0.0.0)", chromeVersion)
    fmt.Printf("Chrome 91.0.4472.124 is in range [90.0.0.0, 92.0.0.0): %v\n", inRange)
    
    // Check if version is greater than 90.0.0.0
    isNewer := uaparser.MatchRange("(90.0.0.0,)", chromeVersion)
    fmt.Printf("Chrome 91.0.4472.124 is newer than 90.0.0.0: %v\n", isNewer)
}
Advanced Version Parsing

Unlike the original ua-parser implementation, this library supports arbitrary-length version numbers:

package main

import (
    "fmt"
    "log"
    "github.com/BaoziCDR/uaparser-go"
)

func main() {
    parser, err := uaparser.NewFromSaved()
    if err != nil {
        log.Fatal(err)
    }

    // Parse complex version numbers
    testCases := []string{
        "CustomBrowser/1.2.3.4.5.6",
        "MyApp/10.15.7.21.1050.2.1",
        "Enterprise/2023.1.15.build.12345",
    }
    
    for _, ua := range testCases {
        result := parser.Parse(ua)
        fmt.Printf("UA: %s\n", ua)
        fmt.Printf("Family: %s, Version: %s\n\n", result.Family, result.Version)
    }
}

Project Structure

uaparser/
├── parser.go            # Core parser implementation
├── user_agent.go        # User agent structures
├── logger.go            # Logger interface and implementations
├── option.go            # Configuration options  
├── cache.go             # Caching implementation
├── comparable.go        # Version comparison utilities
├── defualt_yaml.go      # Built-in regex definitions
├── test/                # Test files
│   └── parser_test.go   # Unit tests
├── example/             # Usage examples
│   └── example.go       # Example implementations
├── README.md            # English Documentation
├── README_Zh.md         # Chinese Documentation
└── go.mod               # Go Module Definition

Configuration Options

Basic Configuration
parser, err := uaparser.NewFromSaved(
    uaparser.WithLogger(myLogger),           // Custom logger
    uaparser.WithDebugMode(true),            // Enable debug logging
    uaparser.WithUseSort(true),              // Enable sorting optimization
    uaparser.WithMissesThreshold(1000000),   // Cache miss threshold
    uaparser.WithMatchIdxNotOk(25),          // Match index threshold
)
Custom Logging
// Use built-in default logger
logger := uaparser.NewDefaultLogger()

// Or use a custom logger
type MyLogger struct{}

func (l *MyLogger) Infof(format string, args ...interface{}) {
    // Implement your logging logic
}

parser, _ := uaparser.NewFromSaved(uaparser.WithLogger(&MyLogger{}))
Performance Tuning Options
Option Description Default Recommended Scenario
WithUseSort Enable dynamic sorting optimization false High-concurrency scenarios
WithMissesThreshold Number of misses to trigger reordering 500,000 Adjust based on concurrency level
WithMatchIdxNotOk Index threshold for "poor matches" 20 Adjust based on main user base
Available Options
  • WithLogger(logger Logger) - Set a custom logger
  • WithDebugMode(bool) - Enable/disable debug logging
  • WithUseSort(bool) - Enable/disable automatic sorting of regex patterns by usage
  • WithMissesThreshold(uint64) - Set the threshold for triggering pattern sorting
  • WithMatchIdxNotOk(int) - Set the index threshold for counting cache misses
Built-in Loggers
DefaultLogger

Logs to stdout with timestamps:

logger := uaparser.NewDefaultLogger()
NoOpLogger

Disables all logging (default):

logger := uaparser.NewNoOpLogger()

Performance Optimization

Caching Mechanism
  • LRU Cache: Automatically cache parsing results to avoid repeated calculations
  • Cache Size: Default 1024 records
  • Hit Rate: Can reach 95%+ in typical applications
Dynamic Sorting
// Enable dynamic sorting, frequently used regex patterns will automatically move to the front
parser, _ := uaparser.NewFromSaved(
    uaparser.WithUseSort(true),
    uaparser.WithMissesThreshold(100000), // Lower threshold for more frequent optimization
)
Memory Alignment

All critical data structures are optimized for memory alignment, delivering best performance on 64-bit systems.

Performance Features
  • Caching: Parsed results are cached using LRU cache
  • Pattern Sorting: Frequently matched patterns are moved to the front
  • Atomic Operations: Thread-safe counters for statistics
  • Memory Alignment: Optimized struct layout for better performance

Supported Browsers

Desktop Browsers
  • Chrome, Firefox, Safari, Edge
  • Internet Explorer, Opera
  • Various Chromium-based browsers
Mobile Browsers
  • Chrome Mobile, Firefox Mobile, Safari Mobile
  • WeChat Browser, QQ Browser, UC Browser
  • Baidu Browser, Mi Browser, Huawei Browser
Special Applications
  • Electron applications, WebView
  • Crawlers and bots
  • Various API clients

Development Guide

Running Tests
# Run all tests
go test ./test/ -v

# Run benchmark tests
go test ./test/ -bench=. -benchmem
Running Examples
# Run basic example
go run ./example/example.go
Performance Testing

The project includes comprehensive performance testing examples showcasing the effects of various configuration options.

Contributing

Contributions make the open source community an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

How to Contribute
  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request
Development Guidelines
  • Ensure code passes all tests
  • Follow Go language coding standards
  • Add appropriate test cases for new features
  • Update relevant documentation

Changelog

  • v1.0.0 - Initial release
    • Basic User Agent parsing functionality
    • Built-in caching mechanism
    • Custom logging support

See Releases for detailed changelog.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Based on ua-parser/uap-go: This project builds upon the excellent foundation provided by the official ua-parser Go implementation
  • Thanks to hashicorp/golang-lru for LRU cache implementation
  • Thanks to yaml.v2 for YAML parsing support
  • Thanks to the ua-parser community for maintaining the regex definitions
  • Thanks to all developers who contributed to this project

⬆ Back to top

If this project helps you, please give it a ⭐️!

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefinitionYaml = []byte(`
user_agent_parsers:
  - regex: '(GeoEvent Server) ((?:\d+\.)*\d+)'
  - regex: '(ArcGIS Pro)(?: ((?:\d+\.)*\d+)|)'
  - regex: 'ArcGIS Client Using WinInet'
    family_replacement: 'ArcMap'
  - regex: '(OperationsDashboard)-(?:Windows)-((?:\d+\.)*\d+)'
    family_replacement: 'Operations Dashboard for ArcGIS'
  - regex: '(arcgisearth)/((?:\d+\.)*\d+)'
    family_replacement: 'ArcGIS Earth'
  - regex: 'com.esri.(earth).phone/((?:\d+\.)*\d+)'
    family_replacement: 'ArcGIS Earth'
  - regex: '(arcgis-explorer)/((?:\d+\.)*\d+)'
    family_replacement: 'Explorer for ArcGIS'
  - regex: 'arcgis-(collector|aurora)/((?:\d+\.)*\d+)'
    family_replacement: 'Collector for ArcGIS'
  - regex: '(arcgis-workforce)/((?:\d+\.)*\d+)'
    family_replacement: 'Workforce for ArcGIS'
  - regex: '(Collector|Explorer|Workforce)-(?:Android|iOS)-((?:\d+\.)*\d+)'
    family_replacement: '$1 for ArcGIS'
  - regex: '(Explorer|Collector)/(\d+) CFNetwork'
    family_replacement: '$1 for ArcGIS'
  - regex: 'ArcGISRuntime-(Android|iOS|NET|Qt)/((?:\d+\.)*\d+)'
    family_replacement: 'ArcGIS Runtime SDK for $1'
  - regex: 'ArcGIS\.?(iOS|Android|NET|Qt)(?:-|\.)(\d+)\.(\d+)(?:\.(\d+)|)'
    family_replacement: 'ArcGIS Runtime SDK for $1'
  - regex: 'ArcGIS\.Runtime\.(Qt)\.((?:\d+\.)*\d+)'
    family_replacement: 'ArcGIS Runtime SDK for $1'
  - regex: '^(Luminary)[Stage]+/(\d+) CFNetwork'
  - regex: '(ESPN)[%20| ]+Radio/((?:\d+\.)*\d+) CFNetwork'
  - regex: '(Antenna)/(\d+) CFNetwork'
    family_replacement: 'AntennaPod'
  - regex: '(TopPodcasts)Pro/(\d+) CFNetwork'
  - regex: '(MusicDownloader)Lite/((?:\d+\.)*\d+) CFNetwork'
  - regex: '^(.{0,200})-iPad\/((?:\d+\.)*\d+) CFNetwork'
  - regex: '^(.{0,200})-iPhone/((?:\d+\.)*\d+) CFNetwork'
  - regex: '^(.{0,200})/((?:\d+\.)*\d+) CFNetwork'
  - regex: '^(Luminary)/((?:\d+\.)*\d+)'
  - regex: '(espn\.go)'
    family_replacement: 'ESPN'
  - regex: '(espnradio\.com)'
    family_replacement: 'ESPN'
  - regex: 'ESPN APP$'
    family_replacement: 'ESPN'
  - regex: '(audioboom\.com)'
    family_replacement: 'AudioBoom'
  - regex: ' (Rivo) RHYTHM'
  - regex: '(CFNetwork)(?:/((?:\d+\.)*\d+)|)'
    family_replacement: 'CFNetwork'
  - regex: '(Pingdom\.com_bot_version_)((?:\d+\.)*\d+)'
    family_replacement: 'PingdomBot'
  - regex: '(PingdomTMS)/((?:\d+\.)*\d+)'
    family_replacement: 'PingdomBot'
  - regex: '(PingdomPageSpeed)/((?:\d+\.)*\d+)'
    family_replacement: 'PingdomBot'
  - regex: ' (PTST)/((?:\d+\.)*\d+)$'
    family_replacement: 'WebPageTest.org bot'
  - regex: 'X11; (Datanyze); Linux'
  - regex: '(NewRelicPinger)/((?:\d+\.)*\d+)'
    family_replacement: 'NewRelicPingerBot'
  - regex: '(Tableau)/((?:\d+\.)*\d+)'
    family_replacement: 'Tableau'
  - regex: 'AppleWebKit/\d{1,10}\.\d{1,10}.{0,200} Safari.{0,200} (CreativeCloud)/((?:\d+\.)*\d+)'
    family_replacement: 'Adobe CreativeCloud'
  - regex: '(Salesforce)(?:.)\/((?:\d+\.)*\d+)'
  - regex: '(\(StatusCake\))'
    family_replacement: 'StatusCakeBot'
  - regex: '(facebookexternalhit)/((?:\d+\.)*\d+)'
    family_replacement: 'FacebookBot'
  - regex: 'Google.{0,50}/\+/web/snippet'
    family_replacement: 'GooglePlusBot'
  - regex: 'via ggpht\.com GoogleImageProxy'
    family_replacement: 'GmailImageProxy'
  - regex: 'YahooMailProxy; https://help\.yahoo\.com/kb/yahoo-mail-proxy-SLN28749\.html'
    family_replacement: 'YahooMailProxy'
  - regex: '(Twitterbot)/((?:\d+\.)*\d+)'
    family_replacement: 'Twitterbot'
  - regex: '/((?:Ant-|)Nutch|[A-z]+[Bb]ot|[A-z]+[Ss]pider|Axtaris|fetchurl|Isara|ShopSalad|Tailsweep)[ \-]((?:\d+\.)*\d+)'
  - regex: '\b(008|Altresium|Argus|BaiduMobaider|BoardReader|DNSGroup|DataparkSearch|EDI|Goodzer|Grub|INGRID|Infohelfer|LinkedInBot|LOOQ|Nutch|OgScrper|Pandora|PathDefender|Peew|PostPost|Steeler|Twitterbot|VSE|WebCrunch|WebZIP|Y!J-BR[A-Z]|YahooSeeker|envolk|sproose|wminer)/((?:\d+\.)*\d+)'
  - regex: '(MSIE) ((?:\d+\.)*\d+)([a-z]\d|[a-z]|);.{0,200} MSIECrawler'
    family_replacement: 'MSIECrawler'
  - regex: '(DAVdroid)/((?:\d+\.)*\d+)'
  - regex: '(Google-HTTP-Java-Client|Apache-HttpClient|PostmanRuntime|Go-http-client|scalaj-http|http%20client|Python-urllib|HttpMonitor|TLSProber|WinHTTP|JNLP|okhttp|aihttp|reqwest|axios|unirest-(?:java|python|ruby|nodejs|php|net))(?:[ /]((?:\d+\.)*\d+)|)'
  - regex: '(Pinterest(?:bot|))/((?:\d+\.)*\d+)[;\s(]+\+https://www.pinterest.com/bot.html'
    family_replacement: 'Pinterestbot'
  - regex: '(CSimpleSpider|Cityreview Robot|CrawlDaddy|CrawlFire|Finderbots|Index crawler|Job Roboter|KiwiStatus Spider|Lijit Crawler|QuerySeekerSpider|ScollSpider|Trends Crawler|USyd-NLP-Spider|SiteCat Webbot|BotName\/\$BotVersion|123metaspider-Bot|1470\.net crawler|50\.nu|8bo Crawler Bot|Aboundex|Accoona-[A-z]{1,30}-Agent|AdsBot-Google(?:-[a-z]{1,30}|)|altavista|AppEngine-Google|archive.{0,30}\.org_bot|archiver|Ask Jeeves|[Bb]ai[Dd]u[Ss]pider(?:-[A-Za-z]{1,30})(?:-[A-Za-z]{1,30}|)|bingbot|BingPreview|blitzbot|BlogBridge|Bloglovin|BoardReader Blog Indexer|BoardReader Favicon Fetcher|boitho.com-dc|BotSeer|BUbiNG|\b\w{0,30}favicon\w{0,30}\b|\bYeti(?:-[a-z]{1,30}|)|Catchpoint(?: bot|)|[Cc]harlotte|Checklinks|clumboot|Comodo HTTP\(S\) Crawler|Comodo-Webinspector-Crawler|ConveraCrawler|CRAWL-E|CrawlConvera|Daumoa(?:-feedfetcher|)|Feed Seeker Bot|Feedbin|findlinks|Flamingo_SearchEngine|FollowSite Bot|furlbot|Genieo|gigabot|GomezAgent|gonzo1|(?:[a-zA-Z]{1,30}-|)Googlebot(?:-[a-zA-Z]{1,30}|)|Google SketchUp|grub-client|gsa-crawler|heritrix|HiddenMarket|holmes|HooWWWer|htdig|ia_archiver|ICC-Crawler|Icarus6j|ichiro(?:/mobile|)|IconSurf|IlTrovatore(?:-Setaccio|)|InfuzApp|Innovazion Crawler|InternetArchive|IP2[a-z]{1,30}Bot|jbot\b|KaloogaBot|Kraken|Kurzor|larbin|LEIA|LesnikBot|Linguee Bot|LinkAider|LinkedInBot|Lite Bot|Llaut|lycos|Mail\.RU_Bot|masscan|masidani_bot|Mediapartners-Google|Microsoft .{0,30} Bot|mogimogi|mozDex|MJ12bot|msnbot(?:-media {0,2}|)|msrbot|Mtps Feed Aggregation System|netresearch|Netvibes|NewsGator[^/]{0,30}|^NING|Nutch[^/]{0,30}|Nymesis|ObjectsSearch|OgScrper|Orbiter|OOZBOT|PagePeeker|PagesInventory|PaxleFramework|Peeplo Screenshot Bot|PHPCrawl|PlantyNet_WebRobot|Pompos|Qwantify|Read%20Later|Reaper|RedCarpet|Retreiver|Riddler|Rival IQ|scooter|Scrapy|Scrubby|searchsight|seekbot|semanticdiscovery|SemrushBot|Simpy|SimplePie|SEOstats|SimpleRSS|SiteCon|Slackbot-LinkExpanding|Slack-ImgProxy|Slurp|snappy|Speedy Spider|Squrl Java|Stringer|TheUsefulbot|ThumbShotsBot|Thumbshots\.ru|Tiny Tiny RSS|Twitterbot|WhatsApp|URL2PNG|Vagabondo|VoilaBot|^vortex|Votay bot|^voyager|WASALive.Bot|Web-sniffer|WebThumb|WeSEE:[A-z]{1,30}|WhatWeb|WIRE|WordPress|Wotbox|www\.almaden\.ibm\.com|Xenu(?:.s|) Link Sleuth|Xerka [A-z]{1,30}Bot|yacy(?:bot|)|YahooSeeker|Yahoo! Slurp|Yandex\w{1,30}|YodaoBot(?:-[A-z]{1,30}|)|YottaaMonitor|Yowedo|^Zao|^Zao-Crawler|ZeBot_www\.ze\.bz|ZooShot|ZyBorg|ArcGIS Hub Indexer)(?:[ /]v?((?:\d+\.)*\d+)|)'
  - regex: '\b(Boto3?|JetS3t|aws-(?:cli|sdk-(?:cpp|go|go-v\d|java|nodejs|ruby2?|dotnet-(?:\d{1,2}|core)))|s3fs)/((?:\d+\.)*\d+)'
  - regex: '(FME)\/((?:\d+\.)*\d+)'
  - regex: '(QGIS)\/((?:\d+\.)*\d+)'
  - regex: '(JOSM)/((?:\d+\.)*\d+)'
  - regex: '(Tygron Platform) \(((?:\d+\.)*\d+(?: RC \d+\.\d+)?)\)'
  - regex: '\[(FBAN/MessengerForiOS|FB_IAB/MESSENGER);FBAV/((?:\d+\.)*\d+)'
    family_replacement: 'Facebook Messenger'
  - regex: '\[FB.{0,300};(FBAV)/((?:\d+\.)*\d+)'
    family_replacement: 'Facebook'
  - regex: '\[FB.{0,300};'
    family_replacement: 'Facebook'
  - regex: '(RecipeRadar)/((?:\d+\.)*\d+)'
  - regex: '^.{0,200}?(?:\/[A-Za-z0-9\.]{0,50}|) {0,2}([A-Za-z0-9 \-_\!\[\]:]{0,50}(?:[Aa]rchiver|[Ii]ndexer|[Ss]craper|[Bb]ot|[Ss]pider|[Cc]rawl[a-z]{0,50}))[/ ]((?:\d+\.)*\d+)'
  - regex: '^.{0,200}?((?:[A-Za-z][A-Za-z0-9 -]{0,50}|)[^C][^Uu][Bb]ot)\b(?:(?:[ /]| v)((?:\d+\.)*\d+)|)'
  - regex: '^.{0,200}?((?:[A-z0-9]{1,50}|[A-z\-]{1,50} ?|)(?: the |)(?:[Ss][Pp][Ii][Dd][Ee][Rr]|[Ss]crape|[Cc][Rr][Aa][Ww][Ll])[A-z0-9]{0,50})(?:(?:[ /]| v)((?:\d+\.)*\d+)|)'
  - regex: '(HbbTV)/((?:\d+\.)*\d+) \('
  - regex: '(Chimera|SeaMonkey|Camino|Waterfox)/((?:\d+\.)*\d+\.?[ab]?\d*[a-z]*)'
  - regex: '(SailfishBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Sailfish Browser'
  - regex: '\[(Pinterest)/[^\]]{1,50}\]'
  - regex: '(Pinterest)(?: for Android(?: Tablet|)|)/((?:\d+\.)*\d+)'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Instagram).((?:\d+\.)*\d+)'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Flipboard).((?:\d+\.)*\d+)'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Flipboard-Briefing).((?:\d+\.)*\d+)'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Onefootball)\/Android.((?:\d+\.)*\d+)'
  - regex: '(Snapchat)\/((?:\d+\.)*\d+)'
  - regex: '(Twitter for (?:iPhone|iPad)|TwitterAndroid)(?:\/((?:\d+\.)*\d+)|)'
    family_replacement: 'Twitter'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Phantom\/ios|Phantom\/android).((?:\d+\.)*\d+)'
    family_replacement: 'Phantom'
  - regex: 'Mozilla.{1,100}Mobile.{1,100}(AspiegelBot|PetalBot)'
    family_replacement: 'Spider'
  - regex: 'AspiegelBot|PetalBot'
    family_replacement: 'Spider'
  - regex: '(Firefox)/((?:\d+\.)*\d+) Basilisk/(\d+)'
    family_replacement: 'Basilisk'
  - regex: '(PaleMoon)/((?:\d+\.)*\d+)'
    family_replacement: 'Pale Moon'
  - regex: '(Fennec)/((?:\d+\.)*\d+\.?[ab]?\d*[a-z]*)'
    family_replacement: 'Firefox Mobile'
  - regex: '(Fennec)/((?:\d+\.)*\d+pre)'
    family_replacement: 'Firefox Mobile'
  - regex: '(Fennec)/((?:\d+\.)*\d+)'
    family_replacement: 'Firefox Mobile'
  - regex: '(?:Mobile|Tablet);.{0,200}(Firefox)/((?:\d+\.)*\d+)'
    family_replacement: 'Firefox Mobile'
  - regex: '(Namoroka|Shiretoko|Minefield)/((?:\d+\.)*\d+(?:pre|))'
    family_replacement: 'Firefox ($1)'
  - regex: '(Firefox)/((?:\d+\.)*\d+a\d+[a-z]*)'
    family_replacement: 'Firefox Alpha'
  - regex: '(Firefox)/((?:\d+\.)*\d+b\d+[a-z]*)'
    family_replacement: 'Firefox Beta'
  - regex: '(Firefox)-(?:\d+\.\d+|)/((?:\d+\.)*\d+a\d+[a-z]*)'
    family_replacement: 'Firefox Alpha'
  - regex: '(Firefox)-(?:\d+\.\d+|)/((?:\d+\.)*\d+b\d+[a-z]*)'
    family_replacement: 'Firefox Beta'
  - regex: '(Namoroka|Shiretoko|Minefield)/((?:\d+\.)*\d+[ab]\d*[a-z]*)'
    family_replacement: 'Firefox ($1)'
  - regex: '(Firefox).{0,200}Tablet browser ((?:\d+\.)*\d+)'
    family_replacement: 'MicroB'
  - regex: '(MozillaDeveloperPreview)/((?:\d+\.)*\d+[ab]\d*[a-z]*)'
  - regex: '(FxiOS)/((?:\d+\.)*\d+)'
    family_replacement: 'Firefox iOS'
  - regex: '(Flock)/((?:\d+\.)*\d+b\d+?)'
  - regex: '(RockMelt)/((?:\d+\.)*\d+)'
  - regex: '(Navigator)/((?:\d+\.)*\d+)'
    family_replacement: 'Netscape'
  - regex: '(Navigator)/((?:\d+\.)*\d+[ab]\d+)'
    family_replacement: 'Netscape'
  - regex: '(Netscape6)/((?:\d+\.)*\d+\.?[ab]?\d*)'
    family_replacement: 'Netscape'
  - regex: '(MyIBrow)/((?:\d+\.)*\d+)'
    family_replacement: 'My Internet Browser'
  - regex: '(UC? ?Browser|UCWEB|U3)[ /]?((?:\d+\.)*\d+)'
    family_replacement: 'UC Browser'
  - regex: '(Opera Tablet).{0,200}Version/((?:\d+\.)*\d+)'
  - regex: '(Opera Mini)(?:/att|)?/?((?:\d+\.)*\d+|)'
  - regex: '(Opera)/.{1,100}Opera Mobi.{1,100}Version/((?:\d+\.)*\d+)'
    family_replacement: 'Opera Mobile'
  - regex: '(Opera)/((?:\d+\.)*\d+).{1,100}Opera Mobi'
    family_replacement: 'Opera Mobile'
  - regex: 'Opera Mobi.{1,100}(Opera)(?:/|\s+)((?:\d+\.)*\d+)'
    family_replacement: 'Opera Mobile'
  - regex: 'Opera Mobi'
    family_replacement: 'Opera Mobile'
  - regex: '(Opera)/9.80.{0,200}Version/((?:\d+\.)*\d+)'
  - regex: '(?:Mobile Safari).{1,300}(OPR)/((?:\d+\.)*\d+)'
    family_replacement: 'Opera Mobile'
  - regex: '(?:Chrome).{1,300}(OPR)/((?:\d+\.)*\d+)'
    family_replacement: 'Opera'
  - regex: '(Coast)/((?:\d+\.)*\d+)'
    family_replacement: 'Opera Coast'
  - regex: '(OPiOS)/((?:\d+\.)*\d+)'
    family_replacement: 'Opera Mini'
  - regex: 'Chrome/.{1,200}( MMS)/((?:\d+\.)*\d+)'
    family_replacement: 'Opera Neon'
  - regex: '(hpw|web)OS/((?:\d+\.)*\d+)'
    family_replacement: 'webOS Browser'
  - regex: '(luakit)'
    family_replacement: 'LuaKit'
  - regex: '(Snowshoe)/((?:\d+\.)*\d+)'
  - regex: 'Gecko/\d+ (Lightning)/((?:\d+\.)*\d+\.?[ab]?\d*[a-z]*)'
  - regex: '(Firefox)/((?:\d+\.)*\d+) \(Swiftfox\)'
    family_replacement: 'Swiftfox'
  - regex: '(rekonq)/((?:\d+\.)*\d+) Safari'
    family_replacement: 'Rekonq'
  - regex: 'rekonq'
    family_replacement: 'Rekonq'
  - regex: '(conkeror|Conkeror)/((?:\d+\.)*\d+)'
    family_replacement: 'Conkeror'
  - regex: '(konqueror)/((?:\d+\.)*\d+)'
    family_replacement: 'Konqueror'
  - regex: '(WeTab)-Browser'
  - regex: '(Comodo_Dragon)/((?:\d+\.)*\d+)'
    family_replacement: 'Comodo Dragon'
  - regex: '(Symphony) ((?:\d+\.)*\d+)'
  - regex: 'PLAYSTATION 3.{1,200}WebKit'
    family_replacement: 'NetFront NX'
  - regex: 'PLAYSTATION 3'
    family_replacement: 'NetFront'
  - regex: '(PlayStation Portable)'
    family_replacement: 'NetFront'
  - regex: '(PlayStation Vita)'
    family_replacement: 'NetFront NX'
  - regex: 'AppleWebKit.{1,200} (NX)/((?:\d+\.)*\d+)'
    family_replacement: 'NetFront NX'
  - regex: '(Nintendo 3DS)'
    family_replacement: 'NetFront NX'
  - regex: '(HuaweiBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Huawei Browser'
  - regex: '(AVG)/((?:\d+\.)*\d+)'
    family_replacement: 'AVG'
  - regex: '(AvastSecureBrowser|Avast)/((?:\d+\.)*\d+)'
    family_replacement: 'Avast Secure Browser'
  - regex: '(Instabridge)/((?:\d+\.)*\d+)'
  - regex: '(AlohaBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Aloha Browser'
  - regex: '((?:B|b)rave(?:\sChrome)?)/((?:\d+\.)*\d+)'
    family_replacement: 'Brave'
  - regex: '(Silk)/((?:\d+\.)*\d+)'
    family_replacement: 'Amazon Silk'
  - regex: '(Puffin)/((?:\d+\.)*\d+)'
  - regex: 'Windows Phone .{0,200}(Edge)/((?:\d+\.)*\d+)'
    family_replacement: 'Edge Mobile'
  - regex: '(EdgiOS|EdgA)/((?:\d+\.)*\d+)'
    family_replacement: 'Edge Mobile'
  - regex: '(OculusBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Oculus Browser'
  - regex: '(SamsungBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Samsung Browser'
  - regex: '(HeyTapBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'HeyTap Browser'
  - regex: '(SznProhlizec)/((?:\d+\.)*\d+)'
    family_replacement: 'Seznam prohlížeč'
  - regex: '(coc_coc_browser)/((?:\d+\.)*\d+)'
    family_replacement: 'Coc Coc'
  - regex: '(baidubrowser)[/\s]((?:\d+\.)*\d+)'
    family_replacement: 'Baidu Browser'
  - regex: '(FlyFlow)/((?:\d+\.)*\d+)'
    family_replacement: 'Baidu Explorer'
  - regex: '(MxBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Maxthon'
  - regex: '(Crosswalk)/((?:\d+\.)*\d+)'
  - regex: '(Line)/((?:\d+\.)*\d+)'
    family_replacement: 'LINE'
  - regex: '(MiuiBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'MiuiBrowser'
  - regex: '(Mint Browser)/((?:\d+\.)*\d+)'
    family_replacement: 'Mint Browser'
  - regex: '(TopBuzz)/((?:\d+\.)*\d+)'
    family_replacement: 'TopBuzz'
  - regex: 'Mozilla.{1,200}Android.{1,200}(GSA)/((?:\d+\.)*\d+)'
    family_replacement: 'Google'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(DuckDuckGo)/((?:\d+\.)*\d+)'
    family_replacement: 'DuckDuckGo Mobile'
  - regex: 'Mozilla.{1,200}(DuckDuckGo)/((?:\d+\.)*\d+)'
    family_replacement: 'DuckDuckGo'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Ddg)/((?:\d+\.)*\d+)'
    family_replacement: 'DuckDuckGo Mobile'
  - regex: 'Mozilla.{1,200}(Ddg)/((?:\d+\.)*\d+)'
    family_replacement: 'DuckDuckGo'
  - regex: '(Tenta/)((?:\d+\.)*\d+)'
    family_replacement: 'Tenta Browser'
  - regex: '(Ecosia) ios@((?:\d+\.)*\d+)'
    family_replacement: 'Ecosia iOS'
  - regex: '(Ecosia) android@((?:\d+\.)*\d+)'
    family_replacement: 'Ecosia Android'
  - regex: '(VivoBrowser)/((?:\d+\.)*\d+)'
  - regex: '(HiBrowser)/v((?:\d+\.)*\d+)'
  - regex: '; wv\).{1,300}(Chrome)/((?:\d+\.)*\d+)'
    family_replacement: 'Chrome Mobile WebView'
  - regex: '(CrMo)/((?:\d+\.)*\d+)'
    family_replacement: 'Chrome Mobile'
  - regex: '(CriOS)/((?:\d+\.)*\d+)'
    family_replacement: 'Chrome Mobile iOS'
  - regex: '(Chrome)/((?:\d+\.)*\d+) Mobile(?:[ /]|$)'
    family_replacement: 'Chrome Mobile'
  - regex: ' Mobile .{1,300}(Chrome)/((?:\d+\.)*\d+)'
    family_replacement: 'Chrome Mobile'
  - regex: '(chromeframe)/((?:\d+\.)*\d+)'
    family_replacement: 'Chrome Frame'
  - regex: '(SLP Browser)/((?:\d+\.)*\d+)'
    family_replacement: 'Tizen Browser'
  - regex: '(SE 2\.X) MetaSr ((?:\d+\.)*\d+)'
    family_replacement: 'Sogou Explorer'
  - regex: '(Rackspace Monitoring)/((?:\d+\.)*\d+)'
    family_replacement: 'RackspaceBot'
  - regex: '(PRTG Network Monitor)'
  - regex: '(PyAMF)/((?:\d+\.)*\d+)'
  - regex: '(YaBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Yandex Browser'
  - regex: '(YaSearchBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Yandex Browser'
  - regex: '(Chrome)/((?:\d+\.)*\d+).{0,100} MRCHROME'
    family_replacement: 'Mail.ru Chromium Browser'
  - regex: '(AOL) ((?:\d+\.)*\d+); AOLBuild (\d+)'
  - regex: '(PodCruncher|Downcast)[ /]?((?:\d+\.)*\d+|)'
  - regex: ' (BoxNotes)/((?:\d+\.)*\d+)'
  - regex: '(Whale)/((?:\d+\.)*\d+) Mobile(?:[ /]|$)'
    family_replacement: 'Whale'
  - regex: '(Whale)/((?:\d+\.)*\d+)'
    family_replacement: 'Whale'
  - regex: '(1Password)/((?:\d+\.)*\d+)'
  - regex: '(Ghost)/((?:\d+\.)*\d+)'
  - regex: 'PAN (GlobalProtect)/((?:\d+\.)*\d+) .{1,100} \(X11; Linux x86_64\)'
  - regex: '^(surveyon)/((?:\d+\.)*\d+)'
    family_replacement: 'Surveyon'
  - regex: '(Slack_SSB)/((?:\d+\.)*\d+)'
    family_replacement: 'Slack Desktop Client'
  - regex: '(HipChat)/?((?:\d+\.)*\d+|)'
    family_replacement: 'HipChat Desktop Client'
  - regex: '\b(MobileIron|FireWeb|Jasmine|ANTGalio|Midori|Fresco|Lobo|PaleMoon|Maxthon|Lynx|OmniWeb|Dillo|Camino|Demeter|Fluid|Fennec|Epiphany|Shiira|Sunrise|Spotify|Flock|Netscape|Lunascape|WebPilot|NetFront|Netfront|Konqueror|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|Opera Mini|iCab|NetNewsWire|ThunderBrowse|Iris|UP\.Browser|Bunjalloo|Google Earth|Raven for Mac|Openwave|MacOutlook|Electron|OktaMobile)/((?:\d+\.)*\d+)'
  - regex: 'Microsoft Office Outlook 12\.\d+\.\d+|MSOffice 12'
    family_replacement: 'Outlook'
    version_replacement: '2007'
  - regex: 'Microsoft Outlook 14\.\d+\.\d+|MSOffice 14'
    family_replacement: 'Outlook'
    version_replacement: '2010'
  - regex: 'Microsoft Outlook 15\.\d+\.\d+'
    family_replacement: 'Outlook'
    version_replacement: '2013'
  - regex: 'Microsoft Outlook (?:Mail )?16\.\d+\.\d+|MSOffice 16'
    family_replacement: 'Outlook'
    version_replacement: '2016'
  - regex: 'Microsoft Office (Word) 2014'
  - regex: 'Outlook-Express\/7\.0'
    family_replacement: 'Windows Live Mail'
  - regex: '(Airmail) ((?:\d+\.)*\d+)'
  - regex: '(Thunderbird)/((?:\d+\.)*\d+(?:pre|))'
    family_replacement: 'Thunderbird'
  - regex: '(Postbox)/((?:\d+\.)*\d+)'
    family_replacement: 'Postbox'
  - regex: '(Barca(?:Pro)?)/((?:\d+\.)*\d+)'
    family_replacement: 'Barca'
  - regex: '(Lotus-Notes)/((?:\d+\.)*\d+)'
    family_replacement: 'Lotus Notes'
  - regex: 'Superhuman'
    family_replacement: 'Superhuman'
  - regex: '(Vivaldi)/((?:\d+\.)*\d+)'
  - regex: '(Edge?)/((?:\d+\.)*\d+)'
    family_replacement: 'Edge'
  - regex: '(Chrome)/((?:\d+\.)*\d+)[\d.]{0,100} Iron[^/]'
    family_replacement: 'Iron'
  - regex: '\b(Dolphin)(?: |HDCN/|/INT\-)((?:\d+\.)*\d+)'
  - regex: '(HeadlessChrome)(?:/((?:\d+\.)*\d+)|)'
  - regex: '(Evolution)/((?:\d+\.)*\d+\.\d+)'
  - regex: '(RCM CardDAV plugin)/((?:\d+\.)*\d+(?:-dev|))'
  - regex: '(bingbot|Bolt|AdobeAIR|Jasmine|IceCat|Skyfire|Midori|Maxthon|Lynx|Arora|IBrowse|Dillo|Camino|Shiira|Fennec|Phoenix|Flock|Netscape|Lunascape|Epiphany|WebPilot|Opera Mini|Opera|NetFront|Netfront|Konqueror|Googlebot|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|iCab|iTunes|MacAppStore|NetNewsWire|Space Bison|Stainless|Orca|Dolfin|BOLT|Minimo|Tizen Browser|Polaris|Abrowser|Planetweb|ICE Browser|mDolphin|qutebrowser|Otter|QupZilla|MailBar|kmail2|YahooMobileMail|ExchangeWebServices|ExchangeServicesClient|Dragon|Outlook-iOS-Android)/((?:\d+\.)*\d+)'
  - regex: '(IEMobile)[ /]((?:\d+\.)*\d+)'
    family_replacement: 'IE Mobile'
  - regex: '(BacaBerita App)\/((?:\d+\.)*\d+)'
  - regex: '^(bPod|Pocket Casts|Player FM)$'
  - regex: '^(AlexaMediaPlayer|VLC)/((?:\d+\.)*\d+)'
  - regex: '^(AntennaPod|WMPlayer|Zune|Podkicker|Radio|ExoPlayerDemo|Overcast|PocketTunes|NSPlayer|okhttp|DoggCatcher|QuickNews|QuickTime|Peapod|Podcasts|GoldenPod|VLC|Spotify|Miro|MediaGo|Juice|iPodder|gPodder|Banshee)/((?:\d+\.)*\d+)'
  - regex: '^(Peapod|Liferea)/([^.\s]+)\.([^.\s]+|)\.?([^.\s]+|)'
  - regex: '^(bPod|Player FM) BMID/(\S+)'
  - regex: '^(Podcast ?Addict)/v((?:\d+\.)*\d+) '
  - regex: '^(Podcast ?Addict) '
    family_replacement: 'PodcastAddict'
  - regex: '(Replay) AV'
  - regex: '(VOX) Music Player'
  - regex: '(CITA) RSS Aggregator/((?:\d+\.)*\d+)'
  - regex: '(Pocket Casts)$'
  - regex: '(Player FM)$'
  - regex: '(LG Player|Doppler|FancyMusic|MediaMonkey|Clementine) ((?:\d+\.)*\d+)'
  - regex: '(philpodder)/((?:\d+\.)*\d+)'
  - regex: '(Player FM|Pocket Casts|DoggCatcher|Spotify|MediaMonkey|MediaGo|BashPodder)'
  - regex: '(QuickTime)\.((?:\d+\.)*\d+)'
  - regex: '(Kinoma)((?:\d+\.)*\d+)'
  - regex: '(Fancy) Cloud Music ((?:\d+\.)*\d+)'
    family_replacement: 'FancyMusic'
  - regex: 'EspnDownloadManager'
    family_replacement: 'ESPN'
  - regex: '(ESPN) Radio ((?:\d+\.)*\d+) ?(?:rv:(\d+)|) '
  - regex: '(podracer|jPodder) v ?((?:\d+\.)*\d+)'
  - regex: '(ZDM)/((?:\d+\.)*\d+)[; ]?'
  - regex: '(Zune|BeyondPod) ((?:\d+\.)*\d+)[\);]'
  - regex: '(WMPlayer)/((?:\d+\.)*\d+)'
  - regex: '^(Lavf)'
    family_replacement: 'WMPlayer'
  - regex: '^(RSSRadio)[ /]?((?:\d+\.)*\d+|)'
  - regex: '(RSS_Radio) ((?:\d+\.)*\d+)'
    family_replacement: 'RSSRadio'
  - regex: '(Podkicker) \S+/((?:\d+\.)*\d+)'
    family_replacement: 'Podkicker'
  - regex: '^(HTC) Streaming Player \S+ / \S+ / \S+ / ((?:\d+\.)*\d+)'
  - regex: '^(Stitcher)/iOS'
  - regex: '^(Stitcher)/Android'
  - regex: '^(VLC) .{0,200}version ((?:\d+\.)*\d+)'
  - regex: ' (VLC) for'
  - regex: '(vlc)/((?:\d+\.)*\d+)'
    family_replacement: 'VLC'
  - regex: '^(foobar)\S{1,10}/((?:\d+\.)*\d+|)'
  - regex: '^(Clementine)\S{1,10} ((?:\d+\.)*\d+|)'
  - regex: '(amarok)/((?:\d+\.)*\d+|)'
    family_replacement: 'Amarok'
  - regex: '(Custom)-Feed Reader'
  - regex: '(iRider|Crazy Browser|SkipStone|iCab|Lunascape|Sleipnir|Maemo Browser) ((?:\d+\.)*\d+)'
  - regex: '(Kindle)/((?:\d+\.)*\d+)'
  - regex: '(Android) Donut'
    version_replacement: '1.2'
  - regex: '(Android) Eclair'
    version_replacement: '2.1'
  - regex: '(Android) Froyo'
    version_replacement: '2.2'
  - regex: '(Android) Gingerbread'
    version_replacement: '2.3'
  - regex: '(Android) Honeycomb'
    version_replacement: '3'
  - regex: '(MSIE) ((?:\d+\.)*\d+).{0,100}XBLWP7'
    family_replacement: 'IE Large Screen'
  - regex: '(Nextcloud)'
  - regex: '(mirall)/((?:\d+\.)*\d+)'
  - regex: '(ownCloud-android)/((?:\d+\.)*\d+)'
    family_replacement: 'Owncloud'
  - regex: '(OC)/((?:\d+\.)*\d+) \(Skype for Business\)'
    family_replacement: 'Skype'
  - regex: '(OpenVAS)(?:-VT)?(?:[ \/]((?:\d+\.)*\d+)|)'
    family_replacement: 'OpenVAS Scanner'
  - regex: '(AnyConnect)\/((?:\d+\.)*\d+|)'
  - regex: 'compatible; monitis'
    family_replacement: 'Monitis'
  - regex: '(Obigo)InternetBrowser'
  - regex: '(Obigo)\-Browser'
  - regex: '(Obigo|OBIGO)[^\d]*((?:\d+\.)*\d+|)'
    family_replacement: 'Obigo'
  - regex: '(MAXTHON|Maxthon) ((?:\d+\.)*\d+)'
    family_replacement: 'Maxthon'
  - regex: '(Maxthon|MyIE2|Uzbl|Shiira)'
    version_replacement: '0'
  - regex: '(BrowseX) \(((?:\d+\.)*\d+)'
  - regex: '(NCSA_Mosaic)/((?:\d+\.)*\d+)'
    family_replacement: 'NCSA Mosaic'
  - regex: '(POLARIS)/((?:\d+\.)*\d+)'
    family_replacement: 'Polaris'
  - regex: '(Embider)/((?:\d+\.)*\d+)'
    family_replacement: 'Polaris'
  - regex: '(BonEcho)/((?:\d+\.)*\d+\.?[ab]?\d*|)'
    family_replacement: 'Bon Echo'
  - regex: '(TopBuzz) com.alex.NewsMaster/((?:\d+\.)*\d+)'
    family_replacement: 'TopBuzz'
  - regex: '(TopBuzz) com.mobilesrepublic.newsrepublic/((?:\d+\.)*\d+)'
    family_replacement: 'TopBuzz'
  - regex: '(TopBuzz) com.topbuzz.videoen/((?:\d+\.)*\d+)'
    family_replacement: 'TopBuzz'
  - regex: '(iPod|iPhone|iPad).{1,200}GSA/((?:\d+\.)*\d+) Mobile'
    family_replacement: 'Google'
  - regex: '(iPod|iPhone|iPad).{1,200}Version/((?:\d+\.)*\d+).{1,200}[ +]Safari'
    family_replacement: 'Mobile Safari'
  - regex: '(iPod|iPod touch|iPhone|iPad);.{0,30}CPU.{0,30}OS[ +](\d+)_(\d+)(?:_(\d+)|).{0,30} AppleNews\/((?:\d+\.)*\d+|)'
    family_replacement: 'Mobile Safari UI/WKWebView'
  - regex: '(iPod|iPhone|iPad).{1,200}Version/((?:\d+\.)*\d+)'
    family_replacement: 'Mobile Safari UI/WKWebView'
  - regex: '(iPod|iPod touch|iPhone|iPad).{0,200} Safari'
    family_replacement: 'Mobile Safari'
  - regex: '(iPod|iPod touch|iPhone|iPad)'
    family_replacement: 'Mobile Safari UI/WKWebView'
  - regex: '(Watch)((?:\d+\.)*\d+),(\d+)'
    family_replacement: 'Apple $1 App'
  - regex: '(Outlook-iOS)/((?:\d+\.)*\d+)\.prod\.iphone \(((?:\d+\.)*\d+)\)'
  - regex: '(AvantGo) ((?:\d+\.)*\d+)'
  - regex: '(OneBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'ONE Browser'
  - regex: '(Avant)'
    version_replacement: '1'
  - regex: '(QtCarBrowser)'
    version_replacement: '1'
  - regex: '^(iBrowser/Mini)((?:\d+\.)*\d+)'
    family_replacement: 'iBrowser Mini'
  - regex: '^(iBrowser|iRAPP)/((?:\d+\.)*\d+)'
  - regex: '^(Nokia)'
    family_replacement: 'Nokia Services (WAP) Browser'
  - regex: '(NokiaBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Nokia Browser'
  - regex: '(BrowserNG)/((?:\d+\.)*\d+)'
    family_replacement: 'Nokia Browser'
  - regex: '(Series60)/5\.0'
    family_replacement: 'Nokia Browser'
    version_replacement: '7.0'
  - regex: '(Series60)/((?:\d+\.)*\d+)'
    family_replacement: 'Nokia OSS Browser'
  - regex: '(S40OviBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Ovi Browser'
  - regex: '(Nokia)[EN]?(\d+)'
  - regex: '(PlayBook).{1,200}RIM Tablet OS ((?:\d+\.)*\d+)'
    family_replacement: 'BlackBerry WebKit'
  - regex: '(Black[bB]erry|BB10).{1,200}Version/((?:\d+\.)*\d+)'
    family_replacement: 'BlackBerry WebKit'
  - regex: '(Black[bB]erry)\s?(\d+)'
    family_replacement: 'BlackBerry'
  - regex: '(OmniWeb)/v((?:\d+\.)*\d+)'
  - regex: '(Blazer)/((?:\d+\.)*\d+)'
    family_replacement: 'Palm Blazer'
  - regex: '(Pre)/((?:\d+\.)*\d+)'
    family_replacement: 'Palm Pre'
  - regex: '(ELinks)/((?:\d+\.)*\d+)'
  - regex: '(ELinks) \(((?:\d+\.)*\d+)'
  - regex: '(Links) \(((?:\d+\.)*\d+)'
  - regex: '(QtWeb) Internet Browser/((?:\d+\.)*\d+)'
  - regex: '(PhantomJS)/((?:\d+\.)*\d+)'
  - regex: '(AppleWebKit)/(\d+)(?:\.(\d+)|)\+ .{0,200} Safari'
    family_replacement: 'WebKit Nightly'
  - regex: '(OLPC)/Update((?:\d+\.)*\d+)'
  - regex: '(OLPC)/Update()\.(\d+)'
    version_replacement: '0'
  - regex: '(SEMC\-Browser)/((?:\d+\.)*\d+)'
  - regex: '(Teleca)'
    family_replacement: 'Teleca Browser'
  - regex: '(Phantom)/V((?:\d+\.)*\d+)'
    family_replacement: 'Phantom Browser'
  - regex: '(Trident)/(7|8)\.(0)'
    family_replacement: 'IE'
    version_replacement: '11'
  - regex: '(Trident)/(6)\.(0)'
    family_replacement: 'IE'
    version_replacement: '10'
  - regex: '(Trident)/(5)\.(0)'
    family_replacement: 'IE'
    version_replacement: '9'
  - regex: '(Trident)/(4)\.(0)'
    family_replacement: 'IE'
    version_replacement: '8'
  - regex: '(Espial)/((?:\d+\.)*\d+)'
  - regex: '(Firefox)/((?:\d+\.)*\d+)$'
  - regex: '(Firefox)/((?:\d+\.)*\d+)(pre|[ab]\d+[a-z]*|)'
  - regex: '([MS]?IE) ((?:\d+\.)*\d+)'
    family_replacement: 'IE'
  - regex: '(python-requests)/((?:\d+\.)*\d+)'
    family_replacement: 'Python Requests'
  - regex: '\b(Windows-Update-Agent|WindowsPowerShell|Microsoft-CryptoAPI|SophosUpdateManager|SophosAgent|Debian APT-HTTP|Ubuntu APT-HTTP|libcurl-agent|libwww-perl|urlgrabber|curl|PycURL|Wget|wget2|aria2|Axel|OpenBSD ftp|lftp|jupdate|insomnia|fetch libfetch|akka-http|got|CloudCockpitBackend|ReactorNetty|axios|Jersey|Vert.x-WebClient|Apache-CXF|Go-CF-client|go-resty|AHC|HTTPie)(?:[ /]((?:\d+\.)*\d+)|)'
  - regex: '^(cf)\/((?:\d+\.)*\d+)'
    family_replacement: 'CloudFoundry'
  - regex: '^(sap-leonardo-iot-sdk-nodejs) \/ ((?:\d+\.)*\d+)'
  - regex: '^(SAP NetWeaver Application Server) \(1.0;(\d{1})(\d{2})\)'
  - regex: '^(\w+-HTTPClient)\/((?:\d+\.)*\d+)-(\S+)'
    family_replacement: 'HTTPClient'
  - regex: '^(go-cli)\s((?:\d+\.)*\d+)'
  - regex: '^(Java-EurekaClient|Java-EurekaClient-Replication|HTTPClient|lua-resty-http)\/v?((?:\d+\.)*\d+)'
  - regex: '^(ping-service|sap xsuaa|Node-oauth|Site24x7|SAP CPI|JAEGER_SECURITY)'
  - regex: '(Python/3\.\d{1,3} aiohttp)/((?:\d+\.)*\d+)'
    family_replacement: 'Python aiohttp'
  - regex: '(Java)[/ ]?\d{1}\.(\d+)\.(\d+)[_-]*([a-zA-Z0-9]+|)'
  - regex: '(Java)[/ ]?((?:\d+\.)*\d+)'
  - regex: '(minio-go)/v((?:\d+\.)*\d+)'
  - regex: '^(ureq)[/ ]((?:\d+\.)*\d+)'
  - regex: '^(http\.rb)/((?:\d+\.)*\d+)'
  - regex: '^(GuzzleHttp)/((?:\d+\.)*\d+)'
  - regex: '^(grab)\b'
  - regex: '^(Cyberduck)/((?:\d+\.)*\d+)(?:\.\d+|)'
  - regex: '^(S3 Browser) ((?:\d+\.)*\d+)(?:\s*https?://s3browser\.com|)'
  - regex: '(S3Gof3r)'
  - regex: '\b(ibm-cos-sdk-(?:core|java|js|python))/((?:\d+\.)*\d+)'
  - regex: '^(rusoto)/((?:\d+\.)*\d+)'
  - regex: '(GeoEvent Server) ((?:\d+\.)*\d+)'
  - regex: '(ArcGIS Pro)(?: ((?:\d+\.)*\d+)|)'
  - regex: 'ArcGIS Client Using WinInet'
    family_replacement: 'ArcMap'
  - regex: '(OperationsDashboard)-(?:Windows)-((?:\d+\.)*\d+)'
    family_replacement: 'Operations Dashboard for ArcGIS'
  - regex: '(arcgisearth)/((?:\d+\.)*\d+)'
    family_replacement: 'ArcGIS Earth'
  - regex: 'com.esri.(earth).phone/((?:\d+\.)*\d+)'
    family_replacement: 'ArcGIS Earth'
  - regex: '(arcgis-explorer)/((?:\d+\.)*\d+)'
    family_replacement: 'Explorer for ArcGIS'
  - regex: 'arcgis-(collector|aurora)/((?:\d+\.)*\d+)'
    family_replacement: 'Collector for ArcGIS'
  - regex: '(arcgis-workforce)/((?:\d+\.)*\d+)'
    family_replacement: 'Workforce for ArcGIS'
  - regex: '(Collector|Explorer|Workforce)-(?:Android|iOS)-((?:\d+\.)*\d+)'
    family_replacement: '$1 for ArcGIS'
  - regex: '(Explorer|Collector)/(\d+) CFNetwork'
    family_replacement: '$1 for ArcGIS'
  - regex: 'ArcGISRuntime-(Android|iOS|NET|Qt)/((?:\d+\.)*\d+)'
    family_replacement: 'ArcGIS Runtime SDK for $1'
  - regex: 'ArcGIS\.?(iOS|Android|NET|Qt)(?:-|\.)(\d+)\.(\d+)(?:\.(\d+)|)'
    family_replacement: 'ArcGIS Runtime SDK for $1'
  - regex: 'ArcGIS\.Runtime\.(Qt)\.((?:\d+\.)*\d+)'
    family_replacement: 'ArcGIS Runtime SDK for $1'
  - regex: '^(Luminary)[Stage]+/(\d+) CFNetwork'
  - regex: '(ESPN)[%20| ]+Radio/((?:\d+\.)*\d+) CFNetwork'
  - regex: '(Antenna)/(\d+) CFNetwork'
    family_replacement: 'AntennaPod'
  - regex: '(TopPodcasts)Pro/(\d+) CFNetwork'
  - regex: '(MusicDownloader)Lite/((?:\d+\.)*\d+) CFNetwork'
  - regex: '^(.{0,200})-iPad\/((?:\d+\.)*\d+) CFNetwork'
  - regex: '^(.{0,200})-iPhone/((?:\d+\.)*\d+) CFNetwork'
  - regex: '^(.{0,200})/((?:\d+\.)*\d+) CFNetwork'
  - regex: '^(Luminary)/((?:\d+\.)*\d+)'
  - regex: '(espn\.go)'
    family_replacement: 'ESPN'
  - regex: '(espnradio\.com)'
    family_replacement: 'ESPN'
  - regex: 'ESPN APP$'
    family_replacement: 'ESPN'
  - regex: '(audioboom\.com)'
    family_replacement: 'AudioBoom'
  - regex: ' (Rivo) RHYTHM'
  - regex: '(CFNetwork)(?:/((?:\d+\.)*\d+)|)'
    family_replacement: 'CFNetwork'
  - regex: '(Pingdom\.com_bot_version_)((?:\d+\.)*\d+)'
    family_replacement: 'PingdomBot'
  - regex: '(PingdomTMS)/((?:\d+\.)*\d+)'
    family_replacement: 'PingdomBot'
  - regex: '(PingdomPageSpeed)/((?:\d+\.)*\d+)'
    family_replacement: 'PingdomBot'
  - regex: ' (PTST)/((?:\d+\.)*\d+)$'
    family_replacement: 'WebPageTest.org bot'
  - regex: 'X11; (Datanyze); Linux'
  - regex: '(NewRelicPinger)/((?:\d+\.)*\d+)'
    family_replacement: 'NewRelicPingerBot'
  - regex: '(Tableau)/((?:\d+\.)*\d+)'
    family_replacement: 'Tableau'
  - regex: 'AppleWebKit/\d{1,10}\.\d{1,10}.{0,200} Safari.{0,200} (CreativeCloud)/((?:\d+\.)*\d+)'
    family_replacement: 'Adobe CreativeCloud'
  - regex: '(Salesforce)(?:.)\/((?:\d+\.)*\d+)'
  - regex: '(\(StatusCake\))'
    family_replacement: 'StatusCakeBot'
  - regex: '(facebookexternalhit)/((?:\d+\.)*\d+)'
    family_replacement: 'FacebookBot'
  - regex: 'Google.{0,50}/\+/web/snippet'
    family_replacement: 'GooglePlusBot'
  - regex: 'via ggpht\.com GoogleImageProxy'
    family_replacement: 'GmailImageProxy'
  - regex: 'YahooMailProxy; https://help\.yahoo\.com/kb/yahoo-mail-proxy-SLN28749\.html'
    family_replacement: 'YahooMailProxy'
  - regex: '(Twitterbot)/((?:\d+\.)*\d+)'
    family_replacement: 'Twitterbot'
  - regex: '/((?:Ant-|)Nutch|[A-z]+[Bb]ot|[A-z]+[Ss]pider|Axtaris|fetchurl|Isara|ShopSalad|Tailsweep)[ \-]((?:\d+\.)*\d+)'
  - regex: '\b(008|Altresium|Argus|BaiduMobaider|BoardReader|DNSGroup|DataparkSearch|EDI|Goodzer|Grub|INGRID|Infohelfer|LinkedInBot|LOOQ|Nutch|OgScrper|Pandora|PathDefender|Peew|PostPost|Steeler|Twitterbot|VSE|WebCrunch|WebZIP|Y!J-BR[A-Z]|YahooSeeker|envolk|sproose|wminer)/((?:\d+\.)*\d+)'
  - regex: '(MSIE) ((?:\d+\.)*\d+)([a-z]\d|[a-z]|);.{0,200} MSIECrawler'
    family_replacement: 'MSIECrawler'
  - regex: '(DAVdroid)/((?:\d+\.)*\d+)'
  - regex: '(Google-HTTP-Java-Client|Apache-HttpClient|PostmanRuntime|Go-http-client|scalaj-http|http%20client|Python-urllib|HttpMonitor|TLSProber|WinHTTP|JNLP|okhttp|aihttp|reqwest|axios|unirest-(?:java|python|ruby|nodejs|php|net))(?:[ /]((?:\d+\.)*\d+)|)'
  - regex: '(Pinterest(?:bot|))/((?:\d+\.)*\d+)[;\s(]+\+https://www.pinterest.com/bot.html'
    family_replacement: 'Pinterestbot'
  - regex: '(CSimpleSpider|Cityreview Robot|CrawlDaddy|CrawlFire|Finderbots|Index crawler|Job Roboter|KiwiStatus Spider|Lijit Crawler|QuerySeekerSpider|ScollSpider|Trends Crawler|USyd-NLP-Spider|SiteCat Webbot|BotName\/\$BotVersion|123metaspider-Bot|1470\.net crawler|50\.nu|8bo Crawler Bot|Aboundex|Accoona-[A-z]{1,30}-Agent|AdsBot-Google(?:-[a-z]{1,30}|)|altavista|AppEngine-Google|archive.{0,30}\.org_bot|archiver|Ask Jeeves|[Bb]ai[Dd]u[Ss]pider(?:-[A-Za-z]{1,30})(?:-[A-Za-z]{1,30}|)|bingbot|BingPreview|blitzbot|BlogBridge|Bloglovin|BoardReader Blog Indexer|BoardReader Favicon Fetcher|boitho.com-dc|BotSeer|BUbiNG|\b\w{0,30}favicon\w{0,30}\b|\bYeti(?:-[a-z]{1,30}|)|Catchpoint(?: bot|)|[Cc]harlotte|Checklinks|clumboot|Comodo HTTP\(S\) Crawler|Comodo-Webinspector-Crawler|ConveraCrawler|CRAWL-E|CrawlConvera|Daumoa(?:-feedfetcher|)|Feed Seeker Bot|Feedbin|findlinks|Flamingo_SearchEngine|FollowSite Bot|furlbot|Genieo|gigabot|GomezAgent|gonzo1|(?:[a-zA-Z]{1,30}-|)Googlebot(?:-[a-zA-Z]{1,30}|)|Google SketchUp|grub-client|gsa-crawler|heritrix|HiddenMarket|holmes|HooWWWer|htdig|ia_archiver|ICC-Crawler|Icarus6j|ichiro(?:/mobile|)|IconSurf|IlTrovatore(?:-Setaccio|)|InfuzApp|Innovazion Crawler|InternetArchive|IP2[a-z]{1,30}Bot|jbot\b|KaloogaBot|Kraken|Kurzor|larbin|LEIA|LesnikBot|Linguee Bot|LinkAider|LinkedInBot|Lite Bot|Llaut|lycos|Mail\.RU_Bot|masscan|masidani_bot|Mediapartners-Google|Microsoft .{0,30} Bot|mogimogi|mozDex|MJ12bot|msnbot(?:-media {0,2}|)|msrbot|Mtps Feed Aggregation System|netresearch|Netvibes|NewsGator[^/]{0,30}|^NING|Nutch[^/]{0,30}|Nymesis|ObjectsSearch|OgScrper|Orbiter|OOZBOT|PagePeeker|PagesInventory|PaxleFramework|Peeplo Screenshot Bot|PHPCrawl|PlantyNet_WebRobot|Pompos|Qwantify|Read%20Later|Reaper|RedCarpet|Retreiver|Riddler|Rival IQ|scooter|Scrapy|Scrubby|searchsight|seekbot|semanticdiscovery|SemrushBot|Simpy|SimplePie|SEOstats|SimpleRSS|SiteCon|Slackbot-LinkExpanding|Slack-ImgProxy|Slurp|snappy|Speedy Spider|Squrl Java|Stringer|TheUsefulbot|ThumbShotsBot|Thumbshots\.ru|Tiny Tiny RSS|Twitterbot|WhatsApp|URL2PNG|Vagabondo|VoilaBot|^vortex|Votay bot|^voyager|WASALive.Bot|Web-sniffer|WebThumb|WeSEE:[A-z]{1,30}|WhatWeb|WIRE|WordPress|Wotbox|www\.almaden\.ibm\.com|Xenu(?:.s|) Link Sleuth|Xerka [A-z]{1,30}Bot|yacy(?:bot|)|YahooSeeker|Yahoo! Slurp|Yandex\w{1,30}|YodaoBot(?:-[A-z]{1,30}|)|YottaaMonitor|Yowedo|^Zao|^Zao-Crawler|ZeBot_www\.ze\.bz|ZooShot|ZyBorg|ArcGIS Hub Indexer)(?:[ /]v?((?:\d+\.)*\d+)|)'
  - regex: '\b(Boto3?|JetS3t|aws-(?:cli|sdk-(?:cpp|go|go-v\d|java|nodejs|ruby2?|dotnet-(?:\d{1,2}|core)))|s3fs)/((?:\d+\.)*\d+)'
  - regex: '(FME)\/((?:\d+\.)*\d+)'
  - regex: '(QGIS)\/((?:\d+\.)*\d+)'
  - regex: '(JOSM)/((?:\d+\.)*\d+)'
  - regex: '(Tygron Platform) \(((?:\d+\.)*\d+(?: RC \d+\.\d+)?)\)'
  - regex: '\[(FBAN/MessengerForiOS|FB_IAB/MESSENGER);FBAV/((?:\d+\.)*\d+)'
    family_replacement: 'Facebook Messenger'
  - regex: '\[FB.{0,300};(FBAV)/((?:\d+\.)*\d+)'
    family_replacement: 'Facebook'
  - regex: '\[FB.{0,300};'
    family_replacement: 'Facebook'
  - regex: '(RecipeRadar)/((?:\d+\.)*\d+)'
  - regex: '^.{0,200}?(?:\/[A-Za-z0-9\.]{0,50}|) {0,2}([A-Za-z0-9 \-_\!\[\]:]{0,50}(?:[Aa]rchiver|[Ii]ndexer|[Ss]craper|[Bb]ot|[Ss]pider|[Cc]rawl[a-z]{0,50}))[/ ]((?:\d+\.)*\d+)'
  - regex: '^.{0,200}?((?:[A-Za-z][A-Za-z0-9 -]{0,50}|)[^C][^Uu][Bb]ot)\b(?:(?:[ /]| v)((?:\d+\.)*\d+)|)'
  - regex: '^.{0,200}?((?:[A-z0-9]{1,50}|[A-z\-]{1,50} ?|)(?: the |)(?:[Ss][Pp][Ii][Dd][Ee][Rr]|[Ss]crape|[Cc][Rr][Aa][Ww][Ll])[A-z0-9]{0,50})(?:(?:[ /]| v)((?:\d+\.)*\d+)|)'
  - regex: '(HbbTV)/((?:\d+\.)*\d+) \('
  - regex: '(Chimera|SeaMonkey|Camino|Waterfox)/((?:\d+\.)*\d+\.?[ab]?\d*[a-z]*)'
  - regex: '(SailfishBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Sailfish Browser'
  - regex: '\[(Pinterest)/[^\]]{1,50}\]'
  - regex: '(Pinterest)(?: for Android(?: Tablet|)|)/((?:\d+\.)*\d+)'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Instagram).((?:\d+\.)*\d+)'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Flipboard).((?:\d+\.)*\d+)'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Flipboard-Briefing).((?:\d+\.)*\d+)'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Onefootball)\/Android.((?:\d+\.)*\d+)'
  - regex: '(Snapchat)\/((?:\d+\.)*\d+)'
  - regex: '(Twitter for (?:iPhone|iPad)|TwitterAndroid)(?:\/((?:\d+\.)*\d+)|)'
    family_replacement: 'Twitter'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Phantom\/ios|Phantom\/android).((?:\d+\.)*\d+)'
    family_replacement: 'Phantom'
  - regex: 'Mozilla.{1,100}Mobile.{1,100}(AspiegelBot|PetalBot)'
    family_replacement: 'Spider'
  - regex: 'AspiegelBot|PetalBot'
    family_replacement: 'Spider'
  - regex: '(Firefox)/((?:\d+\.)*\d+) Basilisk/(\d+)'
    family_replacement: 'Basilisk'
  - regex: '(PaleMoon)/((?:\d+\.)*\d+)'
    family_replacement: 'Pale Moon'
  - regex: '(Fennec)/((?:\d+\.)*\d+\.?[ab]?\d*[a-z]*)'
    family_replacement: 'Firefox Mobile'
  - regex: '(Fennec)/((?:\d+\.)*\d+pre)'
    family_replacement: 'Firefox Mobile'
  - regex: '(Fennec)/((?:\d+\.)*\d+)'
    family_replacement: 'Firefox Mobile'
  - regex: '(?:Mobile|Tablet);.{0,200}(Firefox)/((?:\d+\.)*\d+)'
    family_replacement: 'Firefox Mobile'
  - regex: '(Namoroka|Shiretoko|Minefield)/((?:\d+\.)*\d+(?:pre|))'
    family_replacement: 'Firefox ($1)'
  - regex: '(Firefox)/((?:\d+\.)*\d+a\d+[a-z]*)'
    family_replacement: 'Firefox Alpha'
  - regex: '(Firefox)/((?:\d+\.)*\d+b\d+[a-z]*)'
    family_replacement: 'Firefox Beta'
  - regex: '(Firefox)-(?:\d+\.\d+|)/((?:\d+\.)*\d+a\d+[a-z]*)'
    family_replacement: 'Firefox Alpha'
  - regex: '(Firefox)-(?:\d+\.\d+|)/((?:\d+\.)*\d+b\d+[a-z]*)'
    family_replacement: 'Firefox Beta'
  - regex: '(Namoroka|Shiretoko|Minefield)/((?:\d+\.)*\d+[ab]\d*[a-z]*)'
    family_replacement: 'Firefox ($1)'
  - regex: '(Firefox).{0,200}Tablet browser ((?:\d+\.)*\d+)'
    family_replacement: 'MicroB'
  - regex: '(MozillaDeveloperPreview)/((?:\d+\.)*\d+[ab]\d*[a-z]*)'
  - regex: '(FxiOS)/((?:\d+\.)*\d+)'
    family_replacement: 'Firefox iOS'
  - regex: '(Flock)/((?:\d+\.)*\d+b\d+?)'
  - regex: '(RockMelt)/((?:\d+\.)*\d+)'
  - regex: '(Navigator)/((?:\d+\.)*\d+)'
    family_replacement: 'Netscape'
  - regex: '(Navigator)/((?:\d+\.)*\d+[ab]\d+)'
    family_replacement: 'Netscape'
  - regex: '(Netscape6)/((?:\d+\.)*\d+\.?[ab]?\d*)'
    family_replacement: 'Netscape'
  - regex: '(MyIBrow)/((?:\d+\.)*\d+)'
    family_replacement: 'My Internet Browser'
  - regex: '(UC? ?Browser|UCWEB|U3)[ /]?((?:\d+\.)*\d+)'
    family_replacement: 'UC Browser'
  - regex: '(Opera Tablet).{0,200}Version/((?:\d+\.)*\d+)'
  - regex: '(Opera Mini)(?:/att|)?/?((?:\d+\.)*\d+|)'
  - regex: '(Opera)/.{1,100}Opera Mobi.{1,100}Version/((?:\d+\.)*\d+)'
    family_replacement: 'Opera Mobile'
  - regex: '(Opera)/((?:\d+\.)*\d+).{1,100}Opera Mobi'
    family_replacement: 'Opera Mobile'
  - regex: 'Opera Mobi.{1,100}(Opera)(?:/|\s+)((?:\d+\.)*\d+)'
    family_replacement: 'Opera Mobile'
  - regex: 'Opera Mobi'
    family_replacement: 'Opera Mobile'
  - regex: '(Opera)/9.80.{0,200}Version/((?:\d+\.)*\d+)'
  - regex: '(?:Mobile Safari).{1,300}(OPR)/((?:\d+\.)*\d+)'
    family_replacement: 'Opera Mobile'
  - regex: '(?:Chrome).{1,300}(OPR)/((?:\d+\.)*\d+)'
    family_replacement: 'Opera'
  - regex: '(Coast)/((?:\d+\.)*\d+)'
    family_replacement: 'Opera Coast'
  - regex: '(OPiOS)/((?:\d+\.)*\d+)'
    family_replacement: 'Opera Mini'
  - regex: 'Chrome/.{1,200}( MMS)/((?:\d+\.)*\d+)'
    family_replacement: 'Opera Neon'
  - regex: '(hpw|web)OS/((?:\d+\.)*\d+)'
    family_replacement: 'webOS Browser'
  - regex: '(luakit)'
    family_replacement: 'LuaKit'
  - regex: '(Snowshoe)/((?:\d+\.)*\d+)'
  - regex: 'Gecko/\d+ (Lightning)/((?:\d+\.)*\d+\.?[ab]?\d*[a-z]*)'
  - regex: '(Firefox)/((?:\d+\.)*\d+) \(Swiftfox\)'
    family_replacement: 'Swiftfox'
  - regex: '(rekonq)/((?:\d+\.)*\d+) Safari'
    family_replacement: 'Rekonq'
  - regex: 'rekonq'
    family_replacement: 'Rekonq'
  - regex: '(conkeror|Conkeror)/((?:\d+\.)*\d+)'
    family_replacement: 'Conkeror'
  - regex: '(konqueror)/((?:\d+\.)*\d+)'
    family_replacement: 'Konqueror'
  - regex: '(WeTab)-Browser'
  - regex: '(Comodo_Dragon)/((?:\d+\.)*\d+)'
    family_replacement: 'Comodo Dragon'
  - regex: '(Symphony) ((?:\d+\.)*\d+)'
  - regex: 'PLAYSTATION 3.{1,200}WebKit'
    family_replacement: 'NetFront NX'
  - regex: 'PLAYSTATION 3'
    family_replacement: 'NetFront'
  - regex: '(PlayStation Portable)'
    family_replacement: 'NetFront'
  - regex: '(PlayStation Vita)'
    family_replacement: 'NetFront NX'
  - regex: 'AppleWebKit.{1,200} (NX)/((?:\d+\.)*\d+)'
    family_replacement: 'NetFront NX'
  - regex: '(Nintendo 3DS)'
    family_replacement: 'NetFront NX'
  - regex: '(HuaweiBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Huawei Browser'
  - regex: '(AVG)/((?:\d+\.)*\d+)'
    family_replacement: 'AVG'
  - regex: '(AvastSecureBrowser|Avast)/((?:\d+\.)*\d+)'
    family_replacement: 'Avast Secure Browser'
  - regex: '(Instabridge)/((?:\d+\.)*\d+)'
  - regex: '(AlohaBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Aloha Browser'
  - regex: '((?:B|b)rave(?:\sChrome)?)/((?:\d+\.)*\d+)'
    family_replacement: 'Brave'
  - regex: '(Silk)/((?:\d+\.)*\d+)'
    family_replacement: 'Amazon Silk'
  - regex: '(Puffin)/((?:\d+\.)*\d+)'
  - regex: 'Windows Phone .{0,200}(Edge)/((?:\d+\.)*\d+)'
    family_replacement: 'Edge Mobile'
  - regex: '(EdgiOS|EdgA)/((?:\d+\.)*\d+)'
    family_replacement: 'Edge Mobile'
  - regex: '(OculusBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Oculus Browser'
  - regex: '(SamsungBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Samsung Browser'
  - regex: '(HeyTapBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'HeyTap Browser'
  - regex: '(SznProhlizec)/((?:\d+\.)*\d+)'
    family_replacement: 'Seznam prohlížeč'
  - regex: '(coc_coc_browser)/((?:\d+\.)*\d+)'
    family_replacement: 'Coc Coc'
  - regex: '(baidubrowser)[/\s]((?:\d+\.)*\d+)'
    family_replacement: 'Baidu Browser'
  - regex: '(FlyFlow)/((?:\d+\.)*\d+)'
    family_replacement: 'Baidu Explorer'
  - regex: '(MxBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Maxthon'
  - regex: '(Crosswalk)/((?:\d+\.)*\d+)'
  - regex: '(Line)/((?:\d+\.)*\d+)'
    family_replacement: 'LINE'
  - regex: '(MiuiBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'MiuiBrowser'
  - regex: '(Mint Browser)/((?:\d+\.)*\d+)'
    family_replacement: 'Mint Browser'
  - regex: '(TopBuzz)/((?:\d+\.)*\d+)'
    family_replacement: 'TopBuzz'
  - regex: 'Mozilla.{1,200}Android.{1,200}(GSA)/((?:\d+\.)*\d+)'
    family_replacement: 'Google'
  - regex: '(MQQBrowser/Mini)(?:(?:(?:\d+\.)*\d+)|)'
    family_replacement: 'QQ Browser'
  - regex: '(MQQBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'QQ Browser'
  - regex: '(QQBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'QQ Browser'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(DuckDuckGo)/((?:\d+\.)*\d+)'
    family_replacement: 'DuckDuckGo Mobile'
  - regex: 'Mozilla.{1,200}(DuckDuckGo)/((?:\d+\.)*\d+)'
    family_replacement: 'DuckDuckGo'
  - regex: 'Mozilla.{1,200}Mobile.{1,100}(Ddg)/((?:\d+\.)*\d+)'
    family_replacement: 'DuckDuckGo Mobile'
  - regex: 'Mozilla.{1,200}(Ddg)/((?:\d+\.)*\d+)'
    family_replacement: 'DuckDuckGo'
  - regex: '(Tenta/)((?:\d+\.)*\d+)'
    family_replacement: 'Tenta Browser'
  - regex: '(Ecosia) ios@((?:\d+\.)*\d+)'
    family_replacement: 'Ecosia iOS'
  - regex: '(Ecosia) android@((?:\d+\.)*\d+)'
    family_replacement: 'Ecosia Android'
  - regex: '(VivoBrowser)/((?:\d+\.)*\d+)'
  - regex: '(HiBrowser)/v((?:\d+\.)*\d+)'
  - regex: 'Version/.{1,300}(Chrome)/((?:\d+\.)*\d+)'
    family_replacement: 'Chrome Mobile WebView'
  - regex: '; wv\).{1,300}(Chrome)/((?:\d+\.)*\d+)'
    family_replacement: 'Chrome Mobile WebView'
  - regex: '(CrMo)/((?:\d+\.)*\d+)'
    family_replacement: 'Chrome Mobile'
  - regex: '(CriOS)/((?:\d+\.)*\d+)'
    family_replacement: 'Chrome Mobile iOS'
  - regex: '(Chrome)/((?:\d+\.)*\d+) Mobile(?:[ /]|$)'
    family_replacement: 'Chrome Mobile'
  - regex: ' Mobile .{1,300}(Chrome)/((?:\d+\.)*\d+)'
    family_replacement: 'Chrome Mobile'
  - regex: '(chromeframe)/((?:\d+\.)*\d+)'
    family_replacement: 'Chrome Frame'
  - regex: '(SLP Browser)/((?:\d+\.)*\d+)'
    family_replacement: 'Tizen Browser'
  - regex: '(SE 2\.X) MetaSr ((?:\d+\.)*\d+)'
    family_replacement: 'Sogou Explorer'
  - regex: '(Rackspace Monitoring)/((?:\d+\.)*\d+)'
    family_replacement: 'RackspaceBot'
  - regex: '(PRTG Network Monitor)'
  - regex: '(PyAMF)/((?:\d+\.)*\d+)'
  - regex: '(YaBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Yandex Browser'
  - regex: '(YaSearchBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Yandex Browser'
  - regex: '(Chrome)/((?:\d+\.)*\d+).{0,100} MRCHROME'
    family_replacement: 'Mail.ru Chromium Browser'
  - regex: '(AOL) ((?:\d+\.)*\d+); AOLBuild (\d+)'
  - regex: '(PodCruncher|Downcast)[ /]?((?:\d+\.)*\d+|)'
  - regex: ' (BoxNotes)/((?:\d+\.)*\d+)'
  - regex: '(Whale)/((?:\d+\.)*\d+) Mobile(?:[ /]|$)'
    family_replacement: 'Whale'
  - regex: '(Whale)/((?:\d+\.)*\d+)'
    family_replacement: 'Whale'
  - regex: '(1Password)/((?:\d+\.)*\d+)'
  - regex: '(Ghost)/((?:\d+\.)*\d+)'
  - regex: 'PAN (GlobalProtect)/((?:\d+\.)*\d+) .{1,100} \(X11; Linux x86_64\)'
  - regex: '^(surveyon)/((?:\d+\.)*\d+)'
    family_replacement: 'Surveyon'
  - regex: '(Slack_SSB)/((?:\d+\.)*\d+)'
    family_replacement: 'Slack Desktop Client'
  - regex: '(HipChat)/?((?:\d+\.)*\d+|)'
    family_replacement: 'HipChat Desktop Client'
  - regex: '\b(MobileIron|FireWeb|Jasmine|ANTGalio|Midori|Fresco|Lobo|PaleMoon|Maxthon|Lynx|OmniWeb|Dillo|Camino|Demeter|Fluid|Fennec|Epiphany|Shiira|Sunrise|Spotify|Flock|Netscape|Lunascape|WebPilot|NetFront|Netfront|Konqueror|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|Opera Mini|iCab|NetNewsWire|ThunderBrowse|Iris|UP\.Browser|Bunjalloo|Google Earth|Raven for Mac|Openwave|MacOutlook|Electron|OktaMobile)/((?:\d+\.)*\d+)'
  - regex: 'Microsoft Office Outlook 12\.\d+\.\d+|MSOffice 12'
    family_replacement: 'Outlook'
    version_replacement: '2007'
  - regex: 'Microsoft Outlook 14\.\d+\.\d+|MSOffice 14'
    family_replacement: 'Outlook'
    version_replacement: '2010'
  - regex: 'Microsoft Outlook 15\.\d+\.\d+'
    family_replacement: 'Outlook'
    version_replacement: '2013'
  - regex: 'Microsoft Outlook (?:Mail )?16\.\d+\.\d+|MSOffice 16'
    family_replacement: 'Outlook'
    version_replacement: '2016'
  - regex: 'Microsoft Office (Word) 2014'
  - regex: 'Outlook-Express\/7\.0'
    family_replacement: 'Windows Live Mail'
  - regex: '(Airmail) ((?:\d+\.)*\d+)'
  - regex: '(Thunderbird)/((?:\d+\.)*\d+(?:pre|))'
    family_replacement: 'Thunderbird'
  - regex: '(Postbox)/((?:\d+\.)*\d+)'
    family_replacement: 'Postbox'
  - regex: '(Barca(?:Pro)?)/((?:\d+\.)*\d+)'
    family_replacement: 'Barca'
  - regex: '(Lotus-Notes)/((?:\d+\.)*\d+)'
    family_replacement: 'Lotus Notes'
  - regex: 'Superhuman'
    family_replacement: 'Superhuman'
  - regex: '(Vivaldi)/((?:\d+\.)*\d+)'
  - regex: '(Edge?)/((?:\d+\.)*\d+)'
    family_replacement: 'Edge'
  - regex: '(Chrome)/((?:\d+\.)*\d+)[\d.]{0,100} Iron[^/]'
    family_replacement: 'Iron'
  - regex: '\b(Dolphin)(?: |HDCN/|/INT\-)((?:\d+\.)*\d+)'
  - regex: '(HeadlessChrome)(?:/((?:\d+\.)*\d+)|)'
  - regex: '(Evolution)/((?:\d+\.)*\d+\.\d+)'
  - regex: '(RCM CardDAV plugin)/((?:\d+\.)*\d+(?:-dev|))'
  - regex: '(bingbot|Bolt|AdobeAIR|Jasmine|IceCat|Skyfire|Midori|Maxthon|Lynx|Arora|IBrowse|Dillo|Camino|Shiira|Fennec|Phoenix|Flock|Netscape|Lunascape|Epiphany|WebPilot|Opera Mini|Opera|NetFront|Netfront|Konqueror|Googlebot|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|iCab|iTunes|MacAppStore|NetNewsWire|Space Bison|Stainless|Orca|Dolfin|BOLT|Minimo|Tizen Browser|Polaris|Abrowser|Planetweb|ICE Browser|mDolphin|qutebrowser|Otter|QupZilla|MailBar|kmail2|YahooMobileMail|ExchangeWebServices|ExchangeServicesClient|Dragon|Outlook-iOS-Android)/((?:\d+\.)*\d+)'
  - regex: '(Chromium|Chrome)/((?:\d+\.)*\d+)'
  - regex: '(IEMobile)[ /]((?:\d+\.)*\d+)'
    family_replacement: 'IE Mobile'
  - regex: '(BacaBerita App)\/((?:\d+\.)*\d+)'
  - regex: '^(bPod|Pocket Casts|Player FM)$'
  - regex: '^(AlexaMediaPlayer|VLC)/((?:\d+\.)*\d+)'
  - regex: '^(AntennaPod|WMPlayer|Zune|Podkicker|Radio|ExoPlayerDemo|Overcast|PocketTunes|NSPlayer|okhttp|DoggCatcher|QuickNews|QuickTime|Peapod|Podcasts|GoldenPod|VLC|Spotify|Miro|MediaGo|Juice|iPodder|gPodder|Banshee)/((?:\d+\.)*\d+)'
  - regex: '^(Peapod|Liferea)/([^.\s]+)\.([^.\s]+|)\.?([^.\s]+|)'
  - regex: '^(bPod|Player FM) BMID/(\S+)'
  - regex: '^(Podcast ?Addict)/v((?:\d+\.)*\d+) '
  - regex: '^(Podcast ?Addict) '
    family_replacement: 'PodcastAddict'
  - regex: '(Replay) AV'
  - regex: '(VOX) Music Player'
  - regex: '(CITA) RSS Aggregator/((?:\d+\.)*\d+)'
  - regex: '(Pocket Casts)$'
  - regex: '(Player FM)$'
  - regex: '(LG Player|Doppler|FancyMusic|MediaMonkey|Clementine) ((?:\d+\.)*\d+)'
  - regex: '(philpodder)/((?:\d+\.)*\d+)'
  - regex: '(Player FM|Pocket Casts|DoggCatcher|Spotify|MediaMonkey|MediaGo|BashPodder)'
  - regex: '(QuickTime)\.((?:\d+\.)*\d+)'
  - regex: '(Kinoma)((?:\d+\.)*\d+)'
  - regex: '(Fancy) Cloud Music ((?:\d+\.)*\d+)'
    family_replacement: 'FancyMusic'
  - regex: 'EspnDownloadManager'
    family_replacement: 'ESPN'
  - regex: '(ESPN) Radio ((?:\d+\.)*\d+) ?(?:rv:(\d+)|) '
  - regex: '(podracer|jPodder) v ?((?:\d+\.)*\d+)'
  - regex: '(ZDM)/((?:\d+\.)*\d+)[; ]?'
  - regex: '(Zune|BeyondPod) ((?:\d+\.)*\d+)[\);]'
  - regex: '(WMPlayer)/((?:\d+\.)*\d+)'
  - regex: '^(Lavf)'
    family_replacement: 'WMPlayer'
  - regex: '^(RSSRadio)[ /]?((?:\d+\.)*\d+|)'
  - regex: '(RSS_Radio) ((?:\d+\.)*\d+)'
    family_replacement: 'RSSRadio'
  - regex: '(Podkicker) \S+/((?:\d+\.)*\d+)'
    family_replacement: 'Podkicker'
  - regex: '^(HTC) Streaming Player \S+ / \S+ / \S+ / ((?:\d+\.)*\d+)'
  - regex: '^(Stitcher)/iOS'
  - regex: '^(Stitcher)/Android'
  - regex: '^(VLC) .{0,200}version ((?:\d+\.)*\d+)'
  - regex: ' (VLC) for'
  - regex: '(vlc)/((?:\d+\.)*\d+)'
    family_replacement: 'VLC'
  - regex: '^(foobar)\S{1,10}/((?:\d+\.)*\d+|)'
  - regex: '^(Clementine)\S{1,10} ((?:\d+\.)*\d+|)'
  - regex: '(amarok)/((?:\d+\.)*\d+|)'
    family_replacement: 'Amarok'
  - regex: '(Custom)-Feed Reader'
  - regex: '(iRider|Crazy Browser|SkipStone|iCab|Lunascape|Sleipnir|Maemo Browser) ((?:\d+\.)*\d+)'
  - regex: '(iCab|Lunascape|Opera|Android|Jasmine|Polaris|Microsoft SkyDriveSync|The Bat!) ((?:\d+\.)*\d+|)'
  - regex: '(Kindle)/((?:\d+\.)*\d+)'
  - regex: '(Android) Donut'
    version_replacement: '1.2'
  - regex: '(Android) Eclair'
    version_replacement: '2.1'
  - regex: '(Android) Froyo'
    version_replacement: '2.2'
  - regex: '(Android) Gingerbread'
    version_replacement: '2.3'
  - regex: '(Android) Honeycomb'
    version_replacement: '3'
  - regex: '(MSIE) ((?:\d+\.)*\d+).{0,100}XBLWP7'
    family_replacement: 'IE Large Screen'
  - regex: '(Nextcloud)'
  - regex: '(mirall)/((?:\d+\.)*\d+)'
  - regex: '(ownCloud-android)/((?:\d+\.)*\d+)'
    family_replacement: 'Owncloud'
  - regex: '(OC)/((?:\d+\.)*\d+) \(Skype for Business\)'
    family_replacement: 'Skype'
  - regex: '(OpenVAS)(?:-VT)?(?:[ \/]((?:\d+\.)*\d+)|)'
    family_replacement: 'OpenVAS Scanner'
  - regex: '(AnyConnect)\/((?:\d+\.)*\d+|)'
  - regex: 'compatible; monitis'
    family_replacement: 'Monitis'
  - regex: '(Obigo)InternetBrowser'
  - regex: '(Obigo)\-Browser'
  - regex: '(Obigo|OBIGO)[^\d]*((?:\d+\.)*\d+|)'
    family_replacement: 'Obigo'
  - regex: '(MAXTHON|Maxthon) ((?:\d+\.)*\d+)'
    family_replacement: 'Maxthon'
  - regex: '(Maxthon|MyIE2|Uzbl|Shiira)'
    version_replacement: '0'
  - regex: '(BrowseX) \(((?:\d+\.)*\d+)'
  - regex: '(NCSA_Mosaic)/((?:\d+\.)*\d+)'
    family_replacement: 'NCSA Mosaic'
  - regex: '(POLARIS)/((?:\d+\.)*\d+)'
    family_replacement: 'Polaris'
  - regex: '(Embider)/((?:\d+\.)*\d+)'
    family_replacement: 'Polaris'
  - regex: '(BonEcho)/((?:\d+\.)*\d+\.?[ab]?\d*|)'
    family_replacement: 'Bon Echo'
  - regex: '(TopBuzz) com.alex.NewsMaster/((?:\d+\.)*\d+)'
    family_replacement: 'TopBuzz'
  - regex: '(TopBuzz) com.mobilesrepublic.newsrepublic/((?:\d+\.)*\d+)'
    family_replacement: 'TopBuzz'
  - regex: '(TopBuzz) com.topbuzz.videoen/((?:\d+\.)*\d+)'
    family_replacement: 'TopBuzz'
  - regex: '(iPod|iPhone|iPad).{1,200}GSA/((?:\d+\.)*\d+) Mobile'
    family_replacement: 'Google'
  - regex: '(iPod|iPhone|iPad).{1,200}Version/((?:\d+\.)*\d+).{1,200}[ +]Safari'
    family_replacement: 'Mobile Safari'
  - regex: '(iPod|iPod touch|iPhone|iPad);.{0,30}CPU.{0,30}OS[ +](\d+)_(\d+)(?:_(\d+)|).{0,30} AppleNews\/((?:\d+\.)*\d+|)'
    family_replacement: 'Mobile Safari UI/WKWebView'
  - regex: '(iPod|iPhone|iPad).{1,200}Version/((?:\d+\.)*\d+)'
    family_replacement: 'Mobile Safari UI/WKWebView'
  - regex: '(iPod|iPod touch|iPhone|iPad).{0,200} Safari'
    family_replacement: 'Mobile Safari'
  - regex: '(iPod|iPod touch|iPhone|iPad)'
    family_replacement: 'Mobile Safari UI/WKWebView'
  - regex: '(Watch)((?:\d+\.)*\d+),(\d+)'
    family_replacement: 'Apple $1 App'
  - regex: '(Outlook-iOS)/((?:\d+\.)*\d+)\.prod\.iphone \(((?:\d+\.)*\d+)\)'
  - regex: '(AvantGo) ((?:\d+\.)*\d+)'
  - regex: '(OneBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'ONE Browser'
  - regex: '(Avant)'
    version_replacement: '1'
  - regex: '(QtCarBrowser)'
    version_replacement: '1'
  - regex: '^(iBrowser/Mini)((?:\d+\.)*\d+)'
    family_replacement: 'iBrowser Mini'
  - regex: '^(iBrowser|iRAPP)/((?:\d+\.)*\d+)'
  - regex: '^(Nokia)'
    family_replacement: 'Nokia Services (WAP) Browser'
  - regex: '(NokiaBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Nokia Browser'
  - regex: '(BrowserNG)/((?:\d+\.)*\d+)'
    family_replacement: 'Nokia Browser'
  - regex: '(Series60)/5\.0'
    family_replacement: 'Nokia Browser'
    version_replacement: '7.0'
  - regex: '(Series60)/((?:\d+\.)*\d+)'
    family_replacement: 'Nokia OSS Browser'
  - regex: '(S40OviBrowser)/((?:\d+\.)*\d+)'
    family_replacement: 'Ovi Browser'
  - regex: '(Nokia)[EN]?(\d+)'
  - regex: '(PlayBook).{1,200}RIM Tablet OS ((?:\d+\.)*\d+)'
    family_replacement: 'BlackBerry WebKit'
  - regex: '(Black[bB]erry|BB10).{1,200}Version/((?:\d+\.)*\d+)'
    family_replacement: 'BlackBerry WebKit'
  - regex: '(Black[bB]erry)\s?(\d+)'
    family_replacement: 'BlackBerry'
  - regex: '(OmniWeb)/v((?:\d+\.)*\d+)'
  - regex: '(Blazer)/((?:\d+\.)*\d+)'
    family_replacement: 'Palm Blazer'
  - regex: '(Pre)/((?:\d+\.)*\d+)'
    family_replacement: 'Palm Pre'
  - regex: '(ELinks)/((?:\d+\.)*\d+)'
  - regex: '(ELinks) \(((?:\d+\.)*\d+)'
  - regex: '(Links) \(((?:\d+\.)*\d+)'
  - regex: '(QtWeb) Internet Browser/((?:\d+\.)*\d+)'
  - regex: '(PhantomJS)/((?:\d+\.)*\d+)'
  - regex: '(AppleWebKit)/(\d+)(?:\.(\d+)|)\+ .{0,200} Safari'
    family_replacement: 'WebKit Nightly'
  - regex: '(Version)/((?:\d+\.)*\d+).{0,100}Safari/'
    family_replacement: 'Safari'
  - regex: '(Safari)/\d+'
  - regex: '(OLPC)/Update((?:\d+\.)*\d+)'
  - regex: '(OLPC)/Update()\.(\d+)'
    version_replacement: '0'
  - regex: '(SEMC\-Browser)/((?:\d+\.)*\d+)'
  - regex: '(Teleca)'
    family_replacement: 'Teleca Browser'
  - regex: '(Phantom)/V((?:\d+\.)*\d+)'
    family_replacement: 'Phantom Browser'
  - regex: '(Trident)/(7|8)\.(0)'
    family_replacement: 'IE'
    version_replacement: '11'
  - regex: '(Trident)/(6)\.(0)'
    family_replacement: 'IE'
    version_replacement: '10'
  - regex: '(Trident)/(5)\.(0)'
    family_replacement: 'IE'
    version_replacement: '9'
  - regex: '(Trident)/(4)\.(0)'
    family_replacement: 'IE'
    version_replacement: '8'
  - regex: '(Espial)/((?:\d+\.)*\d+)'
  - regex: '(AppleWebKit)/((?:\d+\.)*\d+)'
    family_replacement: 'Apple Mail'
  - regex: '(Firefox)/((?:\d+\.)*\d+)$'
  - regex: '(Firefox)/((?:\d+\.)*\d+)(pre|[ab]\d+[a-z]*|)'
  - regex: '([MS]?IE) ((?:\d+\.)*\d+)'
    family_replacement: 'IE'
  - regex: '(python-requests)/((?:\d+\.)*\d+)'
    family_replacement: 'Python Requests'
  - regex: '\b(Windows-Update-Agent|WindowsPowerShell|Microsoft-CryptoAPI|SophosUpdateManager|SophosAgent|Debian APT-HTTP|Ubuntu APT-HTTP|libcurl-agent|libwww-perl|urlgrabber|curl|PycURL|Wget|wget2|aria2|Axel|OpenBSD ftp|lftp|jupdate|insomnia|fetch libfetch|akka-http|got|CloudCockpitBackend|ReactorNetty|axios|Jersey|Vert.x-WebClient|Apache-CXF|Go-CF-client|go-resty|AHC|HTTPie)(?:[ /]((?:\d+\.)*\d+)|)'
  - regex: '^(cf)\/((?:\d+\.)*\d+)'
    family_replacement: 'CloudFoundry'
  - regex: '^(sap-leonardo-iot-sdk-nodejs) \/ ((?:\d+\.)*\d+)'
  - regex: '^(SAP NetWeaver Application Server) \(1.0;(\d{1})(\d{2})\)'
  - regex: '^(\w+-HTTPClient)\/((?:\d+\.)*\d+)-(\S+)'
    family_replacement: 'HTTPClient'
  - regex: '^(go-cli)\s((?:\d+\.)*\d+)'
  - regex: '^(Java-EurekaClient|Java-EurekaClient-Replication|HTTPClient|lua-resty-http)\/v?((?:\d+\.)*\d+)'
  - regex: '^(ping-service|sap xsuaa|Node-oauth|Site24x7|SAP CPI|JAEGER_SECURITY)'
  - regex: '(Python/3\.\d{1,3} aiohttp)/((?:\d+\.)*\d+)'
    family_replacement: 'Python aiohttp'
  - regex: '(Java)[/ ]?\d{1}\.(\d+)\.(\d+)[_-]*([a-zA-Z0-9]+|)'
  - regex: '(Java)[/ ]?((?:\d+\.)*\d+)'
  - regex: '(minio-go)/v((?:\d+\.)*\d+)'
  - regex: '^(ureq)[/ ]((?:\d+\.)*\d+)'
  - regex: '^(http\.rb)/((?:\d+\.)*\d+)'
  - regex: '^(GuzzleHttp)/((?:\d+\.)*\d+)'
  - regex: '^(grab)\b'
  - regex: '^(Cyberduck)/((?:\d+\.)*\d+)(?:\.\d+|)'
  - regex: '^(S3 Browser) ((?:\d+\.)*\d+)(?:\s*https?://s3browser\.com|)'
  - regex: '(S3Gof3r)'
  - regex: '\b(ibm-cos-sdk-(?:core|java|js|python))/((?:\d+\.)*\d+)'
  - regex: '^(rusoto)/((?:\d+\.)*\d+)'
  - regex: '^(rclone)/v((?:\d+\.)*\d+)'
  - regex: '^(Roku)/DVP-((?:\d+\.)*\d+)'
  - regex: '(Kurio)\/((?:\d+\.)*\d+)'
    family_replacement: 'Kurio App'
  - regex: '^(Box(?: Sync)?)/((?:\d+\.)*\d+)'
  - regex: '^(ViaFree|Viafree)-(?:tvOS-)?[A-Z]{2}/((?:\d+\.)*\d+)'
    family_replacement: 'ViaFree'
  - regex: '(Transmit)/((?:\d+\.)*\d+)'
  - regex: '(Download Master)'
  - regex: '\b(HTTrack) ((?:\d+\.)*\d+|)'
  - regex: '(Ladybird)\/((?:\d+\.)*\d+)'
  - regex: '(MullvadBrowser)/((?:\d+\.)*\d+|)'
  - regex: '^(rclone)/v((?:\d+\.)*\d+)'
  - regex: '^(Roku)/DVP-((?:\d+\.)*\d+)'
  - regex: '(Kurio)\/((?:\d+\.)*\d+)'
    family_replacement: 'Kurio App'
  - regex: '^(Box(?: Sync)?)/((?:\d+\.)*\d+)'
  - regex: '^(ViaFree|Viafree)-(?:tvOS-)?[A-Z]{2}/((?:\d+\.)*\d+)'
    family_replacement: 'ViaFree'
  - regex: '(Transmit)/((?:\d+\.)*\d+)'
  - regex: '(Download Master)'
  - regex: '\b(HTTrack) ((?:\d+\.)*\d+|)'
  - regex: '(Ladybird)\/((?:\d+\.)*\d+)'
  - regex: '(MullvadBrowser)/((?:\d+\.)*\d+|)'
`)

Functions

func MatchRange

func MatchRange(rangeStr string, value Comparable) bool

MatchRange 通用范围匹配

Types

type Comparable

type Comparable interface {
	Compare(other Comparable) int
}

Comparable 接口

type DefaultLogger

type DefaultLogger struct {
	// contains filtered or unexported fields
}

DefaultLogger is a simple implementation of Logger using the standard log package.

func NewDefaultLogger

func NewDefaultLogger() *DefaultLogger

NewDefaultLogger creates a new DefaultLogger.

func (*DefaultLogger) Infof

func (l *DefaultLogger) Infof(format string, args ...interface{})

Infof implements the Logger interface.

type IntComparable

type IntComparable int

IntComparable 实现

func (IntComparable) Compare

func (i IntComparable) Compare(other Comparable) int

type Logger

type Logger interface {
	// Infof logs an info message with format.
	Infof(format string, args ...interface{})
}

Logger is the interface for logging functionality. Users can implement this interface to provide their own logger.

type NoOpLogger

type NoOpLogger struct{}

NoOpLogger is a logger that does nothing. Useful when you want to disable logging completely.

func NewNoOpLogger

func NewNoOpLogger() *NoOpLogger

NewNoOpLogger creates a new NoOpLogger.

func (*NoOpLogger) Infof

func (l *NoOpLogger) Infof(format string, args ...interface{})

Infof implements the Logger interface but does nothing.

type Parser

type Parser struct {
	/* atomic operation are done on the following unit64.
	 * These must be 64bit aligned. On 32bit architectures
	 * this is only guaranteed to be on the beginning of a struct */
	UserAgentMisses uint64

	RegexesDefinitions
	UseSort bool
	// contains filtered or unexported fields
}

func New

func New(data []byte, opts ...ParserOption) (*Parser, error)

func NewFromFile

func NewFromFile(regexFile string, opts ...ParserOption) (*Parser, error)

func NewFromSaved

func NewFromSaved(opts ...ParserOption) (*Parser, error)

func (*Parser) Parse

func (parser *Parser) Parse(line string) *UserAgent

type ParserOption

type ParserOption func(*Parser)

func WithDebugMode

func WithDebugMode(debugMode bool) ParserOption

func WithLogger

func WithLogger(logger Logger) ParserOption

func WithMatchIdxNotOk

func WithMatchIdxNotOk(topCnt int) ParserOption

func WithMissesThreshold

func WithMissesThreshold(threshold uint64) ParserOption

func WithUseSort

func WithUseSort(useSort bool) ParserOption

type RegexesDefinitions

type RegexesDefinitions struct {
	UA []*uaParser `yaml:"user_agent_parsers"`

	sync.RWMutex
	// contains filtered or unexported fields
}

type UserAgent

type UserAgent struct {
	Family  string
	Version string
}

func (*UserAgent) ToString

func (ua *UserAgent) ToString() string

type UserAgentSorter

type UserAgentSorter []*uaParser

func (UserAgentSorter) Len

func (a UserAgentSorter) Len() int

func (UserAgentSorter) Less

func (a UserAgentSorter) Less(i, j int) bool

func (UserAgentSorter) Swap

func (a UserAgentSorter) Swap(i, j int)

type VersionComparable

type VersionComparable string

VersionComparable 实现

func (VersionComparable) Compare

func (v VersionComparable) Compare(other Comparable) int

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL