From b62272f50ee903a7b61d51cdf1e170ab3d15f3ef Mon Sep 17 00:00:00 2001 From: handmine Date: Fri, 16 Jul 2021 18:49:05 +0800 Subject: [PATCH] init package --- README.md | 86 +++++++ anti_useragent/__init__.py | 7 + anti_useragent/exceptions.py | 8 + anti_useragent/settings/__init__.py | 249 ++++++++++++++++++++ anti_useragent/settings/default_settings.py | 56 +++++ anti_useragent/useragent/__init__.py | 44 ++++ anti_useragent/useragent/chrome.py | 42 ++++ anti_useragent/useragent/firefox.py | 43 ++++ anti_useragent/useragent/opera.py | 45 ++++ anti_useragent/useragent/ua.py | 39 +++ anti_useragent/utils/__init__.py | 3 + anti_useragent/utils/log.py | 96 ++++++++ anti_useragent/utils/misc.py | 7 + setup.py | 58 +++++ test.py | 12 + 15 files changed, 795 insertions(+) create mode 100644 README.md create mode 100644 anti_useragent/__init__.py create mode 100644 anti_useragent/exceptions.py create mode 100644 anti_useragent/settings/__init__.py create mode 100644 anti_useragent/settings/default_settings.py create mode 100644 anti_useragent/useragent/__init__.py create mode 100644 anti_useragent/useragent/chrome.py create mode 100644 anti_useragent/useragent/firefox.py create mode 100644 anti_useragent/useragent/opera.py create mode 100644 anti_useragent/useragent/ua.py create mode 100644 anti_useragent/utils/__init__.py create mode 100644 anti_useragent/utils/log.py create mode 100644 anti_useragent/utils/misc.py create mode 100644 setup.py create mode 100644 test.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..47c77eb --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# anti-useragent + + + +> info: fake chrome, firefox, opera browser header anti useragent + +## Features + +- more browser up to date +- more randomize ruler +- grabs up to date `useragent` from [useragentstring.com](http://useragentstring.com/) + +### Installation + +```shell +pip install anti-useragent +``` + +### Usage + +```python +from anti_useragent import UserAgent +ua = UserAgent() + +ua.opera +# Opera/9.80 (X11; Linux i686; U; ru) Presto/2.8.131 Version/11.11 +ua.chrome +# Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36 +ua['chrome'] +# Mozilla/5.0 (Windows NT 5.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.2.3576.5 Safari/537.36 +ua.firefox +# Mozilla/5.0 (Windows NT 5.1; WOW64; rv:47.0) Gecko/20100101 Firefox/45.0 +ua['firefox'] +# Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:49.0) Gecko/20100101 Firefox/31.0 + +# and the best one, random via real world browser usage statistic +ua.random +# Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.3.8610.5 Safari/537.36 +``` + + + +If You want to specify the platform just: + +```python +from anti_useragent import UserAgent +ua = UserAgent(platform='mac') +``` + +If You want to specify the browser max version just: + +```python +from anti_useragent import UserAgent +ua = UserAgent(version=90) +``` + +If You want to specify the enable logger just: + +```python +from anti_useragent import UserAgent +ua = UserAgent(logger=True) + +# the default install loguru +try: + from loguru import logger +except: + install("loguru") + from loguru import logger +``` + + + +Make sure that You using latest version + +``` +pip install -U anti-useragent +``` + +Check version via python console: + +``` +import anti_useragent + +print(anti_useragent.VERSION) +``` + diff --git a/anti_useragent/__init__.py b/anti_useragent/__init__.py new file mode 100644 index 0000000..cba7a7f --- /dev/null +++ b/anti_useragent/__init__.py @@ -0,0 +1,7 @@ +from __future__ import absolute_import, unicode_literals + +from anti_useragent.useragent.ua import AntiUserAgent, UserAgent + +__version__ = '0.0.1' + +VERSION = __version__ diff --git a/anti_useragent/exceptions.py b/anti_useragent/exceptions.py new file mode 100644 index 0000000..0f24e44 --- /dev/null +++ b/anti_useragent/exceptions.py @@ -0,0 +1,8 @@ +from __future__ import absolute_import, unicode_literals + + +class AntiUserAgentError(Exception): + pass + + +UserAgentError = AntiUserAgentError diff --git a/anti_useragent/settings/__init__.py b/anti_useragent/settings/__init__.py new file mode 100644 index 0000000..ecfab14 --- /dev/null +++ b/anti_useragent/settings/__init__.py @@ -0,0 +1,249 @@ +import json +import copy +from collections.abc import MutableMapping +from importlib import import_module +from pprint import pformat + +from . import default_settings + + +SETTINGS_PRIORITIES = { + 'default': 0, + 'command': 10, + 'project': 20, + 'spider': 30, + 'cmdline': 40, +} + + +def get_settings_priority(priority) -> int: + """ + get settings priority [SETTINGS_PRIORITIES] ~ `from settings import SETTINGS_PRIORITIES` + :param priority: config | others + :return: int + """ + if isinstance(priority, str): + return SETTINGS_PRIORITIES[priority] + else: + return priority + + +class SettingsAttribute: + + def __init__(self, value, priority): + self.value = value + if isinstance(self.value, BaseSettings): + self.priority = max(self.value.maxpriority(), priority) + else: + self.priority = priority + + def set(self, value, priority): + if priority >= self.priority: + if isinstance(self.value, BaseSettings): + value = BaseSettings(value, priority=priority) + self.value = value + self.priority = priority + + def __str__(self): + return f"" + + __repr__ = __str__ + + +class BaseSettings(MutableMapping): + + def __init__(self, values=None, priority='project'): + self.frozen = False + self.attributes = {} + if values: + self.update(values, priority) + + def __getitem__(self, opt_name): + if opt_name not in self: + return None + return self.attributes[opt_name].value + + def __contains__(self, name): + return name in self.attributes + + def get(self, name, default=None): + return self[name] if self[name] is not None else default + + def getbool(self, name, default=False): + got = self.get(name, default) + try: + return bool(int(got)) + except ValueError: + if got in ("True", "true"): + return True + if got in ("False", "false"): + return False + raise ValueError("Supported values for boolean settings " + "are 0/1, True/False, '0'/'1', " + "'True'/'False' and 'true'/'false'") + + def getint(self, name, default=0): + return int(self.get(name, default)) + + def getfloat(self, name, default=0.0): + return float(self.get(name, default)) + + def getlist(self, name, default=None): + + value = self.get(name, default or []) + if isinstance(value, str): + value = value.split(',') + return list(value) + + def getdict(self, name, default=None): + value = self.get(name, default or {}) + if isinstance(value, str): + value = json.loads(value) + return dict(value) + + def getwithbase(self, name): + compbs = BaseSettings() + compbs.update(self[name + '_BASE']) + compbs.update(self[name]) + return compbs + + def getpriority(self, name): + if name not in self: + return None + return self.attributes[name].priority + + def maxpriority(self): + if len(self) > 0: + return max(self.getpriority(name) for name in self) + else: + return get_settings_priority('default') + + def __setitem__(self, name, value): + self.set(name, value) + + def set(self, name, value, priority='project'): + self._assert_mutability() + priority = get_settings_priority(priority) + if name not in self: + if isinstance(value, SettingsAttribute): + self.attributes[name] = value + else: + self.attributes[name] = SettingsAttribute(value, priority) + else: + self.attributes[name].set(value, priority) + + def setdict(self, values, priority='project'): + self.update(values, priority) + + def setmodule(self, module, priority='project'): + self._assert_mutability() + if isinstance(module, str): + module = import_module(module) + for key in dir(module): + if key.isupper(): + self.set(key, getattr(module, key), priority) + + def update(self, values, priority='project'): + self._assert_mutability() + if isinstance(values, str): + values = json.loads(values) + if values is not None: + if isinstance(values, BaseSettings): + for name, value in values.items(): + self.set(name, value, values.getpriority(name)) + else: + for name, value in values.items(): + self.set(name, value, priority) + + def delete(self, name, priority='project'): + self._assert_mutability() + priority = get_settings_priority(priority) + if priority >= self.getpriority(name): + del self.attributes[name] + + def __delitem__(self, name): + self._assert_mutability() + del self.attributes[name] + + def _assert_mutability(self): + if self.frozen: + raise TypeError("Trying to modify an immutable Settings object") + + def copy(self): + return copy.deepcopy(self) + + def freeze(self): + self.frozen = True + + def frozencopy(self): + copy = self.copy() + copy.freeze() + return copy + + def __iter__(self): + return iter(self.attributes) + + def __len__(self): + return len(self.attributes) + + def _to_dict(self): + return {k: (v._to_dict() if isinstance(v, BaseSettings) else v) + for k, v in self.items()} + + def copy_to_dict(self): + settings = self.copy() + return settings._to_dict() + + def _repr_pretty_(self, p, cycle): + if cycle: + p.text(repr(self)) + else: + p.text(pformat(self.copy_to_dict())) + + +class _DictProxy(MutableMapping): + + def __init__(self, settings, priority): + self.o = {} + self.settings = settings + self.priority = priority + + def __len__(self): + return len(self.o) + + def __getitem__(self, k): + return self.o[k] + + def __setitem__(self, k, v): + self.settings.set(k, v, priority=self.priority) + self.o[k] = v + + def __delitem__(self, k): + del self.o[k] + + def __iter__(self, k, v): + return iter(self.o) + + +class Settings(BaseSettings): + + def __init__(self, values=None, priority='project'): + super().__init__() + self.setmodule(default_settings, 'default') + for name, val in self.items(): + if isinstance(val, dict): + self.set(name, BaseSettings(val, 'default'), 'default') + self.update(values, priority) + + +def iter_default_settings(): + for name in dir(default_settings): + if name.isupper(): + yield name, getattr(default_settings, name) + + +def overridden_settings(settings): + for name, defvalue in iter_default_settings(): + value = settings[name] + if not isinstance(defvalue, dict) and value != defvalue: + yield name, value diff --git a/anti_useragent/settings/default_settings.py b/anti_useragent/settings/default_settings.py new file mode 100644 index 0000000..89b3f29 --- /dev/null +++ b/anti_useragent/settings/default_settings.py @@ -0,0 +1,56 @@ + +BASE_USER_AGENT_FIREFOX = 'Mozilla/5.0 (%(system_info)s; %(system_bit)s; rv:%(r_version)s)' \ + ' Gecko/20100101 Firefox/%(version)s' + +BASE_USER_AGENT_CHROME = 'Mozilla/5.0 (%(system_info)s; %(system_bit)s) ' \ + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome' \ + '/%(big_version)d.%(mid_version)d.%(small_version)d.%(beta_version)d Safari/537.36' + +BASE_USER_AGENT_OPERA = 'Mozilla/5.0 (%(system_info)s; %(system_bit)s) ' \ + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome' \ + '/%(big_version)d.%(mid_version)d.%(small_version)d.%(beta_version)d Safari/537.36 ' \ + 'OPR/%(o_version)s.0.%(os_version)s.%(ob_version)s' + + +WIN_SYSTEM = [ + 'Windows NT 5.0', + 'Windows NT 5.1', + 'Windows NT 6.0', + 'Windows NT 6.1', + 'Windows NT 6.2', + 'Windows NT 6.3', + 'Windows NT 10.0', +] + +WIN_SYSTEM_BIT = [ + 'Win64; x64', + 'WOW64', +] + +MAC_SYSTEM = [ + 'Macintosh', +] + +MAC_SYSTEM_BIT = [ + 'PPC Mac OS X', + 'Intel Mac OS X' +] + +LINUX_SYSTEM = [ + 'X11' +] + +LINUX_SYSTEM_BIT = [ + 'Linux ppc', + 'Linux ppc64', + 'Linux i686', + 'Linux x86_64', +] + +PLATFORM = ['windows', 'mac', 'linux'] + +PLATFORM_OVERRIDES = { + 'windows': [WIN_SYSTEM, WIN_SYSTEM_BIT], + 'mac': [MAC_SYSTEM, MAC_SYSTEM_BIT], + 'linux': [LINUX_SYSTEM, LINUX_SYSTEM_BIT] +} diff --git a/anti_useragent/useragent/__init__.py b/anti_useragent/useragent/__init__.py new file mode 100644 index 0000000..9eef66d --- /dev/null +++ b/anti_useragent/useragent/__init__.py @@ -0,0 +1,44 @@ +from anti_useragent.settings import Settings +from anti_useragent.utils import logging + + +class BaseUserAgent: + _INSTANCE = None + + def __new__(cls, *args, **kwargs): + if not cls._INSTANCE: + cls._INSTANCE = super().__new__(cls) + return cls._INSTANCE + + def __init__(self, platform: str, version: int, logger, *args, **kwargs): + self.settings = self._settings + if version: + assert isinstance(version, int), 'Version must be int' + if platform: + assert isinstance(platform, str), 'Platform must be string' + if all([platform, (platform not in self.settings.get('PLATFORM'))]): + raise TypeError('Unknown platform type: %s' % platform) + + self.logger = logger + self.platform = platform + self.version = version + self.s_info = None + self.s_bit = None + + if logger: + self.logger = logging.get_logger('anti_useragent') + + def set_platform(self, platform): + if not platform: + platform = 'windows' + self.platform = platform + self.select_params(self.platform) + + def select_params(self, platform): + assert platform, 'Must be have param platform' + self.s_info = self.settings.get('PLATFORM_OVERRIDES')[platform][0] + self.s_bit = self.settings.get('PLATFORM_OVERRIDES')[platform][1] + + @property + def _settings(self) -> Settings: + return Settings() diff --git a/anti_useragent/useragent/chrome.py b/anti_useragent/useragent/chrome.py new file mode 100644 index 0000000..4ce5671 --- /dev/null +++ b/anti_useragent/useragent/chrome.py @@ -0,0 +1,42 @@ +from random import randint, choice +from anti_useragent.useragent import BaseUserAgent + + +class ChromeUA(BaseUserAgent): + """ + Mozilla/5.0 是一个通用标记符号,用来表示与 Mozilla 兼容,这几乎是现代浏览器的标配。 + platform 用来说明浏览器所运行的原生系统平台(例如 Windows、Mac、Linux 或 Android),以及是否运行在手机上。搭载 Firefox OS 的手机仅简单地使用了 "Mobile" 这个字符串;因为 web 本身就是平台。注意 platform 可能会包含多个使用 "; " 隔开的标记符号。参见下文获取更多的细节信息及示例。 + Chrome (或 Chromium/blink-based engines)用户代理字符串与 Firefox 的格式类似。为了兼容性,它添加了诸如 "KHTML, like Gecko" 和 "Safari" 这样的字符串。 + + example: + Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36 + platform: + windows + mac + linux + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if not self.version: + self.version = 90 + + @property + def ua(self): + self.set_platform(self.platform) + _ua = self.settings.get('BASE_USER_AGENT_CHROME') % { + 'system_info': choice(self.s_info), + 'system_bit': choice(self.s_bit), + 'big_version': randint(55, self.version), + 'mid_version': randint(0, 9), + 'small_version': randint(3000, 9999), + 'beta_version': randint(0, 9), + } + if self.logger: + self.logger.debug(_ua) + return _ua + + def __repr__(self): + return "" % (self.platform, self.version) + + diff --git a/anti_useragent/useragent/firefox.py b/anti_useragent/useragent/firefox.py new file mode 100644 index 0000000..ef46970 --- /dev/null +++ b/anti_useragent/useragent/firefox.py @@ -0,0 +1,43 @@ +from random import randint, choice +from anti_useragent.useragent import BaseUserAgent + + +class FirefoxUA(BaseUserAgent): + """ + Mozilla/5.0 是一个通用标记符号,用来表示与 Mozilla 兼容,这几乎是现代浏览器的标配。 + platform 用来说明浏览器所运行的原生系统平台(例如 Windows、Mac、Linux 或 Android),以及是否运行在手机上。搭载 Firefox OS 的手机仅简单地使用了 "Mobile" 这个字符串;因为 web 本身就是平台。注意 platform 可能会包含多个使用 "; " 隔开的标记符号。参见下文获取更多的细节信息及示例。 + rv:geckoversion 表示 Gecko 的发布版本号(例如 "17.0")。在近期发布的版本中,geckoversion 表示的值与 firefoxversion 相同。 + Gecko/geckotrail 表示该浏览器基于 Gecko 渲染引擎。 + 在桌面浏览器中, geckotrail 是固定的字符串 "20100101" 。 + Firefox/firefoxversion 表示该浏览器是 Firefox,并且提供了版本号信息(例如 "17.0")。 + + eg: Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion + example: + Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 + Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0 + platform: + windows + mac + linux + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if not self.version: + self.version = 50 + + @property + def ua(self): + self.set_platform(self.platform) + _ua = self.settings.get('BASE_USER_AGENT_FIREFOX') % { + 'system_info': choice(self.s_info), + 'system_bit': choice(self.s_bit), + 'r_version': float(randint(10, self.version)), + 'version': float(randint(10, 50)) + } + if self.logger: + self.logger.debug(_ua) + return _ua + + def __repr__(self): + return "" % (self.platform, self.version) diff --git a/anti_useragent/useragent/opera.py b/anti_useragent/useragent/opera.py new file mode 100644 index 0000000..fc6e315 --- /dev/null +++ b/anti_useragent/useragent/opera.py @@ -0,0 +1,45 @@ +from random import randint, choice +from anti_useragent.useragent import BaseUserAgent + + +class OperaUA(BaseUserAgent): + """ + Mozilla/5.0 是一个通用标记符号,用来表示与 Mozilla 兼容,这几乎是现代浏览器的标配。 + platform 用来说明浏览器所运行的原生系统平台(例如 Windows、Mac、Linux 或 Android),以及是否运行在手机上。搭载 Firefox OS 的手机仅简单地使用了 "Mobile" 这个字符串;因为 web 本身就是平台。注意 platform 可能会包含多个使用 "; " 隔开的标记符号。参见下文获取更多的细节信息及示例。 + Chrome Opera 也是一款基于 blink 引擎的浏览器,这也是为什么它的 UA 看起来(和 Chrome 的)几乎一样的原因,不过,它添加了一个 "OPR/"。 + + example: + Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36 OPR/38.0.2220.41 + platform: + windows + mac + linux + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if not self.version: + self.version = 90 + + @property + def ua(self): + self.set_platform(self.platform) + _ua = self.settings.get('BASE_USER_AGENT_OPERA') % { + 'system_info': choice(self.s_info), + 'system_bit': choice(self.s_bit), + 'big_version': randint(55, self.version), + 'mid_version': randint(0, 9), + 'small_version': randint(3000, 9999), + 'beta_version': randint(0, 9), + 'o_version': randint(30, self.version), + 'os_version': randint(1000, 9999), + 'ob_version': randint(10, 99), + } + if self.logger: + self.logger.debug(_ua) + return _ua + + def __repr__(self): + return "" % (self.platform, self.version) + + diff --git a/anti_useragent/useragent/ua.py b/anti_useragent/useragent/ua.py new file mode 100644 index 0000000..7e82c59 --- /dev/null +++ b/anti_useragent/useragent/ua.py @@ -0,0 +1,39 @@ +from random import choice + +from anti_useragent.useragent.chrome import ChromeUA +from anti_useragent.useragent.firefox import FirefoxUA +from anti_useragent.useragent.opera import OperaUA +from anti_useragent.exceptions import UserAgentError, AntiUserAgentError + + +class UserAgent(object): + shortcut = { + 'chrome': ChromeUA, + 'firefox': FirefoxUA, + 'opera': OperaUA + } + + def __init__(self, platform=None, version=None, logger=False): + self.logger = logger + self.platform = platform + self.version = version + + def __getitem__(self, item): + return self.__getattr__(item) + + def __getattr__(self, item): + try: + if item == 'random': + attr = choice(list(self.shortcut.keys())) + _ua = self.shortcut[attr](self.platform, self.version, self.logger) + if not _ua.platform: + platform = choice(_ua.settings.get('PLATFORM')) + _ua.set_platform(platform) + return getattr(_ua, 'ua') + else: + return getattr(self.shortcut[item](self.platform, self.version, self.logger), 'ua') + except UserAgentError: + raise AntiUserAgentError('Error occurred during getting useragent') + + +AntiUserAgent = UserAgent diff --git a/anti_useragent/utils/__init__.py b/anti_useragent/utils/__init__.py new file mode 100644 index 0000000..0235f43 --- /dev/null +++ b/anti_useragent/utils/__init__.py @@ -0,0 +1,3 @@ +from anti_useragent.utils.log import LogFormatter + +logging = LogFormatter() diff --git a/anti_useragent/utils/log.py b/anti_useragent/utils/log.py new file mode 100644 index 0000000..32e726a --- /dev/null +++ b/anti_useragent/utils/log.py @@ -0,0 +1,96 @@ +from __future__ import absolute_import, unicode_literals + +import sys +import socket + +from anti_useragent.utils.misc import install + + +try: + from loguru import logger +except: + install("loguru") + from loguru import logger + + +def set_log_config(formatter, logfile=None): + return { + "default": { + "handlers": [ + { + "sink": sys.stdout, + "format": formatter, + "level": "TRACE" + }, + { + "sink": "info.log" if not logfile else logfile, + "format": formatter, + "level": "INFO", + "rotation": '1 week', + "retention": '30 days', + 'encoding': 'utf-8' + }, + ], + "extra": { + "host": socket.gethostbyname(socket.gethostname()), + 'log_name': 'default', + 'type': 'None' + }, + "levels": [ + dict(name="TRACE", icon="✏️", color=""), + dict(name="DEBUG", icon="❄️", color=""), + dict(name="INFO", icon="♻️", color=""), + dict(name="SUCCESS", icon="✔️", color=""), + dict(name="WARNING", icon="⚠️", color=""), + dict(name="ERROR", icon="❌️", color=""), + dict(name="CRITICAL", icon="☠️", color=""), + ] + }, + 'kafka': True + } + + +class LogFormatter(object): + default_formatter = '{time:YYYY-MM-DD HH:mm:ss,SSS} | ' \ + '[{extra[log_name]}] {module}:{name}:{function}:{line} | ' \ + '{extra[host]} | ' \ + '{level.icon}{level: <5} | ' \ + '{level.no} | ' \ + '{extra[type]} | ' \ + '{message} ' + + kafka_formatter = '{time:YYYY-MM-DD HH:mm:ss,SSS}| ' \ + '[{extra[log_name]}] {module}:{name}:{function}:{line} | ' \ + '{extra[host]} | ' \ + '{process} | ' \ + '{thread} | ' \ + '{level: <5} | ' \ + '{level.no} | ' \ + '{extra[type]}| ' \ + '{message} ' + + def __init__(self): + self.logger = logger + + def setter_log_handler(self, callback=None): + assert callable(callback), 'callback must be a callable object' + self.logger.add(callback, format=self.kafka_formatter) + + def get_logger(self, name=None): + log_config = set_log_config(self.default_formatter) + config = log_config.pop('default', {}) + if name: + config['extra']['log_name'] = name + self.logger.configure(**config) + return self.logger + + @staticmethod + def format(spider, meta): + if hasattr(spider, 'logging_keys'): + logging_txt = [] + for key in spider.logging_keys: + if meta.get(key, None) is not None: + logging_txt.append(u'{0}:{1} '.format(key, meta[key])) + logging_txt.append('successfully') + return ' '.join(logging_txt) + diff --git a/anti_useragent/utils/misc.py b/anti_useragent/utils/misc.py new file mode 100644 index 0000000..13d90be --- /dev/null +++ b/anti_useragent/utils/misc.py @@ -0,0 +1,7 @@ +import sys +import subprocess + + +def install(package): + subprocess.call([sys.executable, "-m", "pip", "install", package]) + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..05fda19 --- /dev/null +++ b/setup.py @@ -0,0 +1,58 @@ +from __future__ import unicode_literals + +import os +import io + +from setuptools import setup + + +def list_dir(dir): + result = [dir] + for file in os.listdir(dir): + if os.path.isdir(os.path.join(dir, file)): + result.extend(list_dir(os.path.join(dir, file))) + return result + + +def read(*parts): + filename = os.path.join(os.path.abspath(os.path.dirname(__file__)), *parts) + + with io.open(filename, encoding='utf-8', mode='rt') as fp: + return fp.read() + + +NAME = "anti_useragent" +PACKAGES = list_dir('anti_useragent') +DESCRIPTION = "fake chrome, firefox, opera browser header anti useragent" +LONG_DESCRIPTION = read('README.md') +URL = "" +AUTHOR = "handmine" +AUTHOR_EMAIL = "handmine@outlook.com" +VERSION = "0.0.1" +LICENSE = "MIT" + +setup( + name=NAME, + version=VERSION, + description=DESCRIPTION, + long_description=LONG_DESCRIPTION, + classifiers=[ + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python', + 'Intended Audience :: Developers', + 'Operating System :: OS Independent', + ], + url=URL, + author=AUTHOR, + author_email=AUTHOR_EMAIL, + license=LICENSE, + packages=PACKAGES, + include_package_data=True, + zip_safe=True, + install_requires=[], + keywords=[ + 'user', 'agent', 'user agent', 'useragent', + 'fake', 'fake useragent', 'fake user agent', + 'anti', 'anti useragent' + ], +) diff --git a/test.py b/test.py new file mode 100644 index 0000000..abed505 --- /dev/null +++ b/test.py @@ -0,0 +1,12 @@ +from anti_useragent import UserAgent +from fake_useragent import FakeUserAgent + + +if __name__ == '__main__': + print(UserAgent(platform='mac').random) + print(UserAgent()['firefox']) + print(FakeUserAgent().chrome) + + import anti_useragent + + print(anti_useragent.VERSION)