|
| 1 | +# File Type Scraper - Data Schema and Sources Guide |
| 2 | + |
| 3 | +## File Types Page Schema |
| 4 | + |
| 5 | +The scraper outputs data in the `FileTypeRawData` schema (defined in `apps/files/types/index.d.ts`), which is the format stored in JSON files at `public/data/files/individual/{extension}.json`. |
| 6 | + |
| 7 | +### Complete Schema Structure |
| 8 | + |
| 9 | +```typescript |
| 10 | +interface FileTypeRawData { |
| 11 | + // Core identification |
| 12 | + slug: string; // URL-friendly identifier (e.g., "pdf") |
| 13 | + extension: string; // File extension (e.g., "pdf") |
| 14 | + name: string; // Full name (e.g., "Portable Document Format File") |
| 15 | + |
| 16 | + // Categorization |
| 17 | + category?: string; // Category name (e.g., "Page Layout Files") |
| 18 | + category_slug?: string; // URL-friendly category (e.g., "page-layout-files") |
| 19 | + |
| 20 | + // Description |
| 21 | + summary?: string; // Brief description/summary |
| 22 | + |
| 23 | + // Developer/Creator |
| 24 | + developer?: string; // Developer name |
| 25 | + developer_org?: string; // Organization name |
| 26 | + developer_name?: string; // Developer full name |
| 27 | + developer_slug?: string; // URL-friendly developer name |
| 28 | + |
| 29 | + // Popularity metrics |
| 30 | + rating?: number; // Rating (1-5 scale) |
| 31 | + votes?: number; // Number of votes |
| 32 | + popularity?: { |
| 33 | + rating: number; |
| 34 | + votes: number; |
| 35 | + }; |
| 36 | + |
| 37 | + // Detailed information |
| 38 | + more_information?: { |
| 39 | + content?: string[]; // Additional info paragraphs |
| 40 | + description?: string[]; // Detailed descriptions |
| 41 | + screenshot?: { |
| 42 | + url: string; |
| 43 | + alt: string; |
| 44 | + caption: string; |
| 45 | + }; |
| 46 | + }; |
| 47 | + |
| 48 | + // Technical specifications |
| 49 | + technical_info?: { |
| 50 | + content?: string[]; // Technical details |
| 51 | + }; |
| 52 | + |
| 53 | + // Usage instructions |
| 54 | + how_to_open?: { |
| 55 | + instructions?: string[]; // Opening instructions |
| 56 | + programs?: Array<{ |
| 57 | + name: string; |
| 58 | + url?: string; |
| 59 | + }>; |
| 60 | + }; |
| 61 | + |
| 62 | + how_to_convert?: { |
| 63 | + instructions?: string[]; // Conversion instructions |
| 64 | + }; |
| 65 | + |
| 66 | + // Software support |
| 67 | + programs?: { |
| 68 | + [platform: string]: Array<{ // e.g., "Windows", "macOS", "Linux" |
| 69 | + name: string; |
| 70 | + url?: string; |
| 71 | + license?: string; |
| 72 | + }>; |
| 73 | + }; |
| 74 | + |
| 75 | + // Media |
| 76 | + images?: Array<{ |
| 77 | + url: string; |
| 78 | + alt: string; |
| 79 | + caption: string; |
| 80 | + }>; |
| 81 | + |
| 82 | + // Metadata |
| 83 | + common_filenames?: string[]; // Common filename examples |
| 84 | + updated_at?: string; // Last update timestamp |
| 85 | + last_updated?: string; // Human-readable update date |
| 86 | + |
| 87 | + // Source tracking |
| 88 | + sources?: Array<{ |
| 89 | + url: string; // Source URL |
| 90 | + retrieved_at: string; // ISO timestamp |
| 91 | + }>; |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | +## Data Sources - What We Extract |
| 96 | + |
| 97 | +### 1. fileinfo.com (Primary Source) |
| 98 | + |
| 99 | +**URL Pattern**: `https://fileinfo.com/extension/{extension}` |
| 100 | + |
| 101 | +**Data Extracted**: |
| 102 | +- ✅ **Name**: Full file type name from `<h1>` tag |
| 103 | +- ✅ **Summary**: Description from `.infoBox p` |
| 104 | +- ✅ **Category**: From category links |
| 105 | +- ✅ **Developer**: From developer section |
| 106 | +- ✅ **Rating**: Numerical rating (1-5) |
| 107 | +- ✅ **Votes**: Number of user votes |
| 108 | +- ✅ **More Information**: Content paragraphs from `.moreInfo` |
| 109 | +- ✅ **How to Open**: Instructions and program lists |
| 110 | +- ✅ **Programs**: Compatible software with URLs |
| 111 | + |
| 112 | +**HTML Selectors Used**: |
| 113 | +```javascript |
| 114 | +$('h1').first() // Name |
| 115 | +$('.infoBox p').first() // Summary |
| 116 | +$('a[href*="/filetypes/"]').first() // Category |
| 117 | +$('td:contains("Developer:")').next('td') // Developer |
| 118 | +$('.rating').first() // Rating |
| 119 | +$('.moreInfo p') // Info sections |
| 120 | +$('ul li a, .programs a') // Programs |
| 121 | +``` |
| 122 | + |
| 123 | +### 2. files.org (Alternative Source) |
| 124 | + |
| 125 | +**URL Pattern**: `https://file.org/extension/{extension}` |
| 126 | + |
| 127 | +**Data Extracted**: |
| 128 | +- ✅ **Name**: File type name from `<h1>` |
| 129 | +- ✅ **Summary**: From `.extension-description` or meta description |
| 130 | +- ✅ **Category**: From `.category` span |
| 131 | +- ✅ **Developer**: From "Developer:" text pattern |
| 132 | +- ✅ **More Information**: Content from multiple sections |
| 133 | +- ✅ **Technical Info**: Technical specifications |
| 134 | +- ✅ **How to Open**: Opening instructions |
| 135 | +- ✅ **Programs**: Software list with URLs |
| 136 | +- ✅ **MIME Type**: MIME type information |
| 137 | + |
| 138 | +**HTML Selectors Used**: |
| 139 | +```javascript |
| 140 | +$('h1').first() // Name |
| 141 | +$('.extension-description p').first() // Summary |
| 142 | +$('.category').first() // Category |
| 143 | +$('h2, h3').each() + next elements // Sections |
| 144 | +$('li a').each() // Programs |
| 145 | +``` |
| 146 | + |
| 147 | +### 3. fileformat.com (API + Web Fallback) |
| 148 | + |
| 149 | +**Primary**: API endpoints (tried in order): |
| 150 | +- `https://www.fileformat.com/api/v1/file-extension/{extension}` |
| 151 | +- `https://fileformat.com/api/extension/{extension}` |
| 152 | +- `https://api.fileformat.com/v1/format/{extension}` |
| 153 | + |
| 154 | +**Fallback**: Web scraping from `https://docs.fileformat.com/extension/{extension}/` |
| 155 | + |
| 156 | +**Data Extracted**: |
| 157 | +- ✅ **Name**: File type name |
| 158 | +- ✅ **Summary**: Description from meta tag |
| 159 | +- ✅ **Category**: Category classification |
| 160 | +- ✅ **Developer**: Creator/company info |
| 161 | +- ✅ **MIME Type**: MIME type from API or text |
| 162 | +- ✅ **Technical Info**: Specifications |
| 163 | +- ✅ **Programs**: Software by platform (from API) |
| 164 | +- ✅ **Content**: Main content paragraphs |
| 165 | + |
| 166 | +**API Response Fields Mapped**: |
| 167 | +```javascript |
| 168 | +json.name / json.title / json.fullName → name |
| 169 | +json.description / json.summary → summary |
| 170 | +json.category → category |
| 171 | +json.developer / json.creator / json.company → developer |
| 172 | +json.mimeType / json.mime_type → mime_type |
| 173 | +json.programs / json.applications → programs (by platform) |
| 174 | +``` |
| 175 | + |
| 176 | +## Data Merging Strategy |
| 177 | + |
| 178 | +When data comes from multiple sources, the merger (`data-merger.ts`) combines them intelligently: |
| 179 | + |
| 180 | +### Priority Rules: |
| 181 | +1. **Name**: Longest, most descriptive name wins |
| 182 | +2. **Summary**: Longest, most informative summary selected |
| 183 | +3. **Developer**: Most specific information preferred |
| 184 | +4. **Content**: All unique paragraphs merged and deduplicated |
| 185 | +5. **Programs**: Combined by platform, deduplicated by name |
| 186 | +6. **Ratings**: Averaged across all sources |
| 187 | +7. **Sources**: All source URLs and timestamps tracked |
| 188 | + |
| 189 | +### Example Merge: |
| 190 | +```javascript |
| 191 | +// fileinfo.com provides: |
| 192 | +{ |
| 193 | + name: "PDF File", |
| 194 | + rating: 4.2, |
| 195 | + votes: 5000, |
| 196 | + content: ["Info A", "Info B"] |
| 197 | +} |
| 198 | + |
| 199 | +// files.org provides: |
| 200 | +{ |
| 201 | + name: "Portable Document Format File", // ← Longer, used |
| 202 | + rating: 4.0, |
| 203 | + votes: 3000, |
| 204 | + content: ["Info B", "Info C"] |
| 205 | +} |
| 206 | + |
| 207 | +// Merged result: |
| 208 | +{ |
| 209 | + name: "Portable Document Format File", // Best from files.org |
| 210 | + rating: 4.1, // Average: (4.2 + 4.0) / 2 |
| 211 | + votes: 4000, // Average: (5000 + 3000) / 2 |
| 212 | + content: ["Info A", "Info B", "Info C"], // Deduplicated union |
| 213 | + sources: [ |
| 214 | + { url: "https://fileinfo.com/...", ... }, |
| 215 | + { url: "https://file.org/...", ... } |
| 216 | + ] |
| 217 | +} |
| 218 | +``` |
| 219 | + |
| 220 | +## How to Run Scrapes |
| 221 | + |
| 222 | +### 1. Scrape Specific Extensions |
| 223 | +```bash |
| 224 | +npm run scrape -- -e pdf,docx,xlsx |
| 225 | +``` |
| 226 | + |
| 227 | +### 2. Scrape A-Z Everything (All 10,477+ Extensions) |
| 228 | + |
| 229 | +**Option A: Update All Existing Files** |
| 230 | +```bash |
| 231 | +npm run scrape -- -u |
| 232 | +``` |
| 233 | +This reads all extensions from `public/data/files/individual/` and re-scrapes them. |
| 234 | + |
| 235 | +**Option B: Scrape from Index** |
| 236 | +```bash |
| 237 | +npm run scrape |
| 238 | +``` |
| 239 | +Without `-e` flag, it loads all extensions from the existing index. |
| 240 | + |
| 241 | +**Option C: Scrape All with Custom Batch Size** |
| 242 | +```bash |
| 243 | +npm run scrape -- -u -b 5 |
| 244 | +``` |
| 245 | +Process 5 at a time (safer for rate limiting). |
| 246 | + |
| 247 | +### 3. Scrape Only New (Missing) Extensions |
| 248 | +```bash |
| 249 | +npm run scrape |
| 250 | +``` |
| 251 | +Without `-u` flag, skips existing files. |
| 252 | + |
| 253 | +### 4. Scrape from Specific Sources Only |
| 254 | +```bash |
| 255 | +npm run scrape -- -e pdf -s fileinfo,files.org |
| 256 | +``` |
| 257 | +Excludes fileformat.com from the scrape. |
| 258 | + |
| 259 | +### 5. Generate Indexes After Scraping |
| 260 | +```bash |
| 261 | +npm run generate-indexes |
| 262 | +``` |
| 263 | +Creates: |
| 264 | +- `index.json` (all extensions alphabetically) |
| 265 | +- `alphabet-index.json` (grouped by first letter) |
| 266 | +- `categories/*.json` (grouped by category) |
| 267 | +- `popular.json` (top 100 by votes) |
| 268 | +- `search-index.json` (search-optimized) |
| 269 | + |
| 270 | +## Complete A-Z Scraping Workflow |
| 271 | + |
| 272 | +```bash |
| 273 | +# Step 1: Test with a few extensions first |
| 274 | +npm run test-scraper test-batch pdf,txt,jpg,mp3 |
| 275 | + |
| 276 | +# Step 2: Scrape all extensions (this will take hours!) |
| 277 | +# Recommended: Use small batch size for safety |
| 278 | +npm run scrape -- -u -b 5 |
| 279 | + |
| 280 | +# Step 3: Regenerate all indexes |
| 281 | +npm run generate-indexes |
| 282 | + |
| 283 | +# Step 4: Validate the data |
| 284 | +npm run test-scraper validate ./public/data/files/individual |
| 285 | +``` |
| 286 | + |
| 287 | +## Performance Estimates |
| 288 | + |
| 289 | +With current rate limiting (2 seconds between requests): |
| 290 | +- **1 extension**: ~6-10 seconds (3 sources × 2-3s each) |
| 291 | +- **100 extensions**: ~10-17 minutes |
| 292 | +- **1,000 extensions**: ~1.7-2.8 hours |
| 293 | +- **10,477 extensions**: ~17-29 hours (full A-Z) |
| 294 | + |
| 295 | +Batch size affects memory but not total time (rate limiting is the bottleneck). |
| 296 | + |
| 297 | +## Tips for A-Z Scraping |
| 298 | + |
| 299 | +1. **Start Small**: Test with `-e a,b,c` first |
| 300 | +2. **Use Small Batches**: `-b 5` or `-b 10` max |
| 301 | +3. **Monitor Progress**: Watch the console output |
| 302 | +4. **Resume Capability**: If interrupted, re-run without `-u` to skip completed |
| 303 | +5. **Validate After**: Run `npm run test-scraper validate` to check quality |
| 304 | +6. **Generate Indexes**: Always run `npm run generate-indexes` after scraping |
| 305 | + |
| 306 | +## Schema Compatibility |
| 307 | + |
| 308 | +The scraper output (`MergedFileData`) is compatible with the existing `FileTypeRawData` schema used throughout the application. The transformer (`files-transformer.ts`) converts it to `FileTypeTemplateData` for page display. |
| 309 | + |
| 310 | +**Data Flow**: |
| 311 | +``` |
| 312 | +Scrapers → MergedFileData → JSON Files (FileTypeRawData) → Transformer → FileTypeTemplateData → UI |
| 313 | +``` |
0 commit comments