1. 项目概述:古籍目录采集的价值与挑战
古籍数字化是当前文化传承领域的重要趋势,而公开古籍目录作为第一道门户,蕴含着丰富的文献线索。我曾参与过多个省级图书馆的古籍数字化项目,发现手工整理目录效率极低——某次为3000多条记录分类竟耗费团队两周时间。这正是Python爬虫技术能大显身手的场景。
通过自动化采集公开古籍目录,我们可以:
- 建立结构化数据库,实现秒级检索(传统手工查询需要5-10分钟/次)
- 分析文献分布规律(如某朝代文献集中保存于哪些机构)
- 发现关联文献(通过作者、版本等字段自动关联)
- 监测新增古籍上线情况(自动对比历史数据)
以国家图书馆"中华古籍资源库"为例,其分页目录包含超过10万条记录,手动采集需要至少200工时,而爬虫程序可在2小时内完成,且数据可直接导入数据库。
2. 技术方案设计
2.1 目标网站分析
我们以典型的古籍目录网站为例,其特点包括:
- 分页采用传统?page=参数形式
- 每条记录包含:题名、作者、版本、藏馆等字段
- 无复杂反爬机制,但需要控制请求频率
- 页面结构采用table布局
python复制# 示例页面URL结构
base_url = "http://example.com/ancient-books?page="
2.2 技术选型对比
| 工具 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Requests+BS4 | 轻量简单 | 需自行处理分页 | 中小规模采集 |
| Scrapy | 内置分页支持 | 学习曲线陡峭 | 大规模分布式采集 |
| Selenium | 可处理JS渲染 | 性能低下 | 动态加载页面 |
考虑到古籍目录通常为静态页面,我们选择Requests+BeautifulSoup组合,配合:
- Pandas进行数据清洗
- tqdm显示进度条
- fake-useragent模拟浏览器头
3. 核心代码实现
3.1 分页采集模块
python复制import requests
from bs4 import BeautifulSoup
from tqdm import tqdm
import pandas as pd
from time import sleep
from fake_useragent import UserAgent
def get_page(page_num):
ua = UserAgent()
headers = {'User-Agent': ua.random}
try:
response = requests.get(
f"http://example.com/ancient-books?page={page_num}",
headers=headers,
timeout=10
)
if response.status_code == 200:
return response.text
return None
except Exception as e:
print(f"Page {page_num} error: {str(e)}")
return None
3.2 数据解析模块
python复制def parse_page(html):
soup = BeautifulSoup(html, 'html.parser')
records = []
table = soup.find('table', {'class': 'book-list'})
for row in table.find_all('tr')[1:]: # 跳过表头
cols = row.find_all('td')
record = {
'title': cols[0].text.strip(),
'author': cols[1].text.strip(),
'dynasty': cols[2].text.strip(),
'library': cols[3].text.strip(),
'identifier': cols[4].text.strip()
}
records.append(record)
return records
3.3 主控流程
python复制def main():
all_data = []
max_page = 50 # 可通过分析页面获取实际页数
for page in tqdm(range(1, max_page+1)):
html = get_page(page)
if html:
all_data.extend(parse_page(html))
sleep(1) # 礼貌性延迟
df = pd.DataFrame(all_data)
df.to_excel('ancient_books_catalog.xlsx', index=False)
4. 结构化处理技巧
4.1 数据清洗要点
古籍目录常见问题及处理方法:
- 异体字统一(如"説"转"说")
python复制variant_chars = {'説':'说', '羣':'群'} df['title'] = df['title'].replace(variant_chars, regex=True) - 朝代信息标准化
python复制dynasty_map = {'明刻本':'明代', '清抄本':'清代'} df['dynasty'] = df['dynasty'].map(dynasty_map).fillna(df['dynasty']) - 多作者分割
python复制df['authors'] = df['author'].str.split(';')
4.2 高级结构化方案
对于更复杂的古籍信息,可采用嵌套结构:
python复制{
"title": "资治通鉴",
"metadata": {
"authors": ["司马光"],
"dynasty": "宋",
"versions": [
{
"type": "刻本",
"year": "元至正年间",
"library": "国家图书馆"
}
]
}
}
5. 反爬策略与伦理考量
5.1 合规采集要点
-
严格遵守robots.txt规定
python复制# 示例:检查目标网站robots.txt import urllib.robotparser rp = urllib.robotparser.RobotFileParser() rp.set_url("http://example.com/robots.txt") rp.read() print(rp.can_fetch("*", "/ancient-books")) -
请求频率控制策略
- 固定延迟:
time.sleep(1.5) - 随机延迟:
sleep(random.uniform(1, 3)) - 自适应延迟(根据响应时间调整)
- 固定延迟:
-
请求头优化方案
python复制headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Referer': 'http://example.com/' }
5.2 数据使用建议
- 存储原始HTML与清洗后数据分离
- 添加数据来源标注
- 重要字段保留原始文本(即使经过清洗)
- 建立数据更新机制而非全量重复采集
6. 性能优化实战
6.1 并发采集方案
python复制from concurrent.futures import ThreadPoolExecutor
def crawl_page_range(start, end):
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(tqdm(
executor.map(get_page, range(start, end+1)),
total=end-start+1
))
return results
# 分片采集示例
page_ranges = [(1,10), (11,20), (21,30)]
all_pages = []
for start, end in page_ranges:
all_pages.extend(crawl_page_range(start, end))
6.2 断点续采实现
python复制import os
import pickle
CHECKPOINT_FILE = 'crawl_progress.pkl'
def save_checkpoint(page_num, data):
with open(CHECKPOINT_FILE, 'wb') as f:
pickle.dump({'last_page': page_num, 'data': data}, f)
def load_checkpoint():
if os.path.exists(CHECKPOINT_FILE):
with open(CHECKPOINT_FILE, 'rb') as f:
return pickle.load(f)
return None
7. 常见问题排查
7.1 典型错误处理表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 返回空数据 | 页面结构变更 | 更新CSS选择器 |
| 403错误 | IP被封禁 | 更换UserAgent,增加延迟 |
| 连接超时 | 服务器限制 | 使用代理IP轮换 |
| 数据错位 | 字段顺序变化 | 添加字段验证逻辑 |
7.2 调试技巧
- 保存问题页面快照
python复制with open(f'error_page_{page_num}.html', 'w', encoding='utf-8') as f: f.write(html) - 使用浏览器开发者工具验证选择器
- 添加字段存在性检查
python复制title = col[0].text.strip() if col[0] else '未知题名'
8. 项目扩展方向
8.1 多源数据整合
python复制def merge_catalogs(sources):
base_df = pd.DataFrame()
for src in sources:
df = pd.read_csv(src['file'])
df['source'] = src['name']
base_df = pd.concat([base_df, df])
return base_df
8.2 可视化分析
- 朝代分布柱状图
- 馆藏机构词云
- 作者关系网络图
python复制import matplotlib.pyplot as plt
df['dynasty'].value_counts().plot(kind='bar')
plt.title('古籍朝代分布')
plt.savefig('dynasty_dist.png')
在实际项目中,我发现最耗时的往往不是爬虫开发,而是后续的数据清洗和标准化。建议在采集阶段就尽可能保留原始文本,同时建立完善的字段映射规则。对于大型项目,可以考虑使用Scrapy框架配合Pipeline进行自动化清洗,这比后期用Excel手动处理要高效得多。
