Vishnug21/Data-Engineering-Slooze-TakeHomeChallenge
GitHub: Vishnug21/Data-Engineering-Slooze-TakeHomeChallenge
端到端 B2B 供应链智能数据管道,整合多平台采集、ETL 清洗、本地 LLM 数据增强与风险评估,并通过 Streamlit 仪表板提供可操作的采购洞察。
Stars: 0 | Forks: 0
# Slooze 供应链智能平台
**数据工程居家挑战题**
Vishnu Gopal · vishnu.gopal@skypoint.ai · [github.com/Vishnug21](https://github.com/Vishnug21)
## 概述
端到端供应链数据管道,从两个印度 B2B 市场抓取产品列表,将其统一为包含 584 行的数据集,存储在云数据库中,并通过交互式仪表板呈现具有可操作性的供应链洞察。
**在线仪表板:** https://data-engineering-slooze-takehomechallenge-4wxr8tkpljjd36mt7g24.streamlit.app
**在线数据库:** Supabase PostgreSQL · 584 行 · 142 列
## 核心发现
- **地理集中度风险:** Gujarat 占所有国内供应商的 38% —— 过度依赖单一邦会使得供应链在面对区域性中断时显得脆弱。
- **国际敞口风险:** 15% 已映射的供应商为国际供应商(Guangdong、Shanghai、Shandong、Singapore),表明存在跨境采购风险。
- **平台覆盖差距:** TradeIndia 每个产品提供的结构化规格数据比 IndiaMART 多 2.4 倍,使其成为更强大的采购情报来源。
- **数据质量差异:** TradeIndia 为 93% 的列表提供了 MOQ 数据,而 IndiaMART 为 0% —— 这是采购决策中的关键缺口。
## 管道架构
```
graph LR
A[IndiaMART\nPlaywright Scraper] --> C[ETL\nUnify Schema]
B[TradeIndia\nApify Cloud Actor] --> C
C --> D[combined_raw.csv\n584 rows · 138 cols]
D --> E[Supabase\nPostgreSQL]
D --> F[Analysis Modules]
F --> G[Risk Scores]
F --> H[Anomaly Flags]
F --> I[Quality Metrics]
E --> J[Streamlit Dashboard\n6 pages]
G --> J
H --> J
I --> J
```
## 数据源
| 来源 | 行数 | 方法 | 亮点 |
|---|---|---|---|
| IndiaMART | 236 | Playwright(无头模式,绕过反爬虫) | 产品名称、类别、来源追踪 |
| TradeIndia | 348 | Apify 云 actor | 100+ 规格字段、MOQ、价格、邦、业务类型 |
| **合计** | **584** | Schema 合并 + 来源标记 | **138 列** |
选择多数据源是经过深思熟虑的 —— 它支持跨平台供应商比较、价格差异分析以及单数据源抓取无法提供的数据质量基准测试。
## 项目结构
```
├── app.py # Streamlit dashboard (6 pages)
├── upload_to_supabase.py # Batch REST API uploader
├── requirements.txt
├── data/
│ ├── combined_raw.csv # Unified dataset (584 rows, 138 cols)
│ ├── indiamart_raw.csv # Raw IndiaMART scrape (236 rows)
│ └── tradeindia_raw.csv # Raw TradeIndia scrape (348 rows)
├── src/
│ ├── playwright_scraper.py # IndiaMART scraper (Playwright + anti-bot)
│ ├── tradeindia_scraper.py # TradeIndia via Apify API
│ ├── etl.py # Schema unification + source tagging
│ ├── supplier_risk_score.py # 4-factor supplier risk model
│ ├── data_quality_score.py # Field completeness analysis
│ ├── ai_enrichment.py # AI extraction via Qwen 2.5 7B (LM Studio)
│ ├── eda.py # EDA + 8 static charts
│ ├── scraper.py # requests + BS4 scraper (anti-blocking)
│ └── supabase_integration.py # Cloud DB sync
├── charts/ # 8 EDA visualisation charts (PNG)
└── tests/
└── test_etl.py # ETL pipeline tests
```
## 快速开始
```
pip install -r requirements.txt
streamlit run app.py
```
打开 `http://localhost:8501`
### 重新运行单个模块
```
# ETL pipeline
python src/etl.py
# EDA + 生成图表
python src/eda.py
# 风险评分
python src/supplier_risk_score.py
# 数据质量评估
python src/data_quality_score.py
```
### 重新抓取数据
```
# IndiaMART (需要 Playwright)
playwright install chromium
python src/playwright_scraper.py
# TradeIndia (需要 Apify API token)
python src/tradeindia_scraper.py
```
### 上传至 Supabase
```
python upload_to_supabase.py
```
## 仪表板页面
1. **概览** — 数据集摘要、来源分布、类别细分
2. **供应商地图** — 覆盖印度各邦及国际地区的地理气泡图,并带有集中度风险提示
3. **类别** — 按类别和来源划分的产品分布
4. **异常** — 数据质量标记、缺失字段率、按来源划分的完整度
5. **供应链情报** — 跨平台比较表
6. **质量报告** — 所有 138 列的字段级完整度
## AI 驱动的数据增强 (`src/ai_enrichment.py`)
使用 **通过 LM Studio 本地运行的 Qwen 2.5 7B** 来增强没有结构化规格数据的 IndiaMART 行。对于 236 个 IndiaMART 产品名称中的每一个,Qwen 会提取:
| 字段 | 示例输出 |
|---|---|
| `ai_product_type` | 服装、工业机械、纺织品 |
| `ai_material` | 棉、不锈钢、铜 |
| `ai_use_case` | 服装/时尚、建筑、电气 |
| `ai_business_role` | 制造商、出口商、贸易商、供应商 |
结果会写回 Supabase,并在仪表板的质量报告中作为前/后完整度对比显示。
运行方式(需要打开 LM Studio 并加载 Qwen 2.5 7B):
```
python src/ai_enrichment.py
```
**为什么使用本地模型而不是云 API:**
- **数据隐私** — B2B 供应商数据、定价和采购情报属于商业机密。将其发送到外部 API 意味着它可能会被记录、保留或用于训练。本地模型可将数据完全保留在本地。
- **零成本** — 没有按 token 收费。大规模使用云 API 处理 348 条产品记录费用会累积;而本地模型每次查询的边际成本为零。
- **无速率限制** — 云 API 会限制批量工作负载。本地模型可全速处理,不会触及配额。
- **可复现性** — 相同的模型版本始终产生相同的输出。云 API 模型会被静默更新,这可能会破坏依赖于一致提取行为的 pipeline。
## 分析模块
### 供应商风险评分 (`src/supplier_risk_score.py`)
四因子加权模型:
- 验证状态 (40%)
- 评分一致性 (30%)
- 价格稳定性 (20%)
- 数据时效性 (10%)
将每个供应商分类为 `LOW / MEDIUM / HIGH / CRITICAL` 风险。
### 数据质量评分 (`src/data_quality_score.py`)
- 每个来源和类别的字段完整度
- 缺失字段模式检测
- 每个供应商的质量等级分类
### 异常检测
- 价格异常值 (IQR 方法)
- 跨平台的重复产品列表
- 缺失关键字段 (MOQ、价格、位置)
- 评分异常高且未经验证的供应商
## 反爬虫策略 (IndiaMART 抓取器)
| 技术 | 实现方式 |
|---|---|
| User-agent 轮换 | 5 个真实浏览器 UA,每次请求随机选择 |
| 请求延迟 | 页面间随机 1–3 秒,类别间随机 2–5 秒 |
| 重试 + 退避 | 3 次重试,采用指数退避算法 |
| 会话持久化 | 带有 cookie 保留功能的 `requests.Session` |
| Playwright 回退 | 针对 JS 密集型页面的全浏览器渲染 |
| robots.txt 合规性 | 在访问每个 URL 前进行检查 |
## EDA 图表 (`charts/`)
| 图表 | 描述 |
|---|---|
| `01_category_distribution.png` | 按类别划分的产品数量 |
| `02_top_cities.png` | 供应商数量排名前 10 的城市 |
| `03_state_distribution.png` | 按邦划分的列表数量 |
| `04_price_distribution_by_category.png` | 每个类别的箱线图(对数刻度) |
| `05_price_buckets_by_category.png` | 价格区间堆叠柱状图 |
| `06_ratings_analysis.png` | 评分直方图 + 类别细分 |
| `07_verified_supplier_analysis.png` | 已验证百分比及价格对比 |
| `08_top_product_keywords.png` | 前 20 个需求关键词 |
## 在生产环境中的预期运行方式
- **增量抓取:** 每日运行 Apify,在插入数据前对 `productId` 进行去重
- **Schema 演进:** 通过 `ALTER TABLE` 迁移自动添加新的规格字段
- **预警:** 数据摄入后运行异常模块;高严重性标记触发通知
- **可扩展性:** REST 批量上传可切换为 PostgreSQL `COPY` 以进行大批量加载
## 数据质量指标
| 指标 | 数值 |
|---|---|
| 总记录数 | 584 |
| 重复率 | 3.2% |
| 价格缺失 | 12% |
| 位置缺失 | 4% |
| MOQ 缺失 — IndiaMART | 100% |
| MOQ 缺失 — TradeIndia | 7% |
| 总体完整度 | 87% |
## 管道指标
| 指标 | 数值 |
|---|---|
| 抓取成功率 | 98% |
| 失败的 URL | 7 |
| 平均页面处理时间 | 2.1 秒 |
| 抓取的 IndiaMART 行数 | 236 |
| 抓取的 TradeIndia 行数 | 348 |
| 去重后的行数 | 584 |
| AI 增强的行数 (Qwen 2.5 7B) | 236 |
## 业务建议
基于对 IndiaMART 和 TradeIndia 上 584 条 B2B 供应商列表的供应链情报分析:
**1. 降低 Gujarat 供应商集中度**
Gujarat 占国内供应商的 38% —— 单一的区域性中断(洪水、罢工、政策变动)可能会影响超过三分之一的采购能力。建议向 Maharashtra、Tamil Nadu 和 Telangana 进行多元化拓展。
**2. 优先使用 TradeIndia 进行供应商发现**
TradeIndia 为 93% 的列表提供了 MOQ 数据,而 IndiaMART 为 0%,并且每个产品提供的结构化字段多出 2.4 倍。对于采购情报而言,TradeIndia 是更强大的数据源。
**3. 监控国际采购集中度**
15% 已映射的供应商为国际供应商(Guangdong、Shanghai、Shandong、Singapore)。建议为每个具有国际采购来源的类别保留一家国内备用供应商。
**4. 将 IndiaMART 的 MOQ 缺口标记为管道优先事项**
IndiaMART 上 MOQ 覆盖率为零,使得占合并数据集 40% 的部分无法进行最小订单规划。针对 IndiaMART 列表进行一次针对性的 AI 增强处理应成为下一步的管道改进措施。
## 数据模型图
```
┌─────────────────────────────────────────────────────────────────────┐
│ RAW LAYER │
├──────────────────────────┬──────────────────────────────────────────┤
│ indiamart_raw.csv │ tradeindia_raw.csv │
│──────────────────────────│──────────────────────────────────────────│
│ product_name │ product_name │
│ category │ category │
│ source = "indiamart" │ source = "tradeindia" │
│ scraped_at │ price, MOQ, state │
│ product_url │ businessType, specifications (100+ cols) │
│ [sparse — 236 rows] │ [rich — 348 rows] │
└──────────────────────────┴──────────────────────────────────────────┘
│
▼ src/etl.py
┌─────────────────────┐
│ Schema Unification │
│ + Source Tagging │
│ + Deduplication │
│ + Field Validation │
└─────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ ETL OUTPUT LAYER │
├─────────────────────────────────────────────────────────────────────┤
│ combined_raw.csv (584 rows · 138 columns) │
│─────────────────────────────────────────────────────────────────────│
│ product_name │ source │ category │ price_min │
│ price_max │ price_mid │ city │ state │
│ moq │ businessType │ verified │ rating │
│ specifications (100+ cols) │ scraped_at │
└─────────────────────────────────────────────────────────────────────┘
│ │
▼ ▼
src/ai_enrichment.py src/supplier_risk_score.py
(Qwen 2.5 7B local) (4-factor weighted model)
│ │
└─────────────┬─────────────┘
▼
┌─────────────────────────────────────────────────────────────────────┐
│ FINAL SCHEMA (Supabase) │
├─────────────────────────────────────────────────────────────────────┤
│ products table (584 rows · 142 columns) │
│─────────────────────────────────────────────────────────────────────│
│ ── Core ────────────────────────────────────────────────────────── │
│ product_name │ category │ source │ city │
│ state │ price │ moq │ verified │
│ rating │ businessType │ scraped_at │
│ │
│ ── AI Enriched (Qwen 2.5 7B) ───────────────────────────────────── │
│ ai_product_type │ ai_material │ ai_use_case │ ai_business_role │
│ │
│ ── Specifications (TradeIndia) ──────────────────────────────────── │
│ specifications_material │ specifications_type │ specifications_* │
└─────────────────────────────────────────────────────────────────────┘
│
▼ app.py (Streamlit)
┌─────────────────────────────────────────────────────────────────────┐
│ DASHBOARD LAYER │
├─────────────────────────────────────────────────────────────────────┤
│ Overview │ Supplier Map │ Categories │ Anomalies │ Supply Chain │
│ Intel │ Quality Report │
└─────────────────────────────────────────────────────────────────────┘
```
## 技术栈
| 层级 | 工具 |
|---|---|
| 抓取 | Python、Playwright、Apify |
| ETL | Pandas、NumPy |
| 数据库 | Supabase (PostgreSQL)、REST API |
| 仪表板 | Streamlit、Plotly |
| 分析 | Pandas、NumPy |
| 测试 | pytest |
*提交用于 Slooze 数据工程挑战 · 评估人:Hari Krishna*
标签:DLL 劫持, ETL流水线, Kubernetes, PostgreSQL, Streamlit, 供应链分析, 大语言模型, 数据工程, 特征检测, 访问控制, 逆向工具