2025最强数据采集指南:用Open Event Scraper实现Google表格到JSON的无缝转换
你是否还在为手动解析Google表格数据而烦恼?面对复杂的会议日程、多轨道演讲安排,人工处理不仅耗时耗力,还容易出错。本文将带你深入了解Open Event Scraper项目,一个专为解析Google表格数据并转换为JSON格式而设计的强大工具。通过本文,你将掌握从环境搭建到高级应用的全过程,轻松应对各类数据采集需求。
读完本文,你将能够:
- 快速搭建Open Event Scraper开发环境
- 理解项目核心架构和工作流程
- 掌握数据解析的关键技术和实现细节
- 成功运行示例并定制自己的解析规则
- 解决常见问题并优化性能
项目概述
Open Event Scraper是一个开源项目,主要用于解析Google Spreadsheet(电子表格)数据,并将其转换为结构化的JSON格式。该项目最初是为FOSSASIA 2016会议设计的,用于处理会议日程、演讲者信息和会场安排等数据。
核心功能
Open Event Scraper提供了以下关键功能:
| 功能 | 描述 |
|---|---|
| Google表格解析 | 支持从Google表格中提取数据,包括多个工作表 |
| 数据结构化 | 将原始表格数据转换为规范化的会话、演讲者和轨道信息 |
| JSON生成 | 输出符合Open Event格式的JSON数据,便于后续处理和展示 |
| 数据验证 | 检查数据一致性,如重复的会话ID |
| 时间处理 | 支持时区转换和时间计算 |
技术栈
项目采用Python作为主要开发语言,结合了多种库和工具:
- 核心语言: Python 2.x(注:可根据需要迁移至Python 3.x)
- 数据处理: csv, json, simplejson
- 网络请求: urllib2
- 时间处理: datetime, pytz
- 数据验证: validators
- 对象序列化: jsonpickle
环境搭建
系统要求
- Python 2.7(推荐使用虚拟环境)
- 网络连接(用于获取Google表格数据)
- Git(用于版本控制)
安装步骤
1. 克隆仓库
git clone https://gitcode.com/gh_mirrors/op/open-event-scraper
cd open-event-scraper
2. 安装依赖
pip install -r requirements.txt
requirements.txt文件内容:
jsonpickle
simplejson
validators
pytz
3. 配置项目
项目无需额外配置,但你可能需要根据自己的需求修改scraper.py中的常量,如:
SHEET_ID: Google表格IDSHEET_VERSIONING_GID: 版本控制工作表IDSESSION_LENGTH: 会话时长(默认30分钟)TZ_LOCAL: 本地时区(默认亚洲/香港)YEAR_OF_CONF: 会议年份(默认2016)
运行项目
./run.sh
run.sh脚本内容非常简单,直接调用主程序:
python ./scraper.py
项目架构
文件结构
open-event-scraper/
├── LICENSE
├── Procfile
├── README.md
├── __init__.py
├── models.py # 数据模型定义
├── package.json
├── parser.py # 解析辅助函数
├── requirements.txt # 依赖列表
├── run.sh # 启动脚本
├── scraper.py # 主程序
├── schedule/ # 调度相关文件
│ ├── generator.js
│ └── schedule.tpl
└── web/ # Web相关文件
└── hook.js
核心模块
1. 数据模型 (models.py)
该文件定义了四个主要类:
Track: 表示会议的一个轨道(如不同主题的分会场)Speaker: 表示演讲者信息Session: 表示一个会议会话Microlocation: 表示微位置信息
class Track(object):
id = 0
header_line = 1
filename = ""
key_color = "#FF4D55"
name = ""
description = ""
track_image_url = "http://lorempixel.com/400/200"
location = ""
gid = ""
order = -1
def __init__(self, id, name, header_line, key_color, location, gid, order):
super(Track, self).__init__()
self.id = id
self.name = name
self.header_line = header_line
self.key_color = key_color
self.track_image_url = "http://lorempixel.com/400/200"
self.location = location
self.gid = gid
self.order = order
2. 主程序 (scraper.py)
这是项目的核心文件,包含了数据获取、解析和JSON生成的主要逻辑。
3. 解析辅助函数 (parser.py)
提供了一些辅助解析功能,如获取LinkedIn URL、处理图片URL等。
工作流程
Open Event Scraper的工作流程可以分为以下几个主要步骤:
- 获取数据: 从Google表格API获取TSV格式的数据
- 解析轨道: 识别不同的会议轨道(Track)
- 解析会话: 为每个轨道解析会话(Session)和演讲者(Speaker)信息
- 数据验证: 检查数据一致性,如重复的会话ID
- 生成JSON: 将结构化数据输出为JSON文件
核心功能实现
1. Google表格数据获取
项目通过Google表格的导出功能获取数据,使用以下URL格式:
def fetch_tsv_data(gid):
base_url = 'https://docs.google.com/spreadsheets/d/' + SHEET_ID + '/export?format=tsv'
url = base_url + '&gid=' + gid
logging.info('GET ' + url)
res = urllib2.urlopen(url)
return res.read()
其中,SHEET_ID是Google表格的唯一标识符,gid是工作表的ID。
2. 轨道解析
轨道(Track)代表会议的不同主题或分会场。解析轨道的主要步骤如下:
def parse_tracklist(track_data):
tracks = []
headers = []
HEADER_LINE = 1
i = 1
track = None
for line in csv.reader(track_data.split("\n"), delimiter="\t"):
if i == HEADER_LINE:
HEADERS = map(str.strip, line)
elif i > HEADER_LINE:
row = create_associative_arr(line, HEADERS)
if not row["Header Line"]:
continue
track = Track(
id = i,
name = row["Track"],
header_line = int(row["Header Line"]),
key_color = row["Key Color"],
location = row["Room"],
gid = row["GID"],
order = i
)
tracks.append(track)
i = i + 1
return tracks
3. 会话和演讲者解析
每个轨道包含多个会话,每个会话可能有一个或多个演讲者。解析逻辑如下:
def parse_sessions(track, session_data):
HEADERS = []
i = 1
speaker = None
session = None
for line in csv.reader(session_data.split("\n"), delimiter="\t"):
if i == track.header_line:
HEADERS = map(str.strip, line)
elif i > track.header_line:
row = create_associative_arr(line, HEADERS)
(res_speaker, res_session) = parse_row(row, speaker, session, track)
if res_speaker is not None:
speaker = res_speaker
if res_session is not None:
session = res_session
i = i + 1
4. 行解析逻辑
解析每一行数据时,需要区分演讲者和会话信息:
def parse_row(row, last_speaker, last_session, current_track):
session_id = row["SessionID"]
# 没有会话ID则跳过此行
if not session_id:
return (None, None)
# 新会话
if last_session is None or last_session.session_id != session_id:
session = Session()
session.session_id = session_id
session.speakers = []
SESSIONS.append(session)
else:
session = last_session
# 解析演讲者信息
if row["Given Name"]:
speaker = Speaker()
speaker.organisation = row["Company, Organization, Project or University"]
speaker.name = (row["Given Name"].strip() + " " + row["Family Name"].strip()).strip()
# 其他演讲者属性...
# ...
# 解析会话信息
session_time = parse_time(row["Date"] + " " + row["Time"])
if session_time is not None:
session.end_time = (session_time + SESSION_LENGTH).isoformat()
if not hasattr(session, 'start_time'):
session.start_time = session_time.isoformat()
# 设置其他会话属性...
# ...
return (speaker, session)
5. 时间处理
项目支持时区转换和时间计算,确保不同地区的用户看到正确的时间:
TZ_UTC = pytz.utc
TZ_LOCAL = pytz.timezone('Asia/Hong_Kong')
SESSION_LENGTH = datetime.timedelta(minutes=30)
def parse_time(time_str):
# 修复年份
if YEAR_OF_CONF not in time_str:
time_str = YEAR_OF_CONF + " " + time_str
for date_format in DATE_FORMATS:
try:
iso_date = datetime.datetime.strptime(time_str, date_format)
except ValueError as e:
pass
else:
break
if iso_date is None:
return None
local_date = TZ_LOCAL.localize(iso_date)
utc_date = local_date.astimezone(TZ_UTC)
return utc_date
6. JSON生成
解析完成后,项目将结构化数据转换为JSON格式:
def write_json(filename, root_key, the_json):
f = open(filename + '.json', 'w')
the_json = simplejson.dumps(simplejson.loads(the_json), indent=2, sort_keys=False)
json_to_write = '{ "%s":%s}' % (root_key, the_json)
f.write(json_to_write)
f.close()
# 使用示例
speakers_json = jsonpickle.encode(SPEAKERS)
write_json('out/speakers', 'speakers', speakers_json)
session_json = jsonpickle.encode(SESSIONS)
write_json('out/sessions', 'sessions', session_json)
tracks_json = jsonpickle.encode(tracks)
write_json('out/tracks', 'tracks', tracks_json)
7. 数据验证
为确保数据质量,项目包含了简单的数据验证功能:
def validate_sessions(sessions):
logging.info('validating')
s_map = {}
dups = []
for session in sessions:
if session.session_id in s_map:
s_map[session.session_id] = s_map[session.session_id] + 1
else:
s_map[session.session_id] = 1
for session_id, count in s_map.iteritems():
if count > 1:
dups.append(session_id)
if len(dups) > 0:
logging.error('Duplicate session ids: ' + (', '.join(dups)))
return False
else:
logging.info('All fine')
return True
运行示例
基本用法
完成环境搭建后,可以通过以下命令运行项目:
./run.sh
run.sh的内容非常简单,直接调用scraper.py:
python ./scraper.py
输出结果
程序运行后,将在项目根目录下创建out文件夹,并生成以下JSON文件:
- speakers.json: 演讲者信息
- sessions.json: 会话信息
- tracks.json: 轨道信息
示例输出(sessions.json片段):
{
"sessions": [
{
"description": "Introduction to Open Event Scraper",
"end_time": "2016-03-11T08:30:00+00:00",
"location": "Main Hall",
"session_id": "S001",
"speakers": [
{
"id": 1,
"name": "John Doe",
"organisation": "FOSSASIA"
}
],
"start_time": "2016-03-11T08:00:00+00:00",
"title": "Open Event Scraper: A Comprehensive Guide",
"track": {
"id": 1,
"name": "Development",
"order": 1
},
"type": "Talk"
},
// 更多会话...
]
}
高级应用与定制
1. 修改表格ID和工作表ID
要解析自己的Google表格,需要修改scraper.py中的以下常量:
SHEET_ID = 'your_google_sheet_id'
SHEET_VERSIONING_GID = 'your_sheet_gid'
你可以从Google表格的URL中获取这些ID:
https://docs.google.com/spreadsheets/d/[SHEET_ID]/edit#gid=[GID]
2. 自定义数据解析规则
如果你的表格结构与默认不同,可以修改解析函数:
parse_tracklist: 修改轨道解析规则parse_sessions: 修改会话解析规则parse_row: 修改行解析逻辑
3. 扩展数据模型
可以通过修改models.py来扩展数据模型,添加新的属性:
class Speaker(object):
def __init__(self):
super(Speaker, self).__init__()
self.twitter = "" # 添加Twitter账号
self.github = "" # 添加GitHub账号
# 其他自定义属性...
4. 集成到其他项目
Open Event Scraper的输出是标准JSON格式,可以轻松集成到其他项目中,如:
- 会议网站:用于展示日程和演讲者信息
- 移动应用:作为后端数据源
- 数据分析工具:用于会议数据统计和分析
常见问题与解决方案
1. 编码问题
问题: 解析非ASCII字符时出现编码错误。
解决方案: 在打开文件时指定编码:
import codecs
f = codecs.open(filename, 'r', 'utf-8')
2. Python版本兼容性
问题: 项目使用Python 2.x,在Python 3.x环境下运行出错。
解决方案: 迁移到Python 3.x,主要修改以下内容:
urllib2→urllib.requestprint语句 →print()函数xrange→range
3. 表格访问权限问题
问题: 无法获取表格数据,出现403错误。
解决方案: 确保Google表格设置为"任何人可查看":
- 打开Google表格
- 点击"分享"按钮
- 设置"链接分享"为"任何有链接的人都可以查看"
4. 数据格式错误
问题: 解析错误,数据格式不符合预期。
解决方案:
- 检查表格结构是否与预期一致
- 启用详细日志,查看错误位置:
logging.getLogger().setLevel(logging.DEBUG)
项目优化与性能提升
1. 缓存机制
为避免重复获取相同的表格数据,可以添加缓存机制:
import os
import time
CACHE_DIR = 'cache'
CACHE_EXPIRE = 3600 # 缓存过期时间(秒)
def fetch_tsv_data(gid):
# 检查缓存
cache_file = os.path.join(CACHE_DIR, f"{gid}.tsv")
if os.path.exists(cache_file) and time.time() - os.path.getmtime(cache_file) < CACHE_EXPIRE:
with open(cache_file, 'r') as f:
return f.read()
# 获取新数据
base_url = 'https://docs.google.com/spreadsheets/d/' + SHEET_ID + '/export?format=tsv'
url = base_url + '&gid=' + gid
logging.info('GET ' + url)
res = urllib2.urlopen(url)
data = res.read()
# 保存缓存
if not os.path.exists(CACHE_DIR):
os.makedirs(CACHE_DIR)
with open(cache_file, 'w') as f:
f.write(data)
return data
2. 多线程处理
对于包含多个工作表的大型表格,可以使用多线程并行获取和解析数据:
import threading
def process_track(track):
logging.info("[Fetching Track] '%s', gid = %s", track.name, track.gid)
data = fetch_tsv_data(track.gid)
logging.info("[Parsing Track] '%s'", track.name)
parse_sessions(track, data)
# 在主程序中使用多线程
threads = []
for track in tracks:
if not track.gid:
continue
t = threading.Thread(target=process_track, args=(track,))
threads.append(t)
t.start()
# 等待所有线程完成
for t in threads:
t.join()
项目贡献与扩展
如何贡献
Open Event Scraper是一个开源项目,欢迎社区贡献。你可以通过以下方式参与:
- 提交bug报告和功能请求
- 提交代码修复和改进
- 编写和改进文档
- 帮助迁移到Python 3.x
未来发展方向
- Python 3.x支持: 迁移代码以支持最新的Python版本
- API集成: 添加对Google Sheets API的直接支持
- 配置文件: 使用配置文件代替硬编码常量
- 数据可视化: 添加简单的数据可视化功能
- 更多输出格式: 支持CSV、XML等其他格式
总结
Open Event Scraper是一个功能强大的数据解析工具,特别适用于从Google表格中提取结构化数据。通过本文的介绍,你已经了解了项目的核心功能、架构设计和实现细节。从环境搭建到高级应用,从基本使用到性能优化,我们覆盖了使用该工具的各个方面。
无论是处理会议数据、收集调研结果,还是任何需要从表格中提取结构化信息的场景,Open Event Scraper都能为你提供高效、可靠的解决方案。希望本文能帮助你更好地理解和使用这个项目,为你的数据处理工作带来便利。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



