1. 项目缘起与整体设计思路QQ空间这玩意儿说起来挺有意思。虽然现在大家发说说的频率远不如十年前但很多人从学生时代到工作初期的碎碎念、照片、心情记录全都沉淀在里面。我身边不少朋友想把这些内容导出来留个纪念或者做个人年度回顾结果发现官方压根没提供导出功能。手动复制几百条说说能把你手指点到抽筋。这时候用 Python 写个轻量爬虫把数据抓下来存进 Excel就成了一个很实际的需求。GetQzonehistory 这个项目就是干这个的。它的核心逻辑并不复杂模拟登录态、翻页请求说说列表、解析返回的 JSON 数据、清洗后写入 Excel。整个链路涉及的技术点包括 HTTP 请求库 requests、数据解析、SQLAlchemy 做中间存储、openpyxl 或 pandas 写 Excel。之所以选 Python是因为它在爬虫和数据处理这两个领域的生态最成熟代码量少调试方便哪怕你只学过基础语法也能看懂。这个项目适合几类人一是想备份自己 QQ 空间内容但不想学太深技术的普通用户二是刚入门 Python 爬虫想找一个真实可操作项目练手的初学者三是需要批量处理社交数据做分析的人。我接下来会把整个项目的设计思路、关键细节、实操步骤和踩坑经验全部拆开讲尽量让不同基础的人都能跟着做出来。1.1 为什么选择轻量爬虫而不是重型框架很多人一提到爬虫就想到 Scrapy觉得不用框架就不专业。但 GetQzonehistory 这个场景完全没必要上 Scrapy。原因有三第一QQ 空间的说说接口是固定的几个 URL不需要分布式调度第二数据量对个人用户来说通常几百到几千条单线程加适当延时完全够用第三Scrapy 的学习曲线对新手不友好光是理解 Item、Pipeline、Middleware 就要花不少时间。用 requests 简单循环的方式代码量可以控制在两百行以内逻辑一目了然。你打开源码就能看到每一步在干什么出了问题也容易定位。这就是“轻量”的价值——不是功能弱而是刚好够用不引入额外复杂度。1.2 数据流向与存储选型整个项目的数据流向是这样的先通过 requests 拿到 JSON 格式的说说数据然后用 SQLAlchemy 把原始数据存进 SQLite 数据库最后从数据库读取并导出到 Excel。为什么要加 SQLAlchemy 这一层直接写 Excel 不行吗可以但有几个问题。第一爬取过程中如果网络中断直接写 Excel 会导致文件损坏或数据不完整第二SQLite 作为中间层可以让你随时中断、续爬已经存过的数据不会重复抓取第三SQLAlchemy 的 ORM 写法让数据字段管理更清晰后续想加字段或改结构也方便。Excel 导出只是最后一步用 pandas 的to_excel一行代码就能搞定。提示如果你只是想快速试一下也可以跳过数据库直接写 Excel但正式跑大量数据时强烈建议保留 SQLAlchemy 这一层。2. 核心细节解析与实操要点2.1 登录态获取整个项目最关键的环节QQ 空间的说说数据不是公开的必须登录后才能访问。所以爬虫的第一步是拿到有效的 Cookie。这里有两种常见做法一是用 selenium 模拟浏览器登录二是手动从浏览器复制 Cookie。我推荐第二种原因很简单——selenium 需要下载对应浏览器驱动还要处理各种验证码和滑块对新手来说坑太多。手动复制 Cookie 虽然看起来“不够自动化”但稳定性高得多而且 Cookie 有效期通常有好几天足够你跑完一次全量抓取。具体操作用 Chrome 打开 QQ 空间登录后按 F12 打开开发者工具切换到 Network 面板刷新页面找到任意一个请求在 Request Headers 里复制 Cookie 字段的完整值。这个值通常很长包含pt2gguin、skey、p_skey等关键字段。把它粘贴到代码里的 headers 字典中即可。注意Cookie 属于敏感信息不要把它提交到公开的代码仓库。建议用环境变量或单独的配置文件存放。2.2 接口分析与翻页逻辑QQ 空间的说说列表接口大致是这样的格式https://user.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6?uin你的QQ号ftype0sort0pos0num20replynum100g_tk计算值callback_preloadCallbackcode_version1formatjsonpneed_private_comment1关键参数说明参数名作用注意事项uin目标 QQ 号填你自己的pos起始位置每页递增第一页为 0num每页条数建议 20太大容易被限制g_tk令牌由 skey 经过特定算法计算得出format返回格式jsonp 需要额外解析g_tk 的计算方法是取 Cookie 中skey的值去掉前两个字符然后经过一个简单的哈希算法。网上有现成的 Python 实现核心代码如下def get_g_tk(skey): hashes 5381 for letter in skey: hashes (hashes 5) ord(letter) return hashes 0x7fffffff翻页逻辑就是循环改变pos的值每次加 20直到返回的数据为空或达到你设定的上限。每页之间建议加 1 到 2 秒的延时避免请求过于频繁。2.3 数据解析与字段提取接口返回的是 JSONP 格式需要先去掉回调函数名和外层括号再用json.loads解析。每条说说包含的字段很多我们主要关注这几个created_time发布时间戳content说说正文commentlist评论列表pic配图列表rt_con转发内容解析时要注意有些说说只有图片没有文字有些是转发别人的内容字段结构会略有不同。建议用.get()方法取字段避免 KeyError。比如content item.get(content, ) created_time item.get(created_time, 0)时间戳需要转换成可读格式用datetime.fromtimestamp即可。图片链接通常有多个尺寸选最大的那个存下来。2.4 Excel 导出pandas 还是 openpyxl导出 Excel 有两种主流方案。pandas 的to_excel最简单适合数据规整的场景openpyxl 更灵活可以设置单元格样式、合并单元格、加超链接。GetQzonehistory 这种项目如果只是把说说内容列出来pandas 足够了。但如果你想做得好看一点比如给表头加背景色、调整列宽、把图片链接变成可点击的超链接那就需要 openpyxl。我个人的做法是先用 pandas 把数据整理成 DataFrame再用 openpyxl 做后处理。这样既享受了 pandas 的便利又能控制最终输出的样式。具体代码后面实操部分会详细写。3. 实操过程与核心环节实现3.1 环境准备与依赖安装先确保你电脑上装了 Python 3.8 或以上版本。打开终端输入python --version确认。然后安装必要的库pip install requests sqlalchemy pandas openpyxl如果你用的是虚拟环境先激活再安装。我习惯用 venvpython -m venv qzone_env source qzone_env/bin/activate # Windows 用 qzone_env\Scripts\activate安装完成后建议再装一个lxml虽然这个项目用不上但以后做其他爬虫解析 HTML 时会用到。3.2 数据库模型设计用 SQLAlchemy 定义一个简单的说说表from sqlalchemy import create_engine, Column, Integer, String, Text, DateTime from sqlalchemy.orm import declarative_base, sessionmaker Base declarative_base() class ShuoShuo(Base): __tablename__ shuoshuo id Column(Integer, primary_keyTrue) created_time Column(DateTime) content Column(Text) images Column(Text) # 多个链接用逗号分隔 comments Column(Text) forward_content Column(Text) engine create_engine(sqlite:///qzone.db) Base.metadata.create_all(engine) Session sessionmaker(bindengine) session Session()这个设计把图片和评论都存成文本用分隔符隔开。虽然不够规范但对个人项目来说够用了。如果你要做得更细可以再拆出图片表和评论表。3.3 爬取主循环实现核心爬取逻辑用一个 while 循环实现import requests import json import time from datetime import datetime headers { Cookie: 你的Cookie值, User-Agent: Mozilla/5.0 ... } pos 0 num 20 while True: url fhttps://user.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6?uin{qq}ftype0sort0pos{pos}num{num}replynum100g_tk{g_tk}callback_preloadCallbackcode_version1formatjsonpneed_private_comment1 resp requests.get(url, headersheaders) text resp.text # 去掉 JSONP 外壳 json_str text[text.index(()1:text.rindex())] data json.loads(json_str) msglist data.get(msglist, []) if not msglist: break for item in msglist: # 解析并存入数据库 ... pos num time.sleep(1.5)这里有几个细节要注意。第一text.index(()和rindex())用来剥离 JSONP 外壳比正则更直观。第二每次请求后 sleep 1.5 秒这是为了避免触发频率限制。第三如果返回的msglist为空说明已经到底了直接 break。3.4 数据清洗与入库解析每条说说时需要处理各种边界情况。比如评论列表可能为空图片列表可能不存在转发内容可能没有。我写了一个清洗函数def parse_item(item): created_time datetime.fromtimestamp(item.get(created_time, 0)) content item.get(content, ).strip() images ,.join([pic.get(url2, ) for pic in item.get(pic, [])]) comments [] for c in item.get(commentlist, []): comments.append(f{c.get(name, )}: {c.get(content, )}) comments_str | .join(comments) forward item.get(rt_con, {}).get(content, ) if item.get(rt_con) else return ShuoShuo( created_timecreated_time, contentcontent, imagesimages, commentscomments_str, forward_contentforward )入库时用session.add()和session.commit()。如果担心重复可以在插入前查一下created_time是否已存在。3.5 导出 Excel 的完整代码从数据库读取数据并导出import pandas as pd from openpyxl import load_workbook from openpyxl.styles import Font, PatternFill, Alignment # 读取数据 query session.query(ShuoShuo).order_by(ShuoShuo.created_time.desc()) rows [] for item in query: rows.append({ 发布时间: item.created_time.strftime(%Y-%m-%d %H:%M:%S), 内容: item.content, 图片链接: item.images, 评论: item.comments, 转发内容: item.forward_content }) df pd.DataFrame(rows) df.to_excel(qzone_shuoshuo.xlsx, indexFalse) # 后处理样式 wb load_workbook(qzone_shuoshuo.xlsx) ws wb.active header_fill PatternFill(start_color4472C4, end_color4472C4, fill_typesolid) header_font Font(colorFFFFFF, boldTrue) for cell in ws[1]: cell.fill header_fill cell.font header_font cell.alignment Alignment(horizontalcenter) ws.column_dimensions[A].width 20 ws.column_dimensions[B].width 60 ws.column_dimensions[C].width 40 ws.column_dimensions[D].width 40 ws.column_dimensions[E].width 30 wb.save(qzone_shuoshuo.xlsx)这段代码先导出基础 Excel再用 openpyxl 调整表头样式和列宽。最终效果是一个带蓝色表头、列宽合理的表格打开就能直接看。4. 常见问题与排查技巧实录4.1 Cookie 失效导致返回空数据这是最常见的问题。表现是程序跑起来不报错但msglist一直是空的。原因通常是 Cookie 过期了或者你复制的时候漏了某个字段。排查方法把请求返回的原始文本打印出来看看是不是包含code:-3000之类的错误码。如果是重新复制 Cookie 即可。实操心得Cookie 里的skey和p_skey是两个不同的字段计算 g_tk 用的是skey别搞混了。我一开始用错字段折腾了半小时才发现。4.2 请求频率过高被限制如果你把 sleep 去掉或者设得太短很快就会被限制访问。表现是返回 403 或者直接连接超时。解决办法很简单把延时加到 2 秒以上并且不要同时开多个线程。个人项目没必要追求速度稳定跑完才是目标。4.3 Excel 写入中文乱码这个问题通常出现在 Windows 系统上用 pandas 写 Excel 时如果编码不对中文会变成乱码。解决办法是确保to_excel时指定encodingutf-8或者直接用 openpyxl 写入。另外文件名也尽量用英文避免路径编码问题。4.4 时间戳转换错误有些说说的created_time字段可能是字符串而不是整数直接fromtimestamp会报错。稳妥的做法是先转成 intts int(item.get(created_time, 0)) created_time datetime.fromtimestamp(ts)4.5 常见问题速查表问题现象可能原因解决方法返回数据为空Cookie 过期重新复制 Cookie403 错误请求频率过高增加 sleep 时间中文乱码编码问题指定 utf-8 或换 openpyxl时间显示 1970时间戳为 0检查字段是否存在图片链接打不开链接过期尽快下载或转存数据库锁死多线程写入改用单线程4.6 独家避坑技巧第一抓取之前先手动翻几页 QQ 空间确认账号能正常访问。有些账号因为长期不用会被要求验证手机号这种情况下爬虫是拿不到数据的。第二图片链接是有有效期的通常几小时到几天。如果你想把图片也保存下来最好在抓取的同时就下载不要等全部抓完再统一下载。第三Excel 单列最大字符数是 32767。如果你的某条说说特别长比如转发了一篇长文可能会被截断。解决办法是把内容拆成多列或者存成 CSV 格式。第四如果你要抓的 QQ 号不是自己的需要对方空间是公开的或者你有权限访问。否则接口会返回权限错误。5. 扩展思路与个人体会这个项目跑通之后其实可以玩出很多花样。比如把导出的 Excel 再做一层分析统计每年发说说的数量、词频、情绪倾向。我试过用 jieba 分词加 wordcloud 生成词云效果挺有意思能看出自己不同阶段关注的东西变化很大。另外如果你想把数据分享给朋友可以把 Excel 转成 Markdown 表格直接贴到博客或笔记软件里。pandas 的to_markdown方法一行就能搞定。不过要注意Markdown 表格对长文本支持不好适合内容较短的场景。我个人在实际操作中的体会是爬虫项目最耗时间的不是写代码而是调试登录态和处理各种边界情况。代码本身可能两小时就写完了但 Cookie 失效、字段缺失、编码问题这些坑可能要花一整天。所以心态要放平遇到问题就打印原始数据一步步排查别想着一次跑通。最后再分享一个小技巧如果你要抓的说说数量很多建议分批次抓比如每次抓 500 条就停第二天再继续。这样既能避免触发限制也不至于因为一次跑太久而中途出错导致前功尽弃。数据库的好处就在这里已经存进去的数据不会丢下次接着跑就行。