PK!Onjj CHANGELOG* Version 0.1.0 + Date :: <2018-12-23 Sun> + Commemorate Version :: First Version - Basic Functions. PK!'H& 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. | * Development Process #+include: "./todo.org" * Epoligue ** History #+include: "./CHANGELOG" PK!<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__ = "0.1.0" PK!a2222nacf/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 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, Ele, Res, Url, Data, Eles, Func, Json, List, Urls, EFunc, Reses, RFunc, SFunc, Tuple, Union, 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( "_urls", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Urls", ), 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: Optional[Url] = None, *urls: Url, first: bool = False, **kwds: a ) -> Callable[[RFunc], RFunc]: """Get url.""" def decorator(func: RFunc) -> RFunc: """Decorate function func.""" @wraps(func) def wrapper( _url: Url = None, *, _urls: Optional[Urls] = None, _s: Optional[s] = None, ) -> Union[Res, Reses]: """Wrap function func.""" return func( (lambda res: first and next(chain(res, (None,))) or res)( map( lambda u: (_s or s()).get(u, **kwds), map( format_url, filter( bool, chain((url,), urls, (_url,), _urls or []) ), ), ) ) ) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_url", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Optional[Url]", ), Parameter( "_urls", Parameter.KEYWORD_ONLY, 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, *urls: Url, data: Data = None, json: Json = None, first: bool = False, **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: Data = None, _json: Json = None, *, _urls: Optional[Urls] = None, _s: Optional[s] = None, **_kwds: b, ) -> Union[Res, Reses]: """Wrap function func.""" return func( (lambda res: first and next(chain(res, (None,))) or res)( map( lambda u: (_s or s()).post( u, data=_data or data, json=_json or json, **kwds, **_kwds, ), map( format_url, filter( bool, chain((url,), urls, (_url,), _urls or []) ), ), ) ) ) 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( "_urls", Parameter.KEYWORD_ONLY, default=None, annotation="Optional[Urls]", ), 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( ( lambda eles: eles if first or isinstance(_res, Res) else chain.from_iterable(eles) )( _res.html.find( selector, containing=containing, clean=clean, first=first, _encoding=_encoding, ) if isinstance(_res, Res) else 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="Union[Res, Reses]", ), ), ) 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( ( lambda eles: eles if first or isinstance(_res, Res) else chain.from_iterable(eles) )( _res.html.xpath( selector, clean=clean, first=first, _encoding=_encoding ) if isinstance(_res, Res) else map( lambda r: r.html.find( selector, clean=clean, first=first, _encoding=_encoding, ), _res, ) ) ) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_res", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Union[Res, Reses]", ), ), ) return wrapper return decorator def text(func: SFunc) -> SFunc: """Get text from response of css/xpath.""" @wraps(func) def wrapper(_ele: Union[Ele, Eles]) -> Union[str, Iterable[str], None]: """Wrap function func.""" return func( _ele.text if isinstance(_ele, Ele) else map(lambda e: e.text, _ele) ) setattr( wrapper, "__signature__", generate_sig( func, Parameter( "_ele", Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation="Union[Ele, Eles]", ), ), ) return wrapper if __name__ == "__main__": import time @urls(["python.org", "python.org", "python.org", "python.org"]) @parallel(4) @get() @css(".widget-title") @text def crawler(res: Iterable[str]) -> None: """Crawler.""" for r in res: print(r) @urls(["python.org", "python.org", "python.org", "python.org"]) # @parallel(1) @get() @css(".widget-title") @text def crawler2(res: Iterable[str]) -> None: """Crawler.""" for r in res: print(r) print("with parallel 4") pt = time.time() crawler() print("with parallel:", time.time() - pt) t = time.time() print("without parallel") crawler2() print("without parallel", time.time() - t) PK!hx 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 # Local Packages from .types import RT, Url, Func, Optional, a, b 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"] PK!~  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, overload, ) # Web Packages 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[..., Union[Res, Reses]] Eles = Iterable[Ele] EFunc = Callable[..., Union[Ele, Eles]] SFunc = Callable[..., Union[str, Iterable[str], None]] # request Data = Optional[Union[Dict[str, a], ByteString, IO]] Json = Dict[str, a] Url = str Urls = List[Url] __all__ = [ "Res", "Reses", "a", "b", "RT", "Func", "RFunc", "SFunc", "Data", "Json", "Url", "Urls", ] PK!O4O4 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.2.1" [[package]] category = "dev" description = "Classes Without Boilerplate" name = "attrs" optional = false python-versions = "*" version = "18.2.0" [[package]] category = "main" description = "Screen-scraping library" name = "beautifulsoup4" optional = false python-versions = "*" version = "4.6.3" [[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 = "2018.11.29" [[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.84.5" [package.dependencies] attrs = ">=16.0.0" [[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.2.5" [[package]] category = "dev" description = "More routines for operating on iterables, beyond itertools" name = "more-itertools" optional = false python-versions = "*" version = "4.3.0" [package.dependencies] six = ">=1.0.0,<2.0.0" [[package]] category = "main" description = "parse() is the opposite of format()" name = "parse" optional = false python-versions = "*" version = "1.9.0" [[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.8.0" [[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.7.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]] category = "main" description = "HTML Parsing for Humans." name = "requests-html" optional = false python-versions = ">=3.6.0" version = "0.9.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 = "Fast, Extensible Progress Meter" name = "tqdm" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" version = "4.28.1" [[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]] category = "main" description = "Library of web-related functions" name = "w3lib" optional = false python-versions = "*" version = "1.19.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 = "df352ceba25f02da83764c4cdcf9cf7e8b1af698916b9dc618c21a2ded68fa16" python-versions = "^3.7" [metadata.hashes] appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"] atomicwrites = ["0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", "ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"] attrs = ["10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", "ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"] beautifulsoup4 = ["194ec62a25438adcb3fdb06378b26559eda1ea8a747367d34c33cef9c7f48d57", "90f8e61121d6ae58362ce3bed8cd997efb00c914eae0ff3d363c32f9a9822d10", "f0abd31228055d698bb392a826528ea08ebb9959e6bea17c606fd9c9009db938"] bs4 = ["36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"] certifi = ["47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7", "993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"] chardet = ["84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"] colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] cssselect = ["066d8bc5229af09617e24b3ca4d52f1f9092d9e061931f4184cd572885c23204", "3b5103e8789da9e936a68d993b70df732d06b8bb9a337a05ed4eb52c17ef7206"] fake-useragent = ["c104998b750eb097eefc28ae28e92d66397598d2cf41a31aa45d5559ef1adf35"] hypothesis = ["22ce150601845c55a0c075da37b3725f5746e398ab4213d1d66553fd3782f905", "a04407a8ab27d32608c4b5005953c4b1b018e7b09c7bc2e65805785014ce4bef", "c206a6b785da5beef329cb68051ed7ec44d21be4bce7df312becb77a081ad982"] idna = ["c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", "ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"] lxml = ["02bc220d61f46e9b9d5a53c361ef95e9f5e1d27171cd461dddb17677ae2289a5", "22f253b542a342755f6cfc047fe4d3a296515cf9b542bc6e261af45a80b8caf6", "2f31145c7ff665b330919bfa44aacd3a0211a76ca7e7b441039d2a0b0451e415", "36720698c29e7a9626a0dc802ef8885f8f0239bfd1689628ecd459a061f2807f", "438a1b0203545521f6616132bfe0f4bca86f8a401364008b30e2b26ec408ce85", "4815892904c336bbaf73dafd54f45f69f4021c22b5bad7332176bbf4fb830568", "5be031b0f15ad63910d8e5038b489d95a79929513b3634ad4babf77100602588", "5c93ae37c3c588e829b037fdfbd64a6e40c901d3f93f7beed6d724c44829a3ad", "60842230678674cdac4a1cf0f707ef12d75b9a4fc4a565add4f710b5fcf185d5", "62939a8bb6758d1bf923aa1c13f0bcfa9bf5b2fc0f5fa917a6e25db5fe0cfa4e", "75830c06a62fe7b8fe3bbb5f269f0b308f19f3949ac81cfd40062f47c1455faf", "81992565b74332c7c1aff6a913a3e906771aa81c9d0c68c68113cffcae45bc53", "8c892fb0ee52c594d9a7751c7d7356056a9682674b92cc1c4dc968ff0f30c52f", "9d862e3cf4fc1f2837dedce9c42269c8c76d027e49820a548ac89fdcee1e361f", "a623965c086a6e91bb703d4da62dabe59fe88888e82c4117d544e11fd74835d6", "a7783ab7f6a508b0510490cef9f857b763d796ba7476d9703f89722928d1e113", "aab09fbe8abfa3b9ce62aaf45aca2d28726b1b9ee44871dbe644050a2fff4940", "abf181934ac3ef193832fb973fd7f6149b5c531903c2ec0f1220941d73eee601", "ae07fa0c115733fce1e9da96a3ac3fa24801742ca17e917e0c79d63a01eeb843", "b9c78242219f674ab645ec571c9a95d70f381319a23911941cd2358a8e0521cf", "bccb267678b870d9782c3b44d0cefe3ba0e329f9af8c946d32bf3778e7a4f271", "c4df4d27f4c93b2cef74579f00b1d3a31a929c7d8023f870c4b476f03a274db4", "caf0e50b546bb60dfa99bb18dfa6748458a83131ecdceaf5c071d74907e7e78a", "d3266bd3ac59ac4edcd5fa75165dee80b94a3e5c91049df5f7c057ccf097551c", "db0d213987bcd4e6d41710fb4532b22315b0d8fb439ff901782234456556aed1", "dbbd5cf7690a40a9f0a9325ab480d0fccf46d16b378eefc08e195d84299bfae1", "e16e07a0ec3a75b5ee61f2b1003c35696738f937dc8148fbda9fe2147ccb6e61", "e175a006725c7faadbe69e791877d09936c0ef2cf49d01b60a6c1efcb0e8be6f", "edd9c13a97f6550f9da2236126bb51c092b3b1ce6187f2bd966533ad794bbb5e", "fa39ea60d527fbdd94215b5e5552f1c6a912624521093f1384a491a8ad89ad8b"] more-itertools = ["c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092", "c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e", "fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"] parse = ["9dd6048ea212cd032a342f9f6aa2b7bc222f7407c7e37bdc2777fecd36897437"] pluggy = ["447ba94990e8014ee25ec853339faf7b0fc8050cdc3289d4d71f7f410fb90095", "bde19360a8ec4dfd8a20dcb811780a30998101f078fc7ded6162f0076f50508f"] py = ["bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694", "e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6"] pyee = ["47f8fa96d6dee61c82001831e1fbba55f3f808003a322d0e6653aa01c59f6b9e", "4ec22817297b7024f89721cc34f790ee2767c5b5ca44284c565ee643abafbe32"] pyppeteer = ["51fe769b722a1718043b74d12c20420f29e0dd9eeea2b66652b7f93a9ad465dd"] pyquery = ["07987c2ed2aed5cba29ff18af95e56e9eb04a2249f42ce47bddfb37f487229a3", "4771db76bd14352eba006463656aef990a0147a0eeaf094725097acfa90442bf"] pytest = ["3f193df1cfe1d1609d4c583838bea3d532b18d6160fd3f55c9447fdca30848ec", "e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"] requests = ["502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", "7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"] requests-html = ["34257d5249b20b8ed14573eba910f48032a61205e70d11ce8a3ef6abf8edc50b", "9686f21c5753ba6c025c6ba223a8329c7b149a935a73055097faf8999eee85b1"] six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] tqdm = ["3c4d4a5a41ef162dd61f1edb86b0e1c7859054ab656b2e7c7b77e7fbf6d9f392", "5b4d5549984503050883bc126280b386f5f4ca87e6c023c5d015655ad75bdebb"] urllib3 = ["61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", "de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"] w3lib = ["55994787e93b411c2d659068b51b9998d9d0c05e0df188e6daf8f45836e1ea38", "aaf7362464532b1036ab0092e2eee78e8fd7b56787baa9ed4967457b083d011b"] websockets = ["04b42a1b57096ffa5627d6a78ea1ff7fad3bc2c0331ffc17bc32a4024da7fea0", "08e3c3e0535befa4f0c4443824496c03ecc25062debbcf895874f8a0b4c97c9f", "10d89d4326045bf5e15e83e9867c85d686b612822e4d8f149cf4840aab5f46e0", "232fac8a1978fc1dead4b1c2fa27c7756750fb393eb4ac52f6bc87ba7242b2fa", "4bf4c8097440eff22bc78ec76fe2a865a6e658b6977a504679aaf08f02c121da", "51642ea3a00772d1e48fb0c492f0d3ae3b6474f34d20eca005a83f8c9c06c561", "55d86102282a636e195dad68aaaf85b81d0bef449d7e2ef2ff79ac450bb25d53", "564d2675682bd497b59907d2205031acbf7d3fadf8c763b689b9ede20300b215", "5d13bf5197a92149dc0badcc2b699267ff65a867029f465accfca8abab95f412", "5eda665f6789edb9b57b57a159b9c55482cbe5b046d7db458948370554b16439", "5edb2524d4032be4564c65dc4f9d01e79fe8fad5f966e5b552f4e5164fef0885", "79691794288bc51e2a3b8de2bc0272ca8355d0b8503077ea57c0716e840ebaef", "7fcc8681e9981b9b511cdee7c580d5b005f3bb86b65bde2188e04a29f1d63317", "8e447e05ec88b1b408a4c9cde85aa6f4b04f06aa874b9f0b8e8319faf51b1fee", "90ea6b3e7787620bb295a4ae050d2811c807d65b1486749414f78cfd6fb61489", "9e13239952694b8b831088431d15f771beace10edfcf9ef230cefea14f18508f", "d40f081187f7b54d7a99d8a5c782eaa4edc335a057aa54c85059272ed826dc09", "e1df1a58ed2468c7b7ce9a2f9752a32ad08eac2bcd56318625c3647c2cd2da6f", "e98d0cec437097f09c7834a11c69d79fe6241729b23f656cfc227e93294fc242", "f8d59627702d2ff27cb495ca1abdea8bd8d581de425c56e93bff6517134e0a9b", "fc30cdf2e949a2225b012a7911d1d031df3d23e99b7eda7dfc982dc4a860dae9"] PK!HnHTUnacf-0.1.0.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK!H:4xNN nacf-0.1.0.dist-info/METADATAVYo6~ׯx %q.;47MEŢF$C$A&973+j1?Q*.l{]e8S^2˘ H6MQ¹$p/t5a2a%9"C` 9h]Ap=)/Y''11W$wq;v{3œs\ꉐ{p1c<5-c\(DHp;IR doLۑPM xáϐZ F.8g= L<9Ne2+* ZC>T?Jq%Ѥ$5֥"+9A҃2_Ka3#Չ2QOXs.^\ԻwLMQPK!Hsnacf-0.1.0.dist-info/RECORDmˎ@@ѽ6k1R @ Q^XK~&qLz2[Ά.'QJ\;w  _a[Pt6!ya[(] ^L:FP/W-fgQTТy= JC.WxV7r>48dXV7l`"zB'$/|(EoJk>fKH6ltLNfQI/(@Lu]Y"Y{huإ̳ g}$ՖM};+_nGVPٕO-<׫]s`^ya<)/h>t~ W#43.mф6Qu59U1AkD㴈gQ<lkArq텝[9'Y7PK!Onjj CHANGELOGPK!'H& README.orgPK!<Fnacf/__init__.pyPK!a2222 nacf/models.pyPK!hx _=nacf/tools.pyPK!~  ADnacf/types.pyPK!O4O4 Lpoetry.lockPK!HnHTUnacf-0.1.0.dist-info/WHEELPK!H:4xNN nacf-0.1.0.dist-info/METADATAPK!HsHnacf-0.1.0.dist-info/RECORDPK t`