File size: 5,370 Bytes
4cef980
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
import random
from threading import Lock

from fake_useragent import settings
from fake_useragent.errors import FakeUserAgentError
from fake_useragent.log import logger
from fake_useragent.utils import load, load_cached, str_types, update


class FakeUserAgent:
    def __init__(
        self,
        use_external_data=False,
        cache_path=settings.DB,
        fallback=None,
        browsers=["chrome", "edge", "internet explorer", "firefox", "safari", "opera"],
        verify_ssl=True,
        safe_attrs=tuple(),
    ):
        assert isinstance(
            use_external_data, bool
        ), "use_external_data must be True or False"

        self.use_external_data = use_external_data

        assert isinstance(cache_path, str_types), "cache_path must be string or unicode"

        self.cache_path = cache_path

        if fallback is not None:
            assert isinstance(fallback, str_types), "fallback must be string or unicode"

        self.fallback = fallback

        assert isinstance(browsers, (list, str)), "browsers must be list or string"

        self.browsers = browsers

        assert isinstance(verify_ssl, bool), "verify_ssl must be True or False"

        self.verify_ssl = verify_ssl

        assert isinstance(
            safe_attrs, (list, set, tuple)
        ), "safe_attrs must be list\\tuple\\set of strings or unicode"

        if safe_attrs:
            str_types_safe_attrs = [isinstance(attr, str_types) for attr in safe_attrs]

            assert all(
                str_types_safe_attrs
            ), "safe_attrs must be list\\tuple\\set of strings or unicode"

        self.safe_attrs = set(safe_attrs)

        # initial empty data
        self.data_browsers = {}

        self.load()

    def load(self):
        try:
            with self.load.lock:
                if self.use_external_data:
                    # Use external resource to retrieve browser data
                    self.data_browsers = load_cached(
                        self.cache_path,
                        self.browsers,
                        verify_ssl=self.verify_ssl,
                    )
                else:
                    # By default we will try to load our local file
                    self.data_browsers = load(
                        self.browsers,
                        verify_ssl=self.verify_ssl,
                    )
        except FakeUserAgentError:
            if self.fallback is None:
                raise
            else:
                logger.warning(
                    "Error occurred during fetching data, "
                    "but was suppressed with fallback.",
                )

    load.lock = Lock()

    def update(self, use_external_data=None):
        with self.update.lock:
            if use_external_data is not None:
                assert isinstance(
                    use_external_data, bool
                ), "use_external_data must be True or False"

                self.use_external_data = use_external_data

            # Update tmp cache file from external data source
            if self.use_external_data:
                update(
                    self.cache_path,
                    self.browsers,
                    verify_ssl=self.verify_ssl,
                )

            self.load()

    update.lock = Lock()

    def __getitem__(self, attr):
        return self.__getattr__(attr)

    def __getattr__(self, attr):
        if attr in self.safe_attrs:
            return super(UserAgent, self).__getattr__(attr)

        try:
            for value, replacement in settings.REPLACEMENTS.items():
                attr = attr.replace(value, replacement)

            attr = attr.lower()

            if attr == "random":
                # Pick a random browser from the browsers argument list
                browser_name = random.choice(self.browsers)
            else:
                browser_name = settings.SHORTCUTS.get(attr, attr)

            # Pick a random user-agent string for a specific browser
            return random.choice(self.data_browsers[browser_name])
        except (KeyError, IndexError):
            if self.fallback is None:
                raise FakeUserAgentError(
                    f"Error occurred during getting browser: {attr}"
                )  # noqa
            else:
                logger.warning(
                    f"Error occurred during getting browser: {attr}, "
                    "but was suppressed with fallback.",
                )

                return self.fallback

    @property
    def chrome(self):
        return self.__getattr__("chrome")

    @property
    def googlechrome(self):
        return self.chrome

    @property
    def edge(self):
        return self.__getattr__("edge")

    @property
    def ie(self):
        return self.__getattr__("ie")

    @property
    def internetexplorer(self):
        return self.ie

    @property
    def msie(self):
        return self.ie

    @property
    def firefox(self):
        return self.__getattr__("firefox")

    @property
    def ff(self):
        return self.firefox

    @property
    def safari(self):
        return self.__getattr__("safari")

    @property
    def opera(self):
        return self.__getattr__("opera")

    @property
    def random(self):
        return self.__getattr__("random")


# common alias
UserAgent = FakeUserAgent