@anysiteio/agent-skills 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +113 -0
- package/.claude-plugin/plugin.json +45 -0
- package/LICENSE +21 -0
- package/README.md +221 -0
- package/bin/install.js +55 -0
- package/package.json +45 -0
- package/skills/anysite-audience-analysis/SKILL.md +273 -0
- package/skills/anysite-audience-analysis/references/PLATFORM_COVERAGE.md +53 -0
- package/skills/anysite-audience-analysis/references/TOOL_MAPPING.md +27 -0
- package/skills/anysite-brand-reputation/SKILL.md +373 -0
- package/skills/anysite-brand-reputation/references/MONITORING_GUIDE.md +84 -0
- package/skills/anysite-competitor-analyzer/SKILL.md +847 -0
- package/skills/anysite-competitor-intelligence/SKILL.md +642 -0
- package/skills/anysite-competitor-intelligence/references/ANALYSIS_PATTERNS.md +97 -0
- package/skills/anysite-content-analytics/SKILL.md +298 -0
- package/skills/anysite-content-analytics/references/METRICS_GUIDE.md +63 -0
- package/skills/anysite-influencer-discovery/SKILL.md +398 -0
- package/skills/anysite-influencer-discovery/references/DISCOVERY_CRITERIA.md +50 -0
- package/skills/anysite-lead-generation/SKILL.md +1022 -0
- package/skills/anysite-lead-generation/references/LINKEDIN_STRATEGIES.md +739 -0
- package/skills/anysite-lead-generation/references/WEB_SCRAPING.md +910 -0
- package/skills/anysite-market-research/SKILL.md +379 -0
- package/skills/anysite-market-research/references/RESEARCH_METHODS.md +65 -0
- package/skills/anysite-person-analyzer/SKILL.md +604 -0
- package/skills/anysite-trend-analysis/SKILL.md +310 -0
- package/skills/anysite-trend-analysis/references/SOCIAL_MONITORING.md +46 -0
- package/skills/anysite-vc-analyst/SKILL.md +269 -0
|
@@ -0,0 +1,910 @@
|
|
|
1
|
+
# Web Scraping for Contact Extraction
|
|
2
|
+
|
|
3
|
+
Strategies and techniques for extracting contact information from company websites using anysite MCP.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Web scraping complements LinkedIn lead generation by:
|
|
8
|
+
- Finding contacts not active on LinkedIn
|
|
9
|
+
- Discovering phone numbers and addresses
|
|
10
|
+
- Verifying email addresses
|
|
11
|
+
- Accessing information from company directories
|
|
12
|
+
- Getting regional office contacts
|
|
13
|
+
|
|
14
|
+
## Contact Extraction Tools
|
|
15
|
+
|
|
16
|
+
### parse_webpage
|
|
17
|
+
|
|
18
|
+
Primary tool for extracting contacts from web pages.
|
|
19
|
+
|
|
20
|
+
**Basic Usage**:
|
|
21
|
+
```
|
|
22
|
+
Tool: mcp__anysite__parse_webpage
|
|
23
|
+
Parameters:
|
|
24
|
+
- url: "https://company.com/contact"
|
|
25
|
+
- extract_contacts: true
|
|
26
|
+
- strip_all_tags: true
|
|
27
|
+
- only_main_content: true
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Returns**:
|
|
31
|
+
- Emails: All email addresses found
|
|
32
|
+
- Phones: Phone numbers in various formats
|
|
33
|
+
- Social links: LinkedIn, Twitter, Facebook, Instagram
|
|
34
|
+
- Content: Page text for additional parsing
|
|
35
|
+
|
|
36
|
+
**Best Practices**:
|
|
37
|
+
- Set `extract_contacts: true` to enable contact extraction
|
|
38
|
+
- Use `only_main_content: true` to avoid footer/header noise
|
|
39
|
+
- Try multiple page variations (/contact, /about, /team)
|
|
40
|
+
|
|
41
|
+
### get_sitemap
|
|
42
|
+
|
|
43
|
+
Discover all pages on a website to find contact information.
|
|
44
|
+
|
|
45
|
+
**Basic Usage**:
|
|
46
|
+
```
|
|
47
|
+
Tool: mcp__anysite__get_sitemap
|
|
48
|
+
Parameters:
|
|
49
|
+
- url: "https://company.com"
|
|
50
|
+
- count: 100
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Returns**:
|
|
54
|
+
- List of URLs from sitemap.xml
|
|
55
|
+
- Typically includes all public pages
|
|
56
|
+
- Useful for finding hidden contact/team pages
|
|
57
|
+
|
|
58
|
+
**Best Practices**:
|
|
59
|
+
- Start with count=100 to get comprehensive coverage
|
|
60
|
+
- Filter results for relevant pages (contact, team, about, leadership)
|
|
61
|
+
- Parse identified pages with parse_webpage
|
|
62
|
+
|
|
63
|
+
## Common Page Patterns
|
|
64
|
+
|
|
65
|
+
### Contact Pages
|
|
66
|
+
|
|
67
|
+
**Standard URLs**:
|
|
68
|
+
```
|
|
69
|
+
/contact
|
|
70
|
+
/contact-us
|
|
71
|
+
/get-in-touch
|
|
72
|
+
/reach-us
|
|
73
|
+
/contact-information
|
|
74
|
+
/locations
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Parsing Strategy**:
|
|
78
|
+
```
|
|
79
|
+
1. Try /contact first (most common)
|
|
80
|
+
2. If 404, try /contact-us
|
|
81
|
+
3. Check sitemap for contact-related URLs
|
|
82
|
+
4. Look for "Contact" link in navigation
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Example**:
|
|
86
|
+
```
|
|
87
|
+
parse_webpage("https://company.com/contact", extract_contacts=true)
|
|
88
|
+
|
|
89
|
+
Expected data:
|
|
90
|
+
- General company email (info@, contact@, hello@)
|
|
91
|
+
- Department emails (sales@, support@, careers@)
|
|
92
|
+
- Phone numbers (often main line)
|
|
93
|
+
- Physical address
|
|
94
|
+
- Social media links
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### About Pages
|
|
98
|
+
|
|
99
|
+
**Standard URLs**:
|
|
100
|
+
```
|
|
101
|
+
/about
|
|
102
|
+
/about-us
|
|
103
|
+
/who-we-are
|
|
104
|
+
/company
|
|
105
|
+
/our-story
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Parsing Strategy**:
|
|
109
|
+
```
|
|
110
|
+
1. Often contains leadership team contacts
|
|
111
|
+
2. May have founder/CEO email
|
|
112
|
+
3. Usually has company description and values
|
|
113
|
+
4. Sometimes includes team photos with names
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Example**:
|
|
117
|
+
```
|
|
118
|
+
parse_webpage("https://company.com/about", extract_contacts=true)
|
|
119
|
+
|
|
120
|
+
Expected data:
|
|
121
|
+
- Leadership emails
|
|
122
|
+
- Company phone
|
|
123
|
+
- Headquarters address
|
|
124
|
+
- Founder/CEO information
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Team/People Pages
|
|
128
|
+
|
|
129
|
+
**Standard URLs**:
|
|
130
|
+
```
|
|
131
|
+
/team
|
|
132
|
+
/our-team
|
|
133
|
+
/people
|
|
134
|
+
/leadership
|
|
135
|
+
/about/team
|
|
136
|
+
/company/team
|
|
137
|
+
/meet-the-team
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Parsing Strategy**:
|
|
141
|
+
```
|
|
142
|
+
1. Best source for individual contacts
|
|
143
|
+
2. Often lists names with titles
|
|
144
|
+
3. May include email addresses
|
|
145
|
+
4. Sometimes has LinkedIn profile links
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Example**:
|
|
149
|
+
```
|
|
150
|
+
parse_webpage("https://company.com/team", extract_contacts=true)
|
|
151
|
+
|
|
152
|
+
Expected data:
|
|
153
|
+
- Individual names and titles
|
|
154
|
+
- Personal email addresses (name@company.com)
|
|
155
|
+
- LinkedIn profile links
|
|
156
|
+
- Role descriptions
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Locations/Offices Pages
|
|
160
|
+
|
|
161
|
+
**Standard URLs**:
|
|
162
|
+
```
|
|
163
|
+
/locations
|
|
164
|
+
/offices
|
|
165
|
+
/contact/locations
|
|
166
|
+
/our-offices
|
|
167
|
+
/global-offices
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Parsing Strategy**:
|
|
171
|
+
```
|
|
172
|
+
1. Find regional office contacts
|
|
173
|
+
2. Get location-specific phone numbers
|
|
174
|
+
3. Extract local addresses
|
|
175
|
+
4. Identify office managers
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Example**:
|
|
179
|
+
```
|
|
180
|
+
parse_webpage("https://company.com/locations", extract_contacts=true)
|
|
181
|
+
|
|
182
|
+
Expected data:
|
|
183
|
+
- Office-specific emails
|
|
184
|
+
- Local phone numbers
|
|
185
|
+
- Regional addresses
|
|
186
|
+
- Office manager contacts
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Email Pattern Recognition
|
|
190
|
+
|
|
191
|
+
### Common Email Formats
|
|
192
|
+
|
|
193
|
+
**Individual Emails**:
|
|
194
|
+
```
|
|
195
|
+
first.last@company.com (Most common)
|
|
196
|
+
first@company.com (Small companies)
|
|
197
|
+
flast@company.com (Medium companies)
|
|
198
|
+
firstl@company.com (Larger companies)
|
|
199
|
+
first_last@company.com (Underscore variant)
|
|
200
|
+
last.first@company.com (Reverse order)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Department Emails**:
|
|
204
|
+
```
|
|
205
|
+
sales@company.com
|
|
206
|
+
info@company.com
|
|
207
|
+
contact@company.com
|
|
208
|
+
hello@company.com
|
|
209
|
+
support@company.com
|
|
210
|
+
careers@company.com
|
|
211
|
+
press@company.com
|
|
212
|
+
marketing@company.com
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Pattern Detection**:
|
|
216
|
+
```
|
|
217
|
+
1. Extract all emails from page
|
|
218
|
+
2. Identify individual vs. department emails
|
|
219
|
+
3. Detect naming pattern from individual emails
|
|
220
|
+
4. Apply pattern to LinkedIn prospects
|
|
221
|
+
5. Validate before using
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Email Validation
|
|
225
|
+
|
|
226
|
+
**Format Validation**:
|
|
227
|
+
```
|
|
228
|
+
Regex pattern: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
|
|
229
|
+
|
|
230
|
+
Check:
|
|
231
|
+
- Contains @ symbol
|
|
232
|
+
- Has domain extension
|
|
233
|
+
- No invalid characters
|
|
234
|
+
- Proper domain format
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Domain Validation**:
|
|
238
|
+
```
|
|
239
|
+
1. Extract domain from email
|
|
240
|
+
2. Verify domain matches company website
|
|
241
|
+
3. Check for common typos (.con instead of .com)
|
|
242
|
+
4. Ensure professional domain (not gmail, yahoo, etc. for B2B)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Deliverability Validation** (external tool required):
|
|
246
|
+
```
|
|
247
|
+
Use email verification services:
|
|
248
|
+
- ZeroBounce
|
|
249
|
+
- NeverBounce
|
|
250
|
+
- Hunter.io
|
|
251
|
+
- EmailListVerify
|
|
252
|
+
|
|
253
|
+
Check:
|
|
254
|
+
- Mailbox exists
|
|
255
|
+
- Domain has MX records
|
|
256
|
+
- Not a catch-all
|
|
257
|
+
- Not a disposable email
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Phone Number Extraction
|
|
261
|
+
|
|
262
|
+
### Phone Number Patterns
|
|
263
|
+
|
|
264
|
+
**US/Canada Formats**:
|
|
265
|
+
```
|
|
266
|
+
(415) 555-1234
|
|
267
|
+
415-555-1234
|
|
268
|
+
415.555.1234
|
|
269
|
+
+1 415 555 1234
|
|
270
|
+
1-415-555-1234
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**International Formats**:
|
|
274
|
+
```
|
|
275
|
+
+44 20 7123 4567 (UK)
|
|
276
|
+
+33 1 23 45 67 89 (France)
|
|
277
|
+
+49 30 123456 (Germany)
|
|
278
|
+
+61 2 1234 5678 (Australia)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Parsing Strategy**:
|
|
282
|
+
```
|
|
283
|
+
1. Look for common format patterns
|
|
284
|
+
2. Extract country code (+1, +44, etc.)
|
|
285
|
+
3. Identify area/city code
|
|
286
|
+
4. Get main number
|
|
287
|
+
5. Check for extension
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Normalization**:
|
|
291
|
+
```
|
|
292
|
+
Input: "(415) 555-1234 ext. 123"
|
|
293
|
+
Normalized: +14155551234
|
|
294
|
+
Extension: 123
|
|
295
|
+
Display: +1 (415) 555-1234 x123
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Phone Number Context
|
|
299
|
+
|
|
300
|
+
**Labels to Look For**:
|
|
301
|
+
```
|
|
302
|
+
Main: General company line
|
|
303
|
+
Sales: Sales team direct line
|
|
304
|
+
Support: Customer support
|
|
305
|
+
Direct: Individual's direct line
|
|
306
|
+
Mobile: Mobile/cell phone
|
|
307
|
+
Toll-free: 800/888 numbers
|
|
308
|
+
Fax: Fax number (avoid using)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Example**:
|
|
312
|
+
```
|
|
313
|
+
Page content:
|
|
314
|
+
"Sales: (415) 555-SALE
|
|
315
|
+
Support: (415) 555-HELP
|
|
316
|
+
Main Office: (415) 555-1234"
|
|
317
|
+
|
|
318
|
+
Extracted:
|
|
319
|
+
- sales_phone: +14155557253
|
|
320
|
+
- support_phone: +14155554357
|
|
321
|
+
- main_phone: +14155551234
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Multi-Page Scraping Workflows
|
|
325
|
+
|
|
326
|
+
### Workflow 1: Comprehensive Company Contact Extraction
|
|
327
|
+
|
|
328
|
+
**Goal**: Extract all available contact information from a company website
|
|
329
|
+
|
|
330
|
+
**Steps**:
|
|
331
|
+
|
|
332
|
+
1. **Get sitemap**
|
|
333
|
+
```
|
|
334
|
+
get_sitemap("https://company.com", count=100)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
2. **Filter for relevant pages**
|
|
338
|
+
```
|
|
339
|
+
Filter sitemap results for:
|
|
340
|
+
- URLs containing: contact, about, team, location, office
|
|
341
|
+
- Exclude: blog, news, privacy, terms
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
3. **Parse each relevant page**
|
|
345
|
+
```
|
|
346
|
+
For each filtered URL:
|
|
347
|
+
parse_webpage(url, extract_contacts=true)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
4. **Consolidate and deduplicate**
|
|
351
|
+
```
|
|
352
|
+
- Combine all emails found
|
|
353
|
+
- Remove duplicates
|
|
354
|
+
- Group by type (sales, support, etc.)
|
|
355
|
+
- Combine all phone numbers
|
|
356
|
+
- Match to departments
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
5. **Enrich with LinkedIn**
|
|
360
|
+
```
|
|
361
|
+
For team member names found:
|
|
362
|
+
search_linkedin_users(first_name, last_name, company_keywords)
|
|
363
|
+
get_linkedin_profile(user)
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Expected Output**:
|
|
367
|
+
- 5-20 email addresses
|
|
368
|
+
- 3-10 phone numbers
|
|
369
|
+
- 10-50 team member names
|
|
370
|
+
- Social media profiles
|
|
371
|
+
- All office locations
|
|
372
|
+
|
|
373
|
+
### Workflow 2: Team Directory Scraping
|
|
374
|
+
|
|
375
|
+
**Goal**: Build contact list of entire team from website directory
|
|
376
|
+
|
|
377
|
+
**Steps**:
|
|
378
|
+
|
|
379
|
+
1. **Find team page**
|
|
380
|
+
```
|
|
381
|
+
Try common patterns:
|
|
382
|
+
- parse_webpage("https://company.com/team")
|
|
383
|
+
- parse_webpage("https://company.com/about/team")
|
|
384
|
+
- parse_webpage("https://company.com/people")
|
|
385
|
+
|
|
386
|
+
Or use sitemap:
|
|
387
|
+
- get_sitemap("https://company.com", count=100)
|
|
388
|
+
- Filter for "team" or "people"
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
2. **Extract team members**
|
|
392
|
+
```
|
|
393
|
+
parse_webpage(team_url, extract_contacts=true)
|
|
394
|
+
|
|
395
|
+
Look for patterns:
|
|
396
|
+
- Name + Title combinations
|
|
397
|
+
- Email addresses
|
|
398
|
+
- LinkedIn profile links
|
|
399
|
+
- Headshot images with alt text containing names
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
3. **Parse individual profiles** (if available)
|
|
403
|
+
```
|
|
404
|
+
Some team pages link to individual profiles:
|
|
405
|
+
- /team/john-smith
|
|
406
|
+
- /people/jane-doe
|
|
407
|
+
|
|
408
|
+
parse_webpage(individual_url, extract_contacts=true)
|
|
409
|
+
→ Often has direct email, phone
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
4. **Match to LinkedIn**
|
|
413
|
+
```
|
|
414
|
+
For each team member name:
|
|
415
|
+
search_linkedin_users(
|
|
416
|
+
first_name=<first>,
|
|
417
|
+
last_name=<last>,
|
|
418
|
+
company_keywords=<company>
|
|
419
|
+
)
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
5. **Construct email addresses**
|
|
423
|
+
```
|
|
424
|
+
If email pattern detected from some team members:
|
|
425
|
+
- Apply pattern to all team members
|
|
426
|
+
- Example: john.doe@company.com → jane.smith@company.com
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**Expected Output**:
|
|
430
|
+
- Complete team roster
|
|
431
|
+
- 40-60% email coverage (direct or inferred)
|
|
432
|
+
- LinkedIn profiles for 70-80% of team
|
|
433
|
+
- Titles and roles for all members
|
|
434
|
+
|
|
435
|
+
### Workflow 3: Multi-Location Contact Extraction
|
|
436
|
+
|
|
437
|
+
**Goal**: Get contacts for all office locations
|
|
438
|
+
|
|
439
|
+
**Steps**:
|
|
440
|
+
|
|
441
|
+
1. **Find locations page**
|
|
442
|
+
```
|
|
443
|
+
parse_webpage("https://company.com/locations", extract_contacts=true)
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
2. **Extract location information**
|
|
447
|
+
```
|
|
448
|
+
For each location found:
|
|
449
|
+
- Office name/city
|
|
450
|
+
- Full address
|
|
451
|
+
- Phone number
|
|
452
|
+
- Regional email
|
|
453
|
+
- Office manager name
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
3. **Parse location-specific pages** (if available)
|
|
457
|
+
```
|
|
458
|
+
Some companies have per-location pages:
|
|
459
|
+
- /locations/san-francisco
|
|
460
|
+
- /offices/new-york
|
|
461
|
+
|
|
462
|
+
parse_webpage(location_url, extract_contacts=true)
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
4. **Find office leaders**
|
|
466
|
+
```
|
|
467
|
+
For each location:
|
|
468
|
+
search_linkedin_users(
|
|
469
|
+
title="Office Manager OR General Manager",
|
|
470
|
+
company_keywords=<company>,
|
|
471
|
+
location=<office_city>
|
|
472
|
+
)
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
5. **Build location contact sheet**
|
|
476
|
+
```
|
|
477
|
+
Spreadsheet with:
|
|
478
|
+
- Location name
|
|
479
|
+
- Address
|
|
480
|
+
- Main phone
|
|
481
|
+
- Local email
|
|
482
|
+
- Office manager name + LinkedIn
|
|
483
|
+
- Office manager email
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Expected Output**:
|
|
487
|
+
- Contact info for all office locations
|
|
488
|
+
- Regional phone numbers
|
|
489
|
+
- Office manager contacts
|
|
490
|
+
- Location-specific emails
|
|
491
|
+
|
|
492
|
+
## Advanced Extraction Techniques
|
|
493
|
+
|
|
494
|
+
### HTML Structure Analysis
|
|
495
|
+
|
|
496
|
+
**Common Team Page Structures**:
|
|
497
|
+
|
|
498
|
+
**Grid Layout**:
|
|
499
|
+
```html
|
|
500
|
+
<div class="team-grid">
|
|
501
|
+
<div class="team-member">
|
|
502
|
+
<img src="john.jpg" alt="John Smith">
|
|
503
|
+
<h3>John Smith</h3>
|
|
504
|
+
<p class="title">VP Sales</p>
|
|
505
|
+
<a href="mailto:john@company.com">Email</a>
|
|
506
|
+
</div>
|
|
507
|
+
...
|
|
508
|
+
</div>
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**List Layout**:
|
|
512
|
+
```html
|
|
513
|
+
<ul class="team-list">
|
|
514
|
+
<li>
|
|
515
|
+
<strong>Jane Doe</strong> - CEO
|
|
516
|
+
<a href="https://linkedin.com/in/janedoe">LinkedIn</a>
|
|
517
|
+
<a href="mailto:jane@company.com">jane@company.com</a>
|
|
518
|
+
</li>
|
|
519
|
+
...
|
|
520
|
+
</ul>
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**Table Layout**:
|
|
524
|
+
```html
|
|
525
|
+
<table class="team-directory">
|
|
526
|
+
<tr>
|
|
527
|
+
<td>Bob Johnson</td>
|
|
528
|
+
<td>CTO</td>
|
|
529
|
+
<td>bob.johnson@company.com</td>
|
|
530
|
+
<td>(415) 555-1234</td>
|
|
531
|
+
</tr>
|
|
532
|
+
...
|
|
533
|
+
</table>
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
**Parsing Strategy**:
|
|
537
|
+
- Look for repeating patterns
|
|
538
|
+
- Identify CSS classes for team members
|
|
539
|
+
- Extract data from consistent positions
|
|
540
|
+
- Handle variations in structure
|
|
541
|
+
|
|
542
|
+
### Contact Form Scraping
|
|
543
|
+
|
|
544
|
+
**Contact Form Analysis**:
|
|
545
|
+
```
|
|
546
|
+
Many contact pages have forms instead of direct emails
|
|
547
|
+
|
|
548
|
+
Indicators:
|
|
549
|
+
- Form fields: name, email, message
|
|
550
|
+
- Submit button
|
|
551
|
+
- "Get in touch" or "Send us a message"
|
|
552
|
+
|
|
553
|
+
Information to extract:
|
|
554
|
+
- Department options (dropdown values)
|
|
555
|
+
- Office location options
|
|
556
|
+
- Inquiry type options (sales, support, etc.)
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**Alternative Approaches**:
|
|
560
|
+
1. **Check page source** for hidden emails
|
|
561
|
+
```
|
|
562
|
+
Sometimes email is in HTML comments
|
|
563
|
+
Or in JavaScript that loads after form submission
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
2. **Try related pages**
|
|
567
|
+
```
|
|
568
|
+
If /contact has only a form:
|
|
569
|
+
- Try /about for team emails
|
|
570
|
+
- Check press/media page for PR contact
|
|
571
|
+
- Look for careers page (jobs@)
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
3. **Use department-specific pages**
|
|
575
|
+
```
|
|
576
|
+
/sales might have sales team contacts
|
|
577
|
+
/support might have support emails
|
|
578
|
+
/media might have press contact
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Social Link Extraction
|
|
582
|
+
|
|
583
|
+
**Social Profiles to Extract**:
|
|
584
|
+
```
|
|
585
|
+
LinkedIn: linkedin.com/company/[company]
|
|
586
|
+
Twitter: twitter.com/[company]
|
|
587
|
+
Facebook: facebook.com/[company]
|
|
588
|
+
Instagram: instagram.com/[company]
|
|
589
|
+
YouTube: youtube.com/[company]
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
**Parsing Strategy**:
|
|
593
|
+
```
|
|
594
|
+
parse_webpage(url, extract_contacts=true) returns social links
|
|
595
|
+
|
|
596
|
+
Use social profiles to:
|
|
597
|
+
1. Verify company information
|
|
598
|
+
2. Find additional team members
|
|
599
|
+
3. Get posting/engagement insights
|
|
600
|
+
4. Identify brand voice
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
**Example**:
|
|
604
|
+
```
|
|
605
|
+
Social links found:
|
|
606
|
+
- LinkedIn: linkedin.com/company/techcorp
|
|
607
|
+
- Twitter: twitter.com/techcorp
|
|
608
|
+
|
|
609
|
+
Next steps:
|
|
610
|
+
get_linkedin_company("techcorp") → Company details
|
|
611
|
+
get_twitter_user("techcorp") → Twitter profile
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
## Error Handling & Troubleshooting
|
|
615
|
+
|
|
616
|
+
### Common Issues
|
|
617
|
+
|
|
618
|
+
#### 1. No Contacts Found
|
|
619
|
+
|
|
620
|
+
**Symptoms**: parse_webpage returns no emails or phones
|
|
621
|
+
|
|
622
|
+
**Causes**:
|
|
623
|
+
- JavaScript-rendered content (page loads dynamically)
|
|
624
|
+
- Contact form instead of direct contacts
|
|
625
|
+
- Contacts hidden in images or PDFs
|
|
626
|
+
- Privacy-focused website design
|
|
627
|
+
|
|
628
|
+
**Solutions**:
|
|
629
|
+
```
|
|
630
|
+
1. Try alternative pages:
|
|
631
|
+
- /about instead of /contact
|
|
632
|
+
- /team instead of /contact-us
|
|
633
|
+
- /leadership for executive contacts
|
|
634
|
+
|
|
635
|
+
2. Check page source manually to verify data exists
|
|
636
|
+
|
|
637
|
+
3. Look for PDF downloads:
|
|
638
|
+
- Company brochure might have contacts
|
|
639
|
+
- Annual report might list executives
|
|
640
|
+
|
|
641
|
+
4. Use LinkedIn as primary source instead
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
#### 2. Malformed Contact Data
|
|
645
|
+
|
|
646
|
+
**Symptoms**: Emails or phones in wrong format
|
|
647
|
+
|
|
648
|
+
**Causes**:
|
|
649
|
+
- Email obfuscation (user[at]company.com)
|
|
650
|
+
- Phone with extra text (Call us: 555-1234)
|
|
651
|
+
- HTML entities (& instead of &)
|
|
652
|
+
|
|
653
|
+
**Solutions**:
|
|
654
|
+
```
|
|
655
|
+
Post-processing:
|
|
656
|
+
- Replace [at] with @
|
|
657
|
+
- Replace [dot] with .
|
|
658
|
+
- Strip "Call", "Phone:", "Email:" prefixes
|
|
659
|
+
- Decode HTML entities
|
|
660
|
+
- Normalize phone format
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
**Example**:
|
|
664
|
+
```
|
|
665
|
+
Raw: "Email us at hello[at]company[dot]com"
|
|
666
|
+
Cleaned: "hello@company.com"
|
|
667
|
+
|
|
668
|
+
Raw: "Phone: (415) 555-1234 ext 123"
|
|
669
|
+
Cleaned: "+14155551234"
|
|
670
|
+
Extension: "123"
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
#### 3. Too Many Irrelevant Results
|
|
674
|
+
|
|
675
|
+
**Symptoms**: Extracted emails include spam, marketing, unrelated domains
|
|
676
|
+
|
|
677
|
+
**Causes**:
|
|
678
|
+
- Partner/sponsor logos with emails
|
|
679
|
+
- Advertisement blocks
|
|
680
|
+
- Footer links to other sites
|
|
681
|
+
|
|
682
|
+
**Solutions**:
|
|
683
|
+
```
|
|
684
|
+
Filtering strategy:
|
|
685
|
+
1. only_main_content: true (remove footer/header)
|
|
686
|
+
2. Verify email domain matches company domain
|
|
687
|
+
3. Exclude common spam patterns (noreply@, donotreply@)
|
|
688
|
+
4. Filter out tracking/marketing emails
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
**Example Filter**:
|
|
692
|
+
```
|
|
693
|
+
Keep:
|
|
694
|
+
- john@company.com ✓
|
|
695
|
+
- sales@company.com ✓
|
|
696
|
+
|
|
697
|
+
Discard:
|
|
698
|
+
- noreply@company.com ✗
|
|
699
|
+
- partner@otherdomain.com ✗
|
|
700
|
+
- ads@advertising-network.com ✗
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
#### 4. Rate Limiting or Blocking
|
|
704
|
+
|
|
705
|
+
**Symptoms**: Requests fail or return errors
|
|
706
|
+
|
|
707
|
+
**Causes**:
|
|
708
|
+
- Website blocks scraping/bots
|
|
709
|
+
- Rate limiting (too many requests too fast)
|
|
710
|
+
- IP-based blocking
|
|
711
|
+
|
|
712
|
+
**Solutions**:
|
|
713
|
+
```
|
|
714
|
+
1. Increase request_timeout parameter
|
|
715
|
+
2. Add delays between requests (30-60 seconds)
|
|
716
|
+
3. Rotate user agents (handled by MCP server)
|
|
717
|
+
4. Scrape during off-peak hours
|
|
718
|
+
5. Use smaller batch sizes
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
**Best Practices**:
|
|
722
|
+
```
|
|
723
|
+
- Wait 30-60 seconds between parse_webpage calls
|
|
724
|
+
- Process 5-10 websites per batch
|
|
725
|
+
- Don't scrape same site multiple times rapidly
|
|
726
|
+
- Respect robots.txt
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
### Validation Workflows
|
|
730
|
+
|
|
731
|
+
**Email Validation Checklist**:
|
|
732
|
+
```
|
|
733
|
+
✓ Contains @ symbol
|
|
734
|
+
✓ Has valid domain extension
|
|
735
|
+
✓ Domain matches company website
|
|
736
|
+
✓ Not a role-based email (noreply, admin, etc.)
|
|
737
|
+
✓ Proper format (no spaces, special chars)
|
|
738
|
+
✓ Not a personal email (gmail, yahoo, etc. for B2B)
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
**Phone Validation Checklist**:
|
|
742
|
+
```
|
|
743
|
+
✓ Contains correct number of digits for region
|
|
744
|
+
✓ Has valid area/city code
|
|
745
|
+
✓ Not a fax number
|
|
746
|
+
✓ Not a toll-free number (for direct contact)
|
|
747
|
+
✓ Matches expected country code
|
|
748
|
+
✓ Includes extension if available
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
**Data Quality Scoring**:
|
|
752
|
+
```
|
|
753
|
+
High Quality (90-100):
|
|
754
|
+
- Direct email (first.last@)
|
|
755
|
+
- Direct phone with extension
|
|
756
|
+
- Verified through LinkedIn match
|
|
757
|
+
|
|
758
|
+
Medium Quality (70-89):
|
|
759
|
+
- Department email (sales@)
|
|
760
|
+
- Main office phone
|
|
761
|
+
- Found on official website
|
|
762
|
+
|
|
763
|
+
Low Quality (50-69):
|
|
764
|
+
- Generic contact form
|
|
765
|
+
- General inquiry email (info@)
|
|
766
|
+
- No phone available
|
|
767
|
+
|
|
768
|
+
Poor Quality (<50):
|
|
769
|
+
- Inferred/guessed email
|
|
770
|
+
- No contact method found
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
## Integration with LinkedIn Data
|
|
774
|
+
|
|
775
|
+
### Workflow: LinkedIn → Web Enrichment
|
|
776
|
+
|
|
777
|
+
**Pattern**: Use LinkedIn as source, enrich with web data
|
|
778
|
+
|
|
779
|
+
**Steps**:
|
|
780
|
+
|
|
781
|
+
1. **Find prospects on LinkedIn**
|
|
782
|
+
```
|
|
783
|
+
search_linkedin_users(title, location, company_keywords)
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
2. **Get company websites**
|
|
787
|
+
```
|
|
788
|
+
For each prospect:
|
|
789
|
+
get_linkedin_company(prospect.company)
|
|
790
|
+
→ Extract website URL
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
3. **Extract company contacts**
|
|
794
|
+
```
|
|
795
|
+
For each company website:
|
|
796
|
+
parse_webpage(website + "/contact", extract_contacts=true)
|
|
797
|
+
parse_webpage(website + "/team", extract_contacts=true)
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
4. **Match web contacts to LinkedIn**
|
|
801
|
+
```
|
|
802
|
+
For each email found on website:
|
|
803
|
+
If email pattern matches prospect name:
|
|
804
|
+
Assign email to LinkedIn prospect
|
|
805
|
+
Else:
|
|
806
|
+
Try find_linkedin_user_email(email)
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
5. **Build enriched prospect list**
|
|
810
|
+
```
|
|
811
|
+
Combine:
|
|
812
|
+
- LinkedIn profile data (title, experience, etc.)
|
|
813
|
+
- Company website (phone, address)
|
|
814
|
+
- Direct email (from web or LinkedIn)
|
|
815
|
+
- Social profiles (from website)
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
**Expected Result**:
|
|
819
|
+
- LinkedIn prospects with 80-90% email coverage
|
|
820
|
+
- Company phone numbers for all prospects
|
|
821
|
+
- Verified company websites
|
|
822
|
+
- Full contact profiles ready for outreach
|
|
823
|
+
|
|
824
|
+
### Workflow: Web → LinkedIn Validation
|
|
825
|
+
|
|
826
|
+
**Pattern**: Use website as source, validate with LinkedIn
|
|
827
|
+
|
|
828
|
+
**Steps**:
|
|
829
|
+
|
|
830
|
+
1. **Extract team from website**
|
|
831
|
+
```
|
|
832
|
+
parse_webpage("https://company.com/team", extract_contacts=true)
|
|
833
|
+
→ Get names, titles, emails
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
2. **Search LinkedIn for each person**
|
|
837
|
+
```
|
|
838
|
+
For each team member:
|
|
839
|
+
search_linkedin_users(
|
|
840
|
+
first_name=<first>,
|
|
841
|
+
last_name=<last>,
|
|
842
|
+
company_keywords=<company>
|
|
843
|
+
)
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
3. **Validate and enrich**
|
|
847
|
+
```
|
|
848
|
+
For each LinkedIn match:
|
|
849
|
+
get_linkedin_profile(user)
|
|
850
|
+
→ Verify title matches
|
|
851
|
+
→ Get full work history
|
|
852
|
+
→ Find additional contact methods
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
4. **Cross-reference data**
|
|
856
|
+
```
|
|
857
|
+
Compare:
|
|
858
|
+
- Website title vs. LinkedIn title (should match)
|
|
859
|
+
- Email pattern consistency
|
|
860
|
+
- Still at company (LinkedIn current position)
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
5. **Build validated contact list**
|
|
864
|
+
```
|
|
865
|
+
Keep prospects where:
|
|
866
|
+
- LinkedIn profile found
|
|
867
|
+
- Title matches (or is recent)
|
|
868
|
+
- Still at company
|
|
869
|
+
- Email validated
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
**Expected Result**:
|
|
873
|
+
- 70-80% of website contacts validated on LinkedIn
|
|
874
|
+
- Up-to-date employment status
|
|
875
|
+
- Enriched with LinkedIn work history
|
|
876
|
+
- Higher quality contact data
|
|
877
|
+
|
|
878
|
+
## Best Practices Summary
|
|
879
|
+
|
|
880
|
+
### Efficiency
|
|
881
|
+
|
|
882
|
+
1. **Start with sitemap** to identify all relevant pages
|
|
883
|
+
2. **Parse in batches** of 5-10 websites at a time
|
|
884
|
+
3. **Prioritize high-value pages** (team, leadership) over generic contact pages
|
|
885
|
+
4. **Use LinkedIn first** for individual contacts, web scraping for company-level contacts
|
|
886
|
+
|
|
887
|
+
### Quality
|
|
888
|
+
|
|
889
|
+
1. **Validate all contact data** before using
|
|
890
|
+
2. **Match emails to people** when possible (vs. generic info@)
|
|
891
|
+
3. **Check data freshness** (recent LinkedIn activity = current employee)
|
|
892
|
+
4. **Verify company domain** matches website being scraped
|
|
893
|
+
|
|
894
|
+
### Compliance
|
|
895
|
+
|
|
896
|
+
1. **Respect robots.txt** and website terms of service
|
|
897
|
+
2. **Rate limit requests** to avoid overloading servers
|
|
898
|
+
3. **Only collect publicly available** contact information
|
|
899
|
+
4. **Provide opt-out mechanism** in all communications
|
|
900
|
+
|
|
901
|
+
### Organization
|
|
902
|
+
|
|
903
|
+
1. **Track source of data** (which page each contact came from)
|
|
904
|
+
2. **Date stamp extractions** to know data freshness
|
|
905
|
+
3. **Deduplicate systematically** (same email from multiple pages)
|
|
906
|
+
4. **Categorize contacts** (by department, location, role)
|
|
907
|
+
|
|
908
|
+
---
|
|
909
|
+
|
|
910
|
+
**Next Steps**: Combine web scraping with LinkedIn strategies from [LINKEDIN_STRATEGIES.md](LINKEDIN_STRATEGIES.md) for comprehensive lead generation.
|