PK!lQ CHANGELOG* Version 1.0.2 + Data :: <2019-03-10 Sun> + Changes :: Update nalude. * Version 1.0.1 + Data :: <2019-03-10 Sun> + Changes :: Update requests-html. * Version 1.0.0 + Data :: <2019-02-28 Thu> + Changes :: Now, old HTTP methods (~get~ and ~post~) cannot accept multiple URLs. Instead, we can use ~gets~ and ~posts~. + Adds :: - ~nacf.html~ - ~nacf.json~ - ~nacf.gets~ - ~nacf.posts~ + Includes :: - ~nalude~ * Version 0.1.2 + Data :: <2018-12-26 Wed> + Fixed :: ~inspect.Parameter~ error in last version. * Version 0.1.1 + Data :: <2018-12-26 Wed> + Ignored :: An error caused by ~inspect.Parameter~ + Help Wanted :: Can someone help me about the Parameter? * Version 0.1.0 + Date :: <2018-12-23 Sun> + Commemorate Version :: First Version - Basic Functions. PK!Ђ,, README.org#+OPTIONS: ':t *:t -:t ::t <:t H:5 \n:nil ^:{} arch:headline author:t #+OPTIONS: broken-links:nil c:nil creator:nil d:(not "LOGBOOK") date:t e:t #+OPTIONS: email:nil f:t inline:t num:nil p:t pri:nil prop:nil stat:t tags:t #+OPTIONS: tasks:t tex:t timestamp:t title:t toc:t todo:t |:t #+TITLE: README #+DATE: <2018-12-23 Sun> #+AUTHOR: Nasy #+EMAIL: nasyxx@gmail.com #+LANGUAGE: en #+SELECT_TAGS: export #+EXCLUDE_TAGS: noexport #+CREATOR: Emacs 27.0.50 (Org mode 9.1.9) #+SETUPFILE: https://fniessen.github.io/org-html-themes/setup/theme-readtheorg.setup * Prologue Never had such a pure crawler like this ~nacf~. Although I often write crawlers, I don't like to use huge frameworks, such as scrapy, but prefer simple ~requests+bs4~ or more general ~requests_html~. However, these two are inconvenient for a crawler. E.g. Places, such as error retrying or parallel crawling, need to be handwritten by myself. It is not very difficult to write it while writing too much can be tedious. Hence I started writing this nacf (Nasy Crawler Framework), hoping to simplify some error retrying or parallel writing of crawlers. * Packages #+caption: Packages | Package | Version | Description | |---------------+---------+----------------------------------------------------| | requests-html | 0.9.0 | HTML Parsing for Humans. | | nalude | 0.2.0 | A standard module. Inspired by Haskell's Prelude. | * Development Process #+include: "./todo.org" * Epoligue ** History #+include: "./CHANGELOG" PK!v"nacf/__init__.py#!/usr/bin/env python3 # -*- coding: utf-8 -*- r""" Life's pathetic, have fun ("▔□▔)/hi~♡ Nasy. Excited without bugs:: | * * | . . | . | * , | . | | * | |\___/| | ) -( . · | =\ - /= | )===( * | / - \ | |- | | / - \ 0.|.0 | NASY___\__( (__/_____(\=/)__+1s____________ | ______|____) )______|______|______|______|_ | ___|______( (____|______|______|______|____ | ______|____\_|______|______|______|______|_ | ___|______|______|______|______|______|____ | ______|______|______|______|______|______|_ | ___|______|______|______|______|______|____ author : Nasy https://nasy.moe date : Dec 23, 2018 email : Nasy filename : __init__.py project : nacf license : GPL-3.0+ There are more things in heaven and earth, Horatio, than are dreamt. -- From "Hamlet" """ __version__ = "1.0.2" # Local Packages from .models import (css, get, gets, html, json, post, text, urls, posts, xpath, parallel,) if __name__ == "__main__": assert css assert get assert gets assert post assert posts assert html assert json assert text assert urls assert xpath assert parallel PK!un::nacf/models.py#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Life's pathetic, have fun ("▔□▔)/hi~♡ Nasy. Excited without bugs:: | * * | . . | . | * , | . | | * | |\___/| | ) -( . · | =\ - /= | )===( * | / - \ | |- | | / - \ 0.|.0 | NASY___\__( (__/_____(\=/)__+1s____________ | ______|____) )______|______|______|______|_ | ___|______( (____|______|______|______|____ | ______|____\_|______|______|______|______|_ | ___|______|______|______|______|______|____ | ______|______|______|______|______|______|_ | ___|______|______|______|______|______|____ author : Nasy https://nasy.moe date : Dec 24, 2018 update : Feb 28, 2019 email : Nasy filename : models.py project : nacf license : GPL-3.0+ There are more things in heaven and earth, Horatio, than are dreamt. -- From "Hamlet" """ # Standard Library from inspect import Parameter from functools import wraps from itertools import chain, cycle from multiprocessing.dummy import Pool # Web Packages from requests_html import HTMLSession as s # Local Packages from .tools import format_url, generate_sig from .types import (RT, HTML, Ele, Res, Url, Data, Eles, Func, Json, List, Urls, EFunc, Reses, RFunc, SFunc, Tuple, Union, RsFunc, Callable, Iterable, Optional, a, b,) def urls(urls_: Urls, **kwds: a) -> Callable[[Func], Func]: """Feed urls to next function.""" def decorator(func: Func) -> Func: """Decorate function func.""" @wraps(func) def wrapper() -> RT: """Wrap function func.""" return func(_urls=urls_, **kwds) return wrapper return decorator def parallel(n: Optional[int] = None, **kwds: a) -> Callable[[Func], Func]: """Run parallelly.""" def decorator(func: Func) -> Func: """Decorate function func.""" @wraps(func) def wrapper( _urls: Urls, *, _datas: Optional[Iterable[Data]] = None, _jsons: Optional[Iterable[Json]] = None, **_kwds: b, ) -> RT: """Wrap function func.""" def runner(udj: Tuple[Url, Optional[Data], Optional[Json]]) -> RT: """Run func wrapper.""" url, data, json = udj return ( func(url, _data=data, _json=json, **_kwds, **kwds) if data or json else func(url, **_kwds, **kwds) ) with Pool(n) as pool: res = pool.map( runner, zip( _urls, _datas or cycle([None]), _jsons or cycle([dict()]), ), ) return res setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_datas", Parameter.KEYWORD_ONLY, default=None, annotation="Optional[Iterable[Data]]", ), Parameter( "_jsons", Parameter.KEYWORD_ONLY, default=None, annotation="Optional[Iterable[Json]]", ), ), ) return wrapper return decorator def get(url: Url = None, **kwds: a) -> Callable[[RFunc], RFunc]: """Get url.""" def decorator(func: RFunc) -> RFunc: """Decorate function func.""" @wraps(func) def wrapper( _url: Optional[Url] = None, *, _s: Optional[s] = None ) -> Res: """Wrap function func.""" return func((_s or s()).get(format_url(_url or url), **kwds)) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_url", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Optional[Url]", ), Parameter( "_s", Parameter.KEYWORD_ONLY, default=None, annotation="Optional[HTMLSession]", ), ), ) return wrapper return decorator def gets(urls: Urls = None, **kwds: a) -> Callable[[RsFunc], RsFunc]: """Get url.""" def decorator(func: RsFunc) -> RsFunc: """Decorate function func.""" @wraps(func) def wrapper( _urls: Optional[Urls] = None, *, _s: Optional[s] = None ) -> Reses: """Wrap function func.""" return func( map( lambda u: (_s or s()).get(format_url(u)), _urls or urls or [""], ), **kwds, ) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_urls", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Optional[Urls]", ), Parameter( "_s", Parameter.KEYWORD_ONLY, default=None, annotation="Optional[HTMLSession]", ), ), ) return wrapper return decorator def post( url: Optional[Url] = None, *, data: Data = None, json: Json = None, **kwds: a, ) -> Callable[[RFunc], RFunc]: """Post url with data and json.""" def decorator(func: RFunc) -> RFunc: """Decorate function func.""" @wraps(func) def wrapper( _url: Optional[Url] = None, _data: Optional[Data] = None, _json: Optional[Json] = None, *, _s: Optional[s] = None, ) -> Res: """Wrap function func.""" return func( (_s or s()).post(format_url(url), data=data, json=json, **kwds) ) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_url", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Optional[Url]", ), Parameter( "_data", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Optional[Data]", ), Parameter( "_json", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Optional[Json]", ), Parameter( "_s", Parameter.KEYWORD_ONLY, default=None, annotation="Optional[HTMLSession]", ), ), ) return wrapper return decorator def posts( urls: Optional[Urls] = None, *, datas: Optional[Iterable[Data]] = None, jsons: Optional[Iterable[Json]] = None, **kwds: a, ) -> Callable[[RsFunc], RsFunc]: """Post url with data and json.""" def decorator(func: RsFunc) -> RsFunc: """Decorate function func.""" @wraps(func) def wrapper( _urls: Optional[Urls] = None, *, _datas: Optional[Iterable[Data]] = None, _jsons: Optional[Iterable[Json]] = None, _s: Optional[s] = None, ) -> Res: """Wrap function func.""" def _post(udj: Tuple[Url, Data, Json]) -> Res: """Make a post.""" url, data, json = udj return (_s or s()).post( format_url(url), data=data, json=json, **kwds ) return func( map( _post, zip( _urls or urls or [""], _datas or datas or cycle([None]), _jsons or jsons or cycle([dict()]), ), ) ) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_urls", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Optional[Urls]", ), Parameter( "_datas", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Optional[Iterable[Data]]", ), Parameter( "_jsons", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Optional[Iterable[Json]]", ), Parameter( "_s", Parameter.KEYWORD_ONLY, default=None, annotation="Optional[HTMLSession]", ), ), ) return wrapper return decorator def css( selector: str = "*", *, containing: Union[str, List[str]] = None, clean: bool = False, first: bool = False, _encoding: str = None, ) -> Callable[[EFunc], EFunc]: """Css response.""" def decorator(func: EFunc) -> EFunc: """Decorate function func.""" @wraps(func) def wrapper(_res: Union[Res, Reses]) -> Union[Ele, Eles]: """Wrap function func.""" return ( func( _res.html.find( selector, containing=containing, clean=clean, first=first, _encoding=_encoding, ) ) if isinstance(_res, Res) else func( (lambda ele: ele if first else chain.from_iterable(ele))( map( lambda r: r.html.find( selector, containing=containing, clean=clean, first=first, _encoding=_encoding, ), _res, ) ) ) ) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_res", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Res", ), ), ) return wrapper return decorator def xpath( selector: str, *, clean: bool = False, first: bool = False, _encoding: str = None, ) -> Callable[[EFunc], EFunc]: """Css response.""" def decorator(func: EFunc) -> EFunc: """Decorate function func.""" @wraps(func) def wrapper(_res: Union[Res, Reses]) -> Union[Ele, Eles]: """Wrap function func.""" return func( _res.html.xpath( selector, clean=clean, first=first, _encoding=_encoding ) if isinstance(_res, Res) else (lambda ele: ele if first else chain.from_iterable(ele))( map( lambda r: r.html.xpath( selector, clean=clean, first=first, _encoding=_encoding, ), _res, ) ) ) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_res", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Res", ), ), ) return wrapper return decorator def html(func: SFunc) -> SFunc: """Get html from response.""" @wraps(func) def wrapper(_res: Union[Res, Reses]) -> Union[HTML, Iterable[HTML]]: """Wrap function func.""" return ( func(_res.html) if isinstance(_res, Res) else map(lambda r: r.html, _res) ) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_res", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Res", ), ), ) return wrapper def json( func: Callable[..., Union[Json, Iterable[Json]]] ) -> Callable[[Union[Res, Reses]], Union[Json, Iterable[Json]]]: """Get json from response.""" @wraps(func) def wrapper(_res: Union[Res, Reses]) -> Union[Json, Iterable[Json]]: """Wrap function func.""" return ( func(_res.json()) if isinstance(_res, Res) else map(lambda r: r.json(), _res) ) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_res", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Res", ), ), ) return wrapper def text(func: SFunc) -> SFunc: """Get text from response or element(s) from css/xpath.""" @wraps(func) def wrapper(_ele: Union[Res, Ele, Eles]) -> Union[str, Iterable[str]]: """Wrap function func.""" return func( _ele.text if isinstance(_ele, Ele) or isinstance(_ele, Res) else map(lambda e: e.text, _ele) ) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_ele", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Union[Res, Ele, Eles]", ), ), ) return wrapper PK!h  nacf/tools.py#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Life's pathetic, have fun ("▔□▔)/hi~♡ Nasy. Excited without bugs:: | * * | . . | . | * , | . | | * | |\___/| | ) -( . · | =\ - /= | )===( * | / - \ | |- | | / - \ 0.|.0 | NASY___\__( (__/_____(\=/)__+1s____________ | ______|____) )______|______|______|______|_ | ___|______( (____|______|______|______|____ | ______|____\_|______|______|______|______|_ | ___|______|______|______|______|______|____ | ______|______|______|______|______|______|_ | ___|______|______|______|______|______|____ author : Nasy https://nasy.moe date : Dec 23, 2018 email : Nasy filename : tools.py project : nacf license : GPL-3.0+ There are more things in heaven and earth, Horatio, than are dreamt. -- From "Hamlet" """ # Standard Library from inspect import Parameter, Signature, signature # Prelude from nalude import flatten # Local Packages from .types import Url, Func, Optional def format_url(url: Optional[Url]) -> Optional[Url]: """Add schema to url.""" return ( url if url and url.startswith("http") else url and ("https://" + url) ) def generate_sig(func: Func, *params: Parameter) -> Signature: """Generate signature with param from func.""" return signature(func).replace( parameters=list(signature(func).parameters.values()) + list(params) ) __all__ = ["format_url", "flatten"] PK!V nacf/types.py#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Life's pathetic, have fun ("▔□▔)/hi~♡ Nasy. Excited without bugs:: | * * | . . | . | * , | . | | * | |\___/| | ) -( . · | =\ - /= | )===( * | / - \ | |- | | / - \ 0.|.0 | NASY___\__( (__/_____(\=/)__+1s____________ | ______|____) )______|______|______|______|_ | ___|______( (____|______|______|______|____ | ______|____\_|______|______|______|______|_ | ___|______|______|______|______|______|____ | ______|______|______|______|______|______|_ | ___|______|______|______|______|______|____ author : Nasy https://nasy.moe date : Dec 23, 2018 email : Nasy filename : types.py project : nacf license : GPL-3.0+ There are more things in heaven and earth, Horatio, than are dreamt. -- From "Hamlet" """ # Standard Library from typing import (IO, Dict, List, Tuple, Union, TypeVar, Callable, Iterable, Optional, ByteString,) # Web Packages from requests_html import HTML from requests_html import Element as Ele from requests_html import HTMLResponse as Res # Normal a = TypeVar("a") b = TypeVar("b") # function RT = TypeVar("RT") Func = Callable[..., RT] Reses = Iterable[Res] RFunc = Callable[..., Res] RsFunc = Callable[..., Reses] Eles = Iterable[Ele] EFunc = Callable[..., Union[Ele, Eles]] SFunc = Callable[..., Union[str, Iterable[str]]] # request Data = Optional[Union[Dict[str, a], ByteString, IO]] Json = Dict[str, a] Url = str Urls = Iterable[Url] __all__ = [ "Res", "Reses", "HTML", "a", "b", "RT", "Func", "RFunc", "RsFunc", "SFunc", "Data", "Json", "Url", "Urls", "List", "Tuple", ] PK!DE;E; poetry.lock[[package]] category = "main" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." name = "appdirs" optional = false python-versions = "*" version = "1.4.3" [[package]] category = "dev" description = "Atomic file writes." name = "atomicwrites" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.3.0" [[package]] category = "dev" description = "Classes Without Boilerplate" name = "attrs" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "19.1.0" [package.extras] dev = ["coverage", "hypothesis", "pympler", "pytest", "six", "zope.interface", "sphinx", "pre-commit"] docs = ["sphinx", "zope.interface"] tests = ["coverage", "hypothesis", "pympler", "pytest", "six", "zope.interface"] [[package]] category = "main" description = "Screen-scraping library" name = "beautifulsoup4" optional = false python-versions = "*" version = "4.7.1" [package.dependencies] soupsieve = ">=1.2" [package.extras] html5lib = ["html5lib"] lxml = ["lxml"] [[package]] category = "main" description = "Dummy package for Beautiful Soup" name = "bs4" optional = false python-versions = "*" version = "0.0.1" [package.dependencies] beautifulsoup4 = "*" [[package]] category = "main" description = "Python package for providing Mozilla's CA Bundle." name = "certifi" optional = false python-versions = "*" version = "2019.3.9" [[package]] category = "main" description = "Universal encoding detector for Python 2 and 3" name = "chardet" optional = false python-versions = "*" version = "3.0.4" [[package]] category = "dev" description = "Cross-platform colored terminal text." marker = "sys_platform == \"win32\"" name = "colorama" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.4.1" [[package]] category = "main" description = "cssselect parses CSS3 Selectors and translates them to XPath 1.0" name = "cssselect" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.0.3" [[package]] category = "main" description = "Up to date simple useragent faker with real world database" name = "fake-useragent" optional = false python-versions = "*" version = "0.1.11" [[package]] category = "dev" description = "A library for property based testing" name = "hypothesis" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "3.88.3" [package.dependencies] attrs = ">=16.0.0" [package.extras] all = ["Faker (>=0.7)", "Faker (>=0.7)", "django (>=1.11)", "dpcontracts (>=0.4)", "numpy (>=1.9.0)", "pandas (>=0.19)", "pytest (>=3.0)", "python-dateutil (>=1.4)", "pytz", "pytz (>=2014.1)", "pytz (>=2014.1)"] datetime = ["pytz (>=2014.1)"] dateutil = ["python-dateutil (>=1.4)"] django = ["pytz", "django (>=1.11)"] dpcontracts = ["dpcontracts (>=0.4)"] fakefactory = ["Faker (>=0.7)"] faker = ["Faker (>=0.7)"] numpy = ["numpy (>=1.9.0)"] pandas = ["pandas (>=0.19)"] pytest = ["pytest (>=3.0)"] pytz = ["pytz (>=2014.1)"] [[package]] category = "main" description = "Internationalized Domain Names in Applications (IDNA)" name = "idna" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.8" [[package]] category = "main" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." name = "lxml" optional = false python-versions = "*" version = "4.3.2" [package.extras] cssselect = ["cssselect (>=0.7)"] html5 = ["html5lib"] htmlsoup = ["beautifulsoup4"] source = ["Cython (>=0.29.1)"] [[package]] category = "dev" description = "More routines for operating on iterables, beyond itertools" name = "more-itertools" optional = false python-versions = ">=3.4" version = "6.0.0" [[package]] category = "main" description = "A standard module. Inspired by Haskell's Prelude." name = "nalude" optional = false python-versions = ">=3.6,<4.0" version = "0.3.0" [[package]] category = "main" description = "parse() is the opposite of format()" name = "parse" optional = false python-versions = "*" version = "1.11.1" [[package]] category = "dev" description = "plugin and hook calling mechanisms for python" name = "pluggy" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.9.0" [package.extras] dev = ["pre-commit", "tox"] [[package]] category = "dev" description = "library with cross-python path, ini-parsing, io, code, log facilities" name = "py" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.8.0" [[package]] category = "main" description = "A port of node.js's EventEmitter to python." name = "pyee" optional = false python-versions = "*" version = "5.0.0" [[package]] category = "main" description = "Headless chrome/chromium automation library (unofficial port of puppeteer)" name = "pyppeteer" optional = false python-versions = ">=3.5" version = "0.0.25" [package.dependencies] appdirs = "*" pyee = "*" tqdm = "*" urllib3 = "*" websockets = "*" [[package]] category = "main" description = "A jquery-like library for python" name = "pyquery" optional = false python-versions = "*" version = "1.4.0" [package.dependencies] cssselect = ">0.7.9" lxml = ">=2.1" [[package]] category = "dev" description = "pytest: simple powerful testing with Python" name = "pytest" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "3.10.1" [package.dependencies] atomicwrites = ">=1.0" attrs = ">=17.4.0" colorama = "*" more-itertools = ">=4.0.0" pluggy = ">=0.7" py = ">=1.5.0" setuptools = "*" six = ">=1.10.0" [[package]] category = "main" description = "Python HTTP for Humans." name = "requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.21.0" [package.dependencies] certifi = ">=2017.4.17" chardet = ">=3.0.2,<3.1.0" idna = ">=2.5,<2.9" urllib3 = ">=1.21.1,<1.25" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] [[package]] category = "main" description = "HTML Parsing for Humans." name = "requests-html" optional = false python-versions = ">=3.6.0" version = "0.10.0" [package.dependencies] bs4 = "*" fake-useragent = "*" parse = "*" pyppeteer = ">=0.0.14" pyquery = "*" requests = "*" w3lib = "*" [[package]] category = "main" description = "Python 2 and 3 compatibility utilities" name = "six" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" version = "1.12.0" [[package]] category = "main" description = "A CSS4 selector implementation for Beautiful Soup." name = "soupsieve" optional = false python-versions = "*" version = "1.8" [[package]] category = "main" description = "Fast, Extensible Progress Meter" name = "tqdm" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" version = "4.31.1" [package.extras] dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"] [[package]] category = "main" description = "HTTP library with thread-safe connection pooling, file post, and more." name = "urllib3" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" version = "1.24.1" [package.extras] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] [[package]] category = "main" description = "Library of web-related functions" name = "w3lib" optional = false python-versions = "*" version = "1.20.0" [package.dependencies] six = ">=1.4.1" [[package]] category = "main" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" name = "websockets" optional = false python-versions = ">=3.4" version = "7.0" [metadata] content-hash = "c6ee818443d78a3370b8818ac7f523a7e4d98d49d48534965c51ff977076aa47" python-versions = "^3.6" [metadata.hashes] appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"] atomicwrites = ["03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", "75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"] attrs = ["69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", "f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"] beautifulsoup4 = ["034740f6cb549b4e932ae1ab975581e6103ac8f942200a0e9759065984391858", "945065979fb8529dd2f37dbb58f00b661bdbcbebf954f93b32fdf5263ef35348", "ba6d5c59906a85ac23dadfe5c88deaf3e179ef565f4898671253e50a78680718"] bs4 = ["36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"] certifi = ["59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", "b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"] chardet = ["84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"] colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] cssselect = ["066d8bc5229af09617e24b3ca4d52f1f9092d9e061931f4184cd572885c23204", "3b5103e8789da9e936a68d993b70df732d06b8bb9a337a05ed4eb52c17ef7206"] fake-useragent = ["c104998b750eb097eefc28ae28e92d66397598d2cf41a31aa45d5559ef1adf35"] hypothesis = ["21000d6c13bc7ad360f416eade493a06f2632c9c2a732e493779f8d51c08f031", "3b1aa7d49bc5f69948425d4a55b5edf4c75d79cbac72b47ab3061f55ad727b74", "76bfc9838d0b589fbc25232f46ec8ae17504f013b25d8221aa03d5368413016c"] idna = ["c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", "ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"] lxml = ["0358b9e9642bc7d39aac5cffe9884a99a5ca68e5e2c1b89e570ed60da9139908", "091a359c4dafebbecd3959d9013f1b896b5371859165e4e50b01607a98d9e3e2", "1998e4e60603c64bcc35af61b4331ab3af087457900d3980e18d190e17c3a697", "2000b4088dee9a41f459fddaf6609bba48a435ce6374bb254c5ccdaa8928c5ba", "2afb0064780d8aaf165875be5898c1866766e56175714fa5f9d055433e92d41d", "2d8f1d9334a4e3ff176d096c14ded3100547d73440683567d85b8842a53180bb", "2e38db22f6a3199fd63675e1b4bd795d676d906869047398f29f38ca55cb453a", "3181f84649c1a1ca62b19ddf28436b1b2cb05ae6c7d2628f33872e713994c364", "37462170dfd88af8431d04de6b236e6e9c06cda71e2ca26d88ef2332fd2a5237", "3a9d8521c89bf6f2a929c3d12ad3ad7392c774c327ea809fd08a13be6b3bc05f", "3d0bbd2e1a28b4429f24fd63a122a450ce9edb7a8063d070790092d7343a1aa4", "483d60585ce3ee71929cea70949059f83850fa5e12deb9c094ed1c8c2ec73cbd", "4888be27d5cba55ce94209baef5bcd7bbd7314a3d17021a5fc10000b3a5f737d", "64b0d62e4209170a2a0c404c446ab83b941a0003e96604d2e4f4cb735f8a2254", "68010900898fdf139ac08549c4dba8206c584070a960ffc530aebf0c6f2794ef", "872ecb066de602a0099db98bd9e57f4cfc1d62f6093d94460c787737aa08f39e", "88a32b03f2e4cd0e63f154cac76724709f40b3fc2f30139eb5d6f900521b44ed", "b1dc7683da4e67ab2bebf266afa68098d681ae02ce570f0d1117312273d2b2ac", "b29e27ce9371810250cb1528a771d047a9c7b0f79630dc7dc5815ff828f4273b", "ce197559596370d985f1ce6b7051b52126849d8159040293bf8b98cb2b3e1f78", "d45cf6daaf22584eff2175f48f82c4aa24d8e72a44913c5aff801819bb73d11f", "e2ff9496322b2ce947ba4a7a5eb048158de9d6f3fe9efce29f1e8dd6878561e6", "f7b979518ec1f294a41a707c007d54d0f3b3e1fd15d5b26b7e99b62b10d9a72e", "f9c7268e9d16e34e50f8246c4f24cf7353764affd2bc971f0379514c246e3f6b", "f9c839806089d79de588ee1dde2dae05dc1156d3355dfeb2b51fde84d9c960ad", "ff962953e2389226adc4d355e34a98b0b800984399153c6678f2367b11b4d4b8"] more-itertools = ["0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40", "590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1"] nalude = ["4da2bade4488c6e6c7934e4321ed4f4d75964174c33a79d8417aaa1192c4edf4", "5eb73cf683d1c278592bf25f134526ddd5705246c0c73e068ec9770ad25ba833"] parse = ["870dd675c1ee8951db3e29b81ebe44fd131e3eb8c03a79483a58ea574f3145c2"] pluggy = ["19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f", "84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746"] py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"] pyee = ["47f8fa96d6dee61c82001831e1fbba55f3f808003a322d0e6653aa01c59f6b9e", "4ec22817297b7024f89721cc34f790ee2767c5b5ca44284c565ee643abafbe32"] pyppeteer = ["51fe769b722a1718043b74d12c20420f29e0dd9eeea2b66652b7f93a9ad465dd"] pyquery = ["07987c2ed2aed5cba29ff18af95e56e9eb04a2249f42ce47bddfb37f487229a3", "4771db76bd14352eba006463656aef990a0147a0eeaf094725097acfa90442bf"] pytest = ["3f193df1cfe1d1609d4c583838bea3d532b18d6160fd3f55c9447fdca30848ec", "e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"] requests = ["502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", "7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"] requests-html = ["7e929ecfed95fb1d0994bb368295d6d7c4d06b03fcb900c33d7d0b17e6003947", "cb8a78cf829c4eca9d6233f28524f65dd2bfaafb4bdbbc407f0a0b8f487df6e2"] six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] soupsieve = ["afa56bf14907bb09403e5d15fbed6275caa4174d36b975226e3b67a3bb6e2c4b", "eaed742b48b1f3e2d45ba6f79401b2ed5dc33b2123dfe216adb90d4bfa0ade26"] tqdm = ["d385c95361699e5cf7622485d9b9eae2d4864b21cd5a2374a9c381ffed701021", "e22977e3ebe961f72362f6ddfb9197cc531c9737aaf5f607ef09740c849ecd05"] urllib3 = ["61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", "de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"] w3lib = ["67eacd24f4f233a80bc472835ad6aad99616e49aa1d795f3dbb7b8b028fec6cf", "e54db912ae1c79d3e246b16d3645bdf701f70ac4f8a2fc46eeb284cc02a31cd7"] websockets = ["04b42a1b57096ffa5627d6a78ea1ff7fad3bc2c0331ffc17bc32a4024da7fea0", "08e3c3e0535befa4f0c4443824496c03ecc25062debbcf895874f8a0b4c97c9f", "10d89d4326045bf5e15e83e9867c85d686b612822e4d8f149cf4840aab5f46e0", "232fac8a1978fc1dead4b1c2fa27c7756750fb393eb4ac52f6bc87ba7242b2fa", "4bf4c8097440eff22bc78ec76fe2a865a6e658b6977a504679aaf08f02c121da", "51642ea3a00772d1e48fb0c492f0d3ae3b6474f34d20eca005a83f8c9c06c561", "55d86102282a636e195dad68aaaf85b81d0bef449d7e2ef2ff79ac450bb25d53", "564d2675682bd497b59907d2205031acbf7d3fadf8c763b689b9ede20300b215", "5d13bf5197a92149dc0badcc2b699267ff65a867029f465accfca8abab95f412", "5eda665f6789edb9b57b57a159b9c55482cbe5b046d7db458948370554b16439", "5edb2524d4032be4564c65dc4f9d01e79fe8fad5f966e5b552f4e5164fef0885", "79691794288bc51e2a3b8de2bc0272ca8355d0b8503077ea57c0716e840ebaef", "7fcc8681e9981b9b511cdee7c580d5b005f3bb86b65bde2188e04a29f1d63317", "8e447e05ec88b1b408a4c9cde85aa6f4b04f06aa874b9f0b8e8319faf51b1fee", "90ea6b3e7787620bb295a4ae050d2811c807d65b1486749414f78cfd6fb61489", "9e13239952694b8b831088431d15f771beace10edfcf9ef230cefea14f18508f", "d40f081187f7b54d7a99d8a5c782eaa4edc335a057aa54c85059272ed826dc09", "e1df1a58ed2468c7b7ce9a2f9752a32ad08eac2bcd56318625c3647c2cd2da6f", "e98d0cec437097f09c7834a11c69d79fe6241729b23f656cfc227e93294fc242", "f8d59627702d2ff27cb495ca1abdea8bd8d581de425c56e93bff6517134e0a9b", "fc30cdf2e949a2225b012a7911d1d031df3d23e99b7eda7dfc982dc4a860dae9"] PK!Hu)GTUnacf-1.0.2.dist-info/WHEEL HM K-*ϳR03rOK-J,/R(O-)$qzd&Y)r$UV&UrPK!HeTqDBnacf-1.0.2.dist-info/METADATAXo8be%vfhKӠI gJdn$QKRqPeqzac3ypH "Q!г]뚤t  ">wmYE5K8dP\<@$Ys BPOi''1 +q35/|;i7Cد_+L"݇NvYK :.ԜJѡ)aV_CPH >? &,m=؎u)Y(0çK8si?\QA)5cPq Q ]F=,+zUcV_8hpM[?xHxL+ "="^)d\)J?tRGߺI4\v1o@u?_3͹de[yJe XuL}eW4y"_ /\d7w$x@on4<94w.EJCFސRAIzFyZX4r#oTQՔIQou}å?;+b0O59 $*v$x ,9:߾!u/ F@ "^ዠ:.d6,b T3^0AX$Tv+-B= &A"Ǻ&Pj֛P9>]A#C,7!iY`u?s[ӓOguZ&mM$ݼsCzx7zo?yx3dm1S-(@o 3=@JG ~̤=M &^s,Ո*~HtwV\~z,nj Qx]VrLrܠ2\GoߞEo>ormG"uv->q6es1sx˂jsx5-Xg[pmIww7:硄YL S:j'W= a6QLZ҆-G\h!qרa)[[]W!y}VBnU2 tmY3 %mכkɺ&FPji'sf +ekVhf|ΨWM62p+:^paỠI_H*Do6<íL_ᒃ;h8,9d3kԎ!o8䄧)ŅWWտs&]A߿ɂ?PK!H nacf-1.0.2.dist-info/RECORDmI@@ѽg q A 7LɐxڈꈺdtTl,b~h&#KZme W@J5VVqzic[Ф6ïP?!^ Xn}ʠO&}0U'׏(7EDd^5卩1'd#GHreH j`7a~x8Tb@)Qª;B6@al!#M0 cՌW)iEIԕkgeߕƅd{A8q;i Wh!(BΙ 1U34,D mozQ#!'K,{bG_CSIDz~k|=Rĵ驶8N x|Ӻ. A3bFL`-Kɗf /p;ʍôsfvnYˉϪ`7PK!lQ CHANGELOGPK!Ђ,, @README.orgPK!v" nacf/__init__.pyPK!un::nacf/models.pyPK!h  Jnacf/tools.pyPK!V Qnacf/types.pyPK!DE;E; Ypoetry.lockPK!Hu)GTU nacf-1.0.2.dist-info/WHEELPK!HeTqDBnacf-1.0.2.dist-info/METADATAPK!H nacf-1.0.2.dist-info/RECORDPK t.