Metadata-Version: 2.4
Name: pafer
Version: 0.9.7
Summary: Regex-based HTML parsing.
Author-email: werson <werson.tech@gmail.com>
License: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: User Interfaces
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Dynamic: license-file

![Python](https://img.shields.io/badge/python-3.8%2B-blue)
![Dependencies](https://img.shields.io/badge/dependencies-none-success)
![Tests](https://img.shields.io/badge/tests-pytest-informational)

## Features

- Parse HTML into structured tag / text / metadata results
- CSS-style selectors (`#id`, `.class`, tag name)
- Whitelist-based sanitization
- Minification with comment stripping
- Link and table extraction
- LRU result caching with TTL
- RSS 2.0 feed parsing
- JSON / CSV export
- CLI tool
- Standard library only, no third-party dependencies

## Installation

```
pip install pafer
```

## Quickstart

```python
from pafer import HTMLParser

html = '<div class="post"><h1>Title</h1><a href="/about">About</a></div>'

p = HTMLParser()
result = p.parse(html)

result['tags']        # list of {name, attributes, content, rows?}
result['metadata']    # {'title': ...}
result['doctype']     # '<!DOCTYPE ...>' or None

p.select(html, '.post')    # match by #id, .class or tag name
p.sanitize(html)           # keep only allowed tags
p.minify(html)             # collapse whitespace, strip comments
p.extract_links(html)      # [{'url': ..., 'text': ..., 'attributes': ...}]
```

Enable the parsed-result cache with `HTMLParser(use_cache=True)`.

## Configuration

```python
from pafer import Config, load_config, save_config

cfg = Config({'strip_whitespace': False, 'cache_size': 100})
save_config(cfg, 'my.pkl')
cfg = load_config('my.pkl')
```

### Default settings

| Key | Default | Purpose |
|---|---|---|
| `parse_tables` | `True` | extract table rows while parsing |
| `strip_whitespace` | `True` | collapse whitespace |
| `encoding` | `utf-8` | decode bytes input |
| `max_depth` | `100` | maximum element nesting |
| `default_tag` | `div` | fallback tag |
| `preserve_comments` | `False` | keep comments in the tree |
| `strict_mode` | `False` | raise on malformed HTML |
| `remove_comments` | `True` | strip comments when minifying |
| `allowed_tags` | `p, br, b, i, a, ul, ol, li, div, span` | sanitize whitelist |
| `max_file_size_mb` | `10` | reject oversized input |
| `cache_size` | `50` | cache capacity |

## Caching

`pafer.cache.LRUCache` is an in-memory LRU cache with TTL expiry:

```python
from pafer.cache import LRUCache

cache = LRUCache(max_size=100, ttl_seconds=300)
cache.set('key', value)
cache.get('key')
```

## RSS feeds

```python
from pafer.feed import FeedParser

items = FeedParser().parse_rss(xml_content)
items[0]['title']       # 'title', 'link', 'description', 'pubDate'
```

## Exporting

```python
from pafer.exporters import DataExporter

DataExporter.to_json(data)    # JSON string with 2-space indent
DataExporter.to_csv(rows)     # CSV from a list of rows
```

## CLI

```
python -m pafer.cli parse -f input.html --minify
python -m pafer.cli sanitize -f input.html
```

## Exceptions

`HTMLParserError` (base), `SanitizationError`, `SelectorSyntaxError`, `ConfigLoadError`.

## LICENSE

MIT - Enjoy

