剑桥词典API实战:用Python爬取单词释义、发音和例句(附完整代码)
剑桥词典数据自动化采集实战:Python逆向工程与语音库构建指南
在语言学习工具开发领域,剑桥词典因其权威释义和标准发音成为众多开发者的首选数据源。本文将揭示如何通过Python技术栈实现词典数据的自动化采集,包括释义解析、发音文件下载以及例句提取,为开发者构建个性化语言学习工具提供完整技术方案。
1. 环境准备与目标分析
构建自动化采集系统前,需要明确技术路线和工具链。不同于官方API调用,我们采用网页解析与文件规律分析相结合的方式实现数据获取。
基础环境配置:
# 核心依赖库 import requests from bs4 import BeautifulSoup import re import json from urllib.parse import quote import os # 语音下载专用库 import wget from pydub import AudioSegment剑桥词典网页结构特点分析:
- 单词查询URL格式固定:
https://dictionary.cambridge.org/dictionary/english/{word} - 发音文件存储路径遵循特定命名规则
- 词性分类使用特定CSS类标识
- 例句存在于结构化HTML标签中
提示:实际操作前建议配置合理的请求间隔(如3-5秒),避免触发反爬机制
2. 网页结构逆向工程
2.1 基础请求与反爬策略
实现稳健的数据采集需要处理网络请求的各种异常情况:
def safe_request(url, max_retry=3): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Accept-Language': 'en-US,en;q=0.9' } for attempt in range(max_retry): try: resp = requests.get(url, headers=headers, timeout=10) if resp.status_code == 200: return resp elif resp.status_code == 429: time.sleep(10) # 请求过频处理 continue except Exception as e: print(f"Attempt {attempt+1} failed: {str(e)}") time.sleep(5) return None2.2 关键数据定位技术
通过分析DOM结构,我们发现主要数据分布在特定标签中:
def parse_word_page(html): soup = BeautifulSoup(html, 'html.parser') # 词性区块定位 pos_blocks = soup.find_all('div', class_='pr entry-body__el') data = { 'word': soup.find('span', class_='hw dhw').text, 'pronunciation': {}, 'definitions': [], 'examples': [] } # 发音文件提取逻辑 uk_audio = soup.find('source', {'type': 'audio/mpeg', 'src': re.compile('uk_pron')}) us_audio = soup.find('source', {'type': 'audio/mpeg', 'src': re.compile('us_pron')}) if uk_audio: data['pronunciation']['uk'] = uk_audio['src'] if us_audio: data['pronunciation']['us'] = us_audio['src'] # 释义提取逻辑 for block in pos_blocks: pos = block.find('span', class_='pos dpos').text def_blocks = block.find_all('div', class_='def-block ddef_block') for def_block in def_blocks: definition = def_block.find('div', class_='def ddef_d db').text example = def_block.find('span', class_='eg deg') data['definitions'].append({ 'pos': pos, 'definition': definition, 'example': example.text if example else None }) return data3. 发音文件系统化采集
3.1 发音文件URL规律解析
通过分析数百个单词的发音文件路径,我们发现以下命名模式:
| 组件 | 示例 | 生成规则 |
|---|---|---|
| 首字母目录 | u | 单词首字母 |
| 二级目录 | ukn | 单词前2-3字母 |
| 三级目录 | uknot | 单词前4-5字母 |
| 文件名 | uknotif012 | 单词+数字后缀 |
实现代码:
def build_pronunciation_url(word, accent='uk'): """根据单词生成可能的发音文件路径""" first_char = word[0].lower() two_chars = word[:2].lower() if len(word) > 1 else first_char three_chars = word[:3].lower() if len(word) > 2 else two_chars base_url = f"https://dictionary.cambridge.org/media/english/" path = f"{accent}_pron/{first_char}/{two_chars}/{three_chars}/" # 尝试常见数字后缀 for i in range(1, 10): filename = f"{three_chars}{i:02d}.mp3" full_url = base_url + path + filename if requests.head(full_url).status_code == 200: return full_url return None3.2 语音文件处理技巧
获取音频文件后,通常需要进行格式转换和标准化处理:
def process_audio(filepath): """统一音频格式和质量""" audio = AudioSegment.from_file(filepath) # 标准化处理 audio = audio.set_frame_rate(22050) audio = audio.set_channels(1) audio = audio.normalize() # 保存为标准格式 output_path = filepath.replace('.mp3', '_processed.wav') audio.export(output_path, format='wav', bitrate='16k') return output_path4. 数据存储与应用集成
4.1 结构化存储方案
采集的数据建议采用混合存储策略:
def save_word_data(word_data, storage_dir): """保存单词数据到文件系统""" word = word_data['word'] # 创建单词目录 word_dir = os.path.join(storage_dir, word.lower()) os.makedirs(word_dir, exist_ok=True) # 保存JSON元数据 with open(os.path.join(word_dir, 'meta.json'), 'w') as f: json.dump({ 'definitions': word_data['definitions'], 'examples': word_data['examples'] }, f, indent=2) # 下载发音文件 for accent, url in word_data['pronunciation'].items(): filename = f"{word}_{accent}.mp3" wget.download(url, os.path.join(word_dir, filename)) return word_dir4.2 数据库集成示例
对于大规模应用,建议使用MongoDB存储结构化数据:
from pymongo import MongoClient class DictionaryDB: def __init__(self, uri='mongodb://localhost:27017/'): self.client = MongoClient(uri) self.db = self.client['dictionary'] self.words = self.db['words'] def insert_word(self, word_data): """插入或更新单词数据""" filter_ = {'word': word_data['word']} self.words.replace_one(filter_, word_data, upsert=True) def get_word(self, word): """查询单词数据""" return self.words.find_one({'word': word.lower()})5. 高级技巧与异常处理
5.1 多义词处理策略
剑桥词典中多义词的页面结构更为复杂,需要特殊处理:
def handle_polysemy(soup): """处理多义词情况""" entries = [] main_entry = soup.find('div', class_='pr entry-body__el') if not main_entry: # 多义词情况 sense_blocks = soup.find_all('div', class_='pr dsense') for block in sense_blocks: headword = block.find('span', class_='hw dsense_hw') definition = block.find('div', class_='def ddef_d db') if headword and definition: entries.append({ 'variant': headword.text, 'definition': definition.text }) return entries5.2 反爬规避实战方案
长期运行的采集系统需要更完善的反反爬策略:
class SmartRequest: def __init__(self): self.session = requests.Session() self.proxies = self._init_proxies() self.current_proxy = 0 self.last_request = 0 def _init_proxies(self): # 实现代理池初始化逻辑 return ['http://proxy1:port', 'http://proxy2:port'] def get(self, url): # 请求速率控制 elapsed = time.time() - self.last_request if elapsed < 2.5: # 保持合理间隔 time.sleep(2.5 - elapsed) try: proxy = {'http': self.proxies[self.current_proxy]} resp = self.session.get(url, proxies=proxy, timeout=10) if resp.status_code == 403: self._rotate_proxy() return self.get(url) self.last_request = time.time() return resp except: self._rotate_proxy() return self.get(url) def _rotate_proxy(self): self.current_proxy = (self.current_proxy + 1) % len(self.proxies)6. 实战应用案例
6.1 构建离线单词本
将采集的数据转化为Anki记忆卡片:
def create_anki_card(word_data): """生成Anki导入格式数据""" front = f"<h1>{word_data['word']}</h1>" back = "<div class='definitions'>" for def_item in word_data['definitions']: back += f""" <div class='definition'> <span class='pos'>{def_item['pos']}</span> <p>{def_item['definition']}</p> {f"<blockquote>{def_item['example']}</blockquote>" if def_item['example'] else ""} </div> """ back += "</div>" # 添加发音按钮 if word_data['pronunciation'].get('uk'): back += f""" <audio controls> <source src="{word_data['pronunciation']['uk']}" type="audio/mpeg"> </audio> """ return [word_data['word'], front, back]6.2 集成到翻译工具
将词典数据与翻译API结合:
class EnhancedTranslator: def __init__(self): self.db = DictionaryDB() self.cache = {} def lookup(self, word): # 先检查缓存 if word in self.cache: return self.cache[word] # 查询本地数据库 db_result = self.db.get_word(word) if db_result: self.cache[word] = db_result return db_result # 实时采集 url = f"https://dictionary.cambridge.org/dictionary/english/{quote(word)}" resp = safe_request(url) if resp: data = parse_word_page(resp.text) self.db.insert_word(data) self.cache[word] = data return data return None