india-trade-analysis/├── scraper/│ ├── __init__.py│ ├── __main__.py # python -m scraper entry point│ ├── config.py # 251 countries, 7 years, endpoints, form fields│ └── scrape.py # HTTP scraper using curl_cffi (no Selenium)├── data/│ ├── raw/ # for failed.json│ └── processed/ # exports.parquet, imports.parquet├── analysis/ # (empty, ready for notebooks)├── pyproject.toml # uv project, deps: curl-cffi, lxml, pandas, pyarrow└── .python-version # 3.13uv run python -m scraper.scrape --test # 3 countries × 2 years × exportuv run python -m scraper.scrape # full scrape (251 × 7 × 2 = 3,514 requests)uv run python -m scraper.scrape --retry # retry failures from failed.jsonS.No. | HSCode | Commodity | <prev_year> | <curr_year> | %Growthscraper/├── cli.py # Typer CLI — scrape, status, build commands├── scrape.py # Async orchestrator — worker pool + rich progress├── client.py # AsyncSession wrapper — CSRF, retries, rate limiting├── parser.py # HTML → dicts (pure function, no I/O)├── tasks.py # Task dataclass, generation, file-existence checks, CSV save├── config.py # Countries, years, endpoints, constants├── build.py # Raw CSVs → Parquet assembly└── __main__.py # Entry pointuv run python -m scraper scrape # full scrape (resume-safe)uv run python -m scraper scrape -c "U S A" -y 2024 # targeteduv run python -m scraper scrape -w 3 -d 2.0 # tune concurrencyuv run python -m scraper status # progress tableuv run python -m scraper build # assemble parquetdata/raw/export/hs2/2024/U_S_A.csv # was: data/raw/export/2024/U_S_A.csvdata/raw/export/hs4/2024/U_S_A.csvdata/processed/exports_hs2.parquet # was: data/processed/exports.parquetdata/processed/exports_hs4.parquetuv run python -m scraper scrape -hs 2 # just HS2 (default)uv run python -m scraper scrape -hs 2 -hs 4 # both HS2 and HS4uv run python -m scraper status -hs 2 -hs 4 # progress for bothuv run python -m scraper build -hs 4 # build only HS4 parquet