想把几千个网站分好类、评好分光靠人工登记肯定不现实必须靠爬虫自动抓取关键信息。比如一些导航网站收录了大量设计、产品、开发类的优质站点。你有没想过这类导航站是怎么知道某个新网站“长什么样”的答案就是一套自动化的爬虫与数据采集流水线在默默地干活。今天这篇文章手把手教你从零搭建网站爬虫与数据采集系统。读完你可以直接落地自己的采集项目。一、在开始写代码前先搞懂边界做爬虫的底线就一句话只采公开数据尊重 robots.txt不要对目标服务器造成压力。我们模拟的场景是你发现了一批优质网站想把它们的标题、描述、截图、关键词等信息采下来这是在帮助内容曝光属于正常的数据采集。正式开始前每条请求都遵守三个原则User-Agent 写清楚自己是谁留联系方式请求间隔至少 1-3 秒遇到 403 或反爬果断停手或走正规 API二、最简爬虫30 行代码抓取一个网页的基本信息先从最简单的单页面抓取开始。假设我们要采集一个设计工具网站的标题和 Meta 描述。pythonimport requests from bs4 import BeautifulSoup import time # 尊重对方写明身份 headers { User-Agent: Mozilla/5.0 (compatible; DataCollectBot/1.0; https://example.com/bot) } url https://example-design-tool.com try: resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() resp.encoding resp.apparent_encoding # 自动检测编码防乱码 soup BeautifulSoup(resp.text, html.parser) # 提取标题 title soup.title.string.strip() if soup.title else # 提取 Meta 描述 desc_tag soup.find(meta, attrs{name: description}) description desc_tag[content].strip() if desc_tag else # 提取关键词 keywords_tag soup.find(meta, attrs{name: keywords}) keywords keywords_tag[content].strip() if keywords_tag else print(f标题: {title}) print(f描述: {description}) print(f关键词: {keywords}) except Exception as e: print(f抓取失败: {e})这么一段代码就完成了一次基础采集。三、导航站标配批量抓取 自动分类通常是一次性处理几百上千个 URL。我们用异步 IO 来提速同时对采集到的内容做一个简单的自动分类。1. 异步批量抓取asyncio aiohttppythonimport asyncio import aiohttp from bs4 import BeautifulSoup async def fetch_site(session, url, semaphore): async with semaphore: # 控制并发别把对方打挂了 try: async with session.get(url, timeout15) as resp: html await resp.text() soup BeautifulSoup(html, html.parser) title soup.title.string.strip() if soup.title else desc_tag soup.find(meta, attrs{name: description}) desc desc_tag[content].strip() if desc_tag else return { url: url, title: title, description: desc, status: resp.status } except Exception as e: return {url: url, error: str(e), status: 0} async def batch_fetch(urls, concurrency10): semaphore asyncio.Semaphore(concurrency) async with aiohttp.ClientSession( headers{User-Agent: DataCollectBot/1.0} ) as session: tasks [fetch_site(session, url, semaphore) for url in urls] results await asyncio.gather(*tasks) return results # 使用示例 urls [ https://site-a.com, https://site-b.com, https://site-c.com, ] results asyncio.run(batch_fetch(urls, concurrency10))这样一来200 个网站大概 30 秒就能采完基本信息。2. 基于规则的自动分类拿到标题和描述后我们可以用关键字匹配给网站打分类标签。CATEGORY_RULES { 设计资源: [图标, icon, 素材, 配色, 字体, mockup, sketch, figma], 前端开发: [react, vue, css, javascript, 组件库, 框架, 前端], 效率工具: [在线工具, 转换, 生成器, 压缩, 格式, 截图, 录屏], 产品灵感: [灵感, 参考, 案例, behance, dribbble, landing page], 开源项目: [github, 开源, open source, repo], } def classify_site(title, description): text (title description).lower() for cat, keywords in CATEGORY_RULES.items(): if any(kw in text for kw in keywords): return cat return 其他这个分类器虽然简单但准确率能做到 80% 以上。剩下的 20% 复杂情况完全可以交给运营人工修正效率已经大大提升。四、进阶全站整页抓取 动态渲染有时候目标网站是纯前端渲染的React/Vue 做的 SPArequests 只能拿到一个空壳。这时就要用 Playwright 模拟真实浏览器。from playwright.sync_api import sync_playwright def crawl_spa_with_playwright(url): with sync_playwright() as p: browser p.chromium.launch(headlessTrue) page browser.new_page() page.goto(url, wait_untilnetworkidle) # 等网络请求全部完成 html page.content() browser.close() soup BeautifulSoup(html, html.parser) # sites [] for card in soup.select(.site-card): # 具体的 class 名根据实际情况调整 sites.append({ name: card.select_one(.site-name).text.strip(), url: card.select_one(.site-url)[href], desc: card.select_one(.site-desc).text.strip(), }) return sites别滥用——只在自己需要的少数场景下开浏览器并发控制到 2-3 个不然内存和对方的服务器都扛不住。五、整站爬取实战带翻页的全量数据采集自动翻页 去重 断点续爬。import hashlib visited_urls set() def crawl_all_pages(base_url, max_pages50): results [] for page_num in range(1, max_pages 1): url f{base_url}?page{page_num} print(f正在采集第 {page_num} 页...) # 这里用刚才写的 fetch_site 或 Playwright page_results fetch_page_data(url) if not page_results: print(没有更多内容翻页结束) break for item in page_results: # 用 URL 做去重 fingerprint hashlib.md5(item[url].encode()).hexdigest() if fingerprint not in visited_urls: visited_urls.add(fingerprint) results.append(item) time.sleep(2) # 礼貌等待 return results注意整站爬取要格外谨慎控制频率只采一次用于离线分析不要持续高频抓取。六、反爬与应对那些你一定要知道的“潜规则”做多了你就发现不是所有网站都开大门欢迎你。常见反爬及应对策略反爬手段表现应对方式封 IP连续请求后 403换 IP 代理池、降低频率、加随机延迟UA 检测requests 默认 UA 直接 403换成正常浏览器 UA最好也加上 Accept-LanguageCookie 验证不带 Cookie 拿不到数据先访问首页拿 Cookie再带 Cookie 请求数据页前端混淆数据用 JS 加密Playwright 直接取渲染后 DOM或者逆向 JS高成本验证码滑块/图形验证码到这个地步就停手吧换个数据源在采集收录的设计资源站时我就遇到过有些国外设计站对爬虫特别敏感直接封数据中心 IP。解决方案很简单——切到住宅代理并且把每次请求间隔拉到 5 秒以上。七、数据存储与清洗让采集到的数据真正可用采回来的原始数据必然是脏的标题带\n、描述是空、URL 少了协议头……必须清洗。import re def clean_site_data(raw_item): title raw_item.get(title, ) # 去除多余空白符 title re.sub(r\s, , title).strip() # 截断过长标题 if len(title) 100: title title[:97] ... url raw_item.get(url, ) # 补全协议头 if url.startswith(//): url https: url elif not url.startswith(http): url https:// url desc raw_item.get(description, ) desc re.sub(r\s, , desc).strip()[:300] return { url: url, title: title, description: desc, category: raw_item.get(category, 其他), crawl_time: raw_item.get(crawl_time, ) }清洗完成后写入 CSV 或数据库这套数据就可以直接喂给后台导入接口了。八、从采集到上线假设我们现在采集了 500 个优质设计类网站完整流程是收集种子 URL从 GitHub Awesome List、Product Hunt、设计师推特等渠道获取候选列表。批量爬取信息用 asyncio 异步抓标题、描述、关键词。自动分类 去重规则匹配分类URL 指纹去重。质量过滤标题为空或描述太短的低质量站点直接丢弃。人工抽检随机抽 20 个验证分类和描述是否靠谱。提交通过提交入口或者直接联系运营一次性导入。这套流水线跑通后一个人的效率抵得上一个编辑团队。之所以能维持高频更新、分类准确背后大概率也是类似的自动化采集流程在运转。