Make sure to read and follow the description of the project and the spirit of it from README.md. You are tasked with building a locally hosted tool that scrapes information off of the public Internet.
You should stick to using the following libraries for scraping.
- Use
zendriverfor web browser scraping. - Use
yt-dlpfor scraping videos and music content off sites like YouTube. - Use
aiohttpfor basic HTTP web scraping.
Configuration lives in config.toml and should never be committed. You must
never read the values from config.toml, only pass them on to internal
functions in code for running DANK.
The DANK system should be built as an asynchronous framework for scraping
information from sites with parallel processing, limiting requests to specific
sites to avoid hitting an individual host too often. Where possible when
using web scraping zendriver should connect to hosts and pull information from
the web requests the browser is sending instead of scraping HTML content on
the pages.
- Code in the
scrapemodule should only be concerned with scraping data. Only indank/scrape/runner.pyshould write to the ClickHouse database or to the filesystem for assets. Submodules should only be concerned with discovering posts and assets that need persisting. - Code in the
processmodule should only be concerned with loading data from the database, processing it, and storing it back again in a processed form. - Never import
scrapecode in theprocessmodule or vice versa. - Only
dank/scrape/runner.py,dank/process/runner.py, and the web app may access ClickHouse.
IMPORTANT: You are not allowed to just create new modules and import process
or scrape code in them to get around the rules of not importing scrape code
in to process code or process code into scrape code through a layer of
indirection.
- You are writing Python 3.12+ code. Never write simple helper functions
if there are very simple first party library functions like
itertools.batched. - Code lives in either
srcortestdirectories. - Use
asyncioas much as possible for great concurrency. - Never ever use a Python
dataclassif at all possible. - Use
NamedTuplefor abstract data types as much as possible to pass immutable data around. - Store original payloads of data scraped from sites as JSON in the database.
- Never use
os.environfor configuration in code, because all configuration should belong in the TOML configuration. - Use brief comments for non-obvious reasoning, constraints, and tradeoffs. Link to an authoritative source beside a borrowed formula or algorithm. Name tunable thresholds and distinguish project heuristics from constants defined by a standard; explain what those choices are intended to achieve.
- Separate calculations, I/O, and presentation where their responsibilities differ. Extract functions, components, or modules to make ownership clearer; keep abstractions proportional to the problem being solved.
- Implement tests, especially unit tests for data processing.
- When changing collection/processing failure handling, quality indicators, or
source-specific workarounds, update
docs/known-issues.mdwith the current behavior, remaining limits, and relevant evidence or tests. - Never use
importlibin test code for importing private functions, and instead import with# pyright: ignore[reportPrivateUsage]
- Run
./run-linters.shto run all linters to check for and autofix errors. - You can run tests with
uv run pytest. - You can scrape data with
uv run scrape.- Pass
--domainsto scrape only matchingsourcesdomains.
- Pass
- You can process scraped data with
uv run process.- You might want to pass
--age 10mto process content scraped in the past 10 minutes, other other ages accepted byparse_age_window.
- You might want to pass
make scrapeandmake processarchive timestamped output inlogs/scrape/andlogs/process/. Use these for recent-run comparisons; each log includes command, checkout revision, duration and exit status. Search these Git-ignored archives withrg --no-ignore.dank.logremains the combined application log.- Query with
uv run clickhouse-query -q "<QUERY>"- You can run only
SELECT,SHOW, orEXPLAINqueries. - Output is truncated by default. To see more information pass the
--fullargument.
- You can run only
These readability conventions apply to both Python and TypeScript.
Keep blank lines before new blocks of code, and keep blank lines after new
blocks of code, except when they are at the start or end of a level of
indentation, such as if blocks and associated elif and else blocks,
and try and associated catch blocks, including all loops, etc. Make sure
we never write blank lines before elif, else, catch etc as they should
be associated with the blocks. Put a blank line before a return unless it
starts a level of indentation.
Use PEP 636 pattern matching where appropriate to simplify code, especially where you have a complex series of nested if statements.
You have access to a local ClickHouse database during development default ClickHouse credentials on Port 9000, and you can use that for your operations.