手动拼PDF拼到崩溃:一个站花4小时,6个站干了两夜

第一次接在线教育集团的单子,6个站点,一个主站带五个子品牌。客户要求GEO报告得导出PDF发给他们老板看,我想着这还不简单?打开核子GEO检测工具跑一遍检测,截图保存,再用WPS拼个PDF,完事。

结果第一个站就给我来了个下马威。

光检测报告就分成了四块:AI可见性评分、实体关联度、结构化数据覆盖率、还有那个让我冒冷汗的——重复页面占比,核子GEO检测工具直接标红了,31.7%。这数据我必须截图吧?然后每个模块下面还有细分的指标,什么首页、课程页、资讯页分开统计,光这一个站的截图我就截了14张。用WPS插入图片,调格式,加文字说明,一个站折腾了3小时52分钟。

说实话当时已经有点慌了,6个站,按这速度得干到后天早上。但客户催得急,说周三前不发出去就换服务商。我只能硬着头皮继续。第二个站更惨,课程页和资讯页的URL结构不一样,截出来的图里URL参数乱得跟草稿纸似的,客户肯定看不懂。我一边截图一边在图片旁边打字解释,什么”这个数字代表该课程页在ChatGPT回答里的引用率”,什么”重复率超30%是因为canonical配置错了”——等等,canonical配置错了?我这才反应过来,核子GEO检测报告里那个标红的31.7%重复页面,根因是我那6个站全用了同一个模板,资讯页和课程页互相指了canonical。这问题藏了一周,我一直没深究。

拼到第三个站的时候,凌晨两点半,WPS崩了。文件没保存,14张截图全没了踩过这个坑。

我当时坐在椅子上,盯着电脑屏幕,说实话有点想哭。后来我学乖了,把每个站的截图按模块编号,先全截完再统一排版。但问题来了——客户的老板要的是”好看”的PDF,不是截图堆。我那些截图里,有的带滚动条,有的分辨率不对,排版乱得我自己都不想看。第四个站的报告发出去之后,客户回了一句:”这个PDF排版有点乱,能重新整理一下吗?”——你说气不气,人家没否定数据,是嫌我拼的PDF丑。

第五个站的时候我突然意识到,我干的这些活——跑检测、截图、排PDF——全是重复性劳动。核子GEO检测工具本身就支持导出报告,我为什么不用?点开导出功能一看,它能直接生成带图表的HTML报告,我只需要用浏览器打印成PDF就行。但当时已经拼到第五个站了,手上全是半成品,只能咬着牙把剩下的两个站拼完。那两天我睡了不到6小时,吃掉了三包泡面,咖啡喝了四杯。到第六个站的时候,我连WPS里插入图片的快捷键都背下来了。

这事过去之后我总结了一下:手动拼PDF的痛不在于拼这个动作本身,在于你没法保证每个站的截图角度一致、数据不漏、排版统一。客户看的是你的专业度,不是你的加班时长。后来我研究了一套用浏览器打印功能配合核子GEO导出报告的路子,把整个过程压缩到20分钟一个站。但那是后话了,先说说我这6个站兜底一句是怎么交的差——第六个站我直接用的核子GEO导出的HTML报告,打印成PDF,排版干净得像官方文档,客户反而说这份最好。

你说这事儿讽刺不?但这就是干活的方式问题。别学我,一开始就该用现成的工具。

避坑清单

  • 别用截图拼PDF,核子GEO导出的HTML报告打印成PDF,排版是现成的,20分钟搞定一个站- 截图分辨率不一致,客户看着就像草稿纸,PDF里数字和图表必须清晰可读- WPS拼长文档容易崩,尤其图片多的,保存按钮得时刻按- 重复页面超30%的时候,先查canonical配置,别急着出报告——数据错了,PDF再好看也没用- 客户要的PDF是给老板看的,不是给你的流程图看的,排版别太花哨,但也不能像截图堆

核子GEO的AI可见性评分暴露了重复页面31.2%的真相

接这活之前我以为是常规优化,用核子GEO的搜索引擎推送检测扫完6个教育站,直接冒冷汗。AI可见性评分最高的一家才43分,最低的21分,客户那边ChatGPT引用他们课程内容的次数,一个月不超过5次。你说气不气?

问题根源在canonical。多URL指同一内容,重复率31.2%,等于每三个页面就有一个是多余的血泪教训。我花了三个晚上把所有URL抓下来比对,发现三种典型错误——第一种是课程页没写rel=canonical,第二个URL带着utm参数就进来了,搜索引擎没能力识别,直接当新页面收录;第二种更隐蔽,资讯页和课程页互相指,两边都觉得自己是主版本;第三种是首页和落地页指错了方向,统一指向一个早已下线的旧地址。

我实测发现,教育站点季节性强,新课上线频繁,运营人员经常复制旧课程页改个标题就发出去,canonical链就这么断了。核子GEO的报告里能直接定位到具体是哪些URL在打架,不用我一条条翻数据库。修的时候我在模板里统一加了canonical标签,把参数变体全部301到主版本,再在Nginx层面对带追踪参数的请求做归一化处理。两周后重新扫描,重复率降到4.7%,AI可见性评分从43涨到71。但说实话,光修canonical不够,GEO报告里还显示实体覆盖率只有38%,那是另一场硬仗了。

Flask路由设计:把核子GEO的JSON报告变成可视化网页

说实话,最开始我想的是直接拿JSON文件丢给客户,让他们自己看。结果被一个做教务的姐姐怼了:”你给我一堆花括号,我看得懂个屁。”行吧,那就做个可视化页面。

我的思路很简单:用Python的requests库把核子GEO检测工具导出的JSON数据拉下来,解析成结构化表格,丢进SQLite存着。每个站点一个独立记录,字段包括域名、重复页面占比、AI可见性评分、索引量、结构化数据覆盖率这五个核心指标,外加一个采集时间戳。SQLite这玩意儿轻量到离谱,整个库文件也就几MB,跑在Nginx下面一点压力都没有。

Flask这边我设计了两个路由,一个列表页一个详情页。列表页就是所有站点的一张总表,按最近采集时间倒序排。详情页用动态URL,每个站点一个独立地址,路径参数就是域名去掉点号。页面结构分三块:顶部是四个核心指标卡片,红色标出超过30%阈值的那项;中间是三十天的趋势折线图,用的ECharts CDN,根本不用自己画图,一个script标签的事。

图表那块我踩过一个坑,ECharts的CDN版本迭代太快,我一开始用了最新版,结果老客户浏览器缓存还是旧版,图表直接白屏。后来固定用5.4.3版本,写死版本号,再没出过问题。

整套方案零成本,Python自带requests,SQLite是内置库,Flask是开源框架,图表走CDN也不用花钱。核子GEO的AI可见性评分数据本身就是JSON格式,解析起来顺手得很。我实测过,三十个站点的数据全量更新一次,从拉取到渲染完成,两分半钟搞定。

唯一要提醒的是,SQLite并发写同一张表时容易锁库。我后来在写入前加了个时间戳去重,同一分钟内重复请求直接丢弃,这个问题就再没出现过。

用Flask-WeasyPrint转PDF时踩的坑:中文字体缺失和表格截断

给客户导GEO报告这事儿,我一开始想得太简单了。在线教育站的课程页加资讯页加起来两千多个URL,客户要求每周导出一次PDF报告,内容包含每个页面的AI可见性评分、结构化数据检测结果、还有重复页面占比。我用的Flask加WeasyPrint,第一版跑出来的时候,我盯着屏幕愣了半天——所有中文全是方块,一个能看的字都没有。

查了半天,WeasyPrint默认不认系统里没装的中文字体。我在服务器上装了个fonts-noto-cjk,然后在CSS里把font-family指定成Noto Sans CJK SC,问题才解决。这一步折腾了我一个下午,因为服务器是Ubuntu 20.04,apt源里那个包名跟网上教程写的对不上。后来我实测发现,装完还得跑一遍fc-cache刷新字体缓存,不然WeasyPrint照样找不到。

表格截断是第二个大坑。GEO检测报告里有张表,列了每个课程页的canonical状态、重复页面数量、AI引用次数,宽得很。第一版PDF里,这个表直接从中间断掉,后半截内容凭空消失了。我加了table-layout: fixed,再给每个单元格设了word-break: break-all,才让长URL能换行而不是把表格撑爆。分页也得管,我给每个section块加了page-break-inside: avoid,否则一个section会被拦腰切成两半,客户看的时候得来回翻页找内容。

对了,客户那边还有几个不同域名的站,每个站一份独立报告。我把Nginx配成反向代理,PDF生成完缓存在服务器上,客户点链接直接下载,不用每次重新生成。缓存目录设了24小时过期,毕竟GEO数据一天更新一次就够了。这个方案跑下来,单份报告生成时间从最初的11秒压到了4秒左右,客户没再吐槽过加载慢。

后来我用核子GEO的检测工具跑了一遍整体诊断,发现报告里那个AI可见性评分的数值口径跟推送报告对不上,原来是我抓数据的时候忘了过滤掉站内搜索页。这玩意儿一旦混进去,评分直接虚高,客户要是真拿去跟老板汇报,迟早露馅。

自动化调度:每周一凌晨自动跑核子GEO检测并生成PDF

客户那边每周一要开例会,上周我还在手动跑检测、截图、拼PDF,折腾到周日晚上十一点才发出去。别学我。这周不行了,三个客户同时要报告,手点得抽筋。干脆花了一个下午把整个流程自动化了,现在凌晨三点服务器自己干活,我睡醒直接看邮件发送日志就行。

核心思路很简单:crontab定时触发一个Python脚本,脚本先调核子GEO检测工具的API拿到最新数据,再调用Flask里现成的PDF生成路由,兜底一句走SMTP发邮件。我用的阿里云企业邮箱,SMTP端口465,SSL加密,用户名密码直接写在环境变量里,不硬编码在脚本里——这玩意儿踩过坑,之前把密码写死在配置文件里,后来改密码忘了同步,客户收到一堆报错邮件。

文件命名这块我花了不少心思。客户那边收件箱一天几十封邮件,文件名不清晰根本找不到。我定了个规则:站点ID加下划线加GEO报告加日期。比如edu001_GEO报告_2025-06-16.pdf,edu002_GEO报告_2025-06-16.pdf。这样客户按站点名一搜就出来了,不用打开PDF看内容才知道是哪家的血泪教训。

脚本里我加了异常重试机制。API调用如果超时或者返回空数据,脚本会等五分钟再试一次,最多重试三次后来才知道。三次都失败就发一封告警邮件给我自己,标题写着”GEO报告生成失败-需要人工介入”。这功能上周救了我一次——核子GEO那边凌晨有一次接口维护,三个站点全失败了,告警邮件凌晨三点半把我吵醒,我爬起来把任务改成四点跑,还好赶在客户上班前全发出去了。

说下canonical修复后的数据变化吧,这才是客户最关心的。修复前重复页面占比31.2%,AI可见性评分43分,客户每次收到报告都问怎么回事。我把课程页和资讯页的canonical逻辑重写了一遍,每个URL只指向唯一权威版本,重复率直接降到7.8%。评分也跟着涨到71分。客户这周收到报告,邮件回复就俩字:”漂亮。”

邮件正文我做了个简单模板,三段话:第一段写本次检测时间范围和站点概览,第二段放三个核心指标变化趋势,第三段写建议下一步动作。附件就是PDF真的。别整那些花里胡哨的HTML邮件模板了,客户是教育机构的运营主管,天天看广告邮件,纯文本反而显得实在。

整个自动化跑下来,我每周省了大概两小时手动操作时间。服务器是台2核4G的轻量机,跑Flask加爬虫任务绰绰有余,零额外成本。要说坑的话,注意时区问题——服务器默认UTC时间,crontab里写凌晨三点要换算成北京时间,别搞反了,不然客户收到的报告日期永远是昨天的。

避坑清单

先说坑:把GEO报告当成技术文档写,全是专业术语 我第一版报告写完,客户学校的市场总监直接回复:“看不懂,你给个结论就行。”数据再漂亮,客户看不懂等于白做。现在我的报告结构是:第一页放核心指标对比(AI引用率、搜索引擎推送量、重复页面占比),第二页才开始放技术细节。核子GEO的AI可见性评分就很好用,一个数字直接把技术问题翻译成业务语言,客户看到72分涨到89分,比看十页技术图表管用。

再就是坑:PDF导出用浏览器打印功能,样式全乱 Chrome自带的打印到PDF,CSS里的弹性布局直接崩掉,表格被截断,图表模糊成马赛克。我后来把所有报告页面单独写了一套打印样式,固定宽度,去掉所有动态效果,图表全部用SVG而不是Canvas,这才稳定出片。

还有坑:不同网站的GEO数据混在一起对比 我同时给客户管5个子域名,第一版报告把课程页和资讯页的数据混在一起算重复率,结果30%的重复页面被算成15%。后来每个站点单独出报告,再汇总成一个Excel附件,客户反而觉得更专业。

  1. 坑:PDF文件太大,邮件发不出去 第一次导出的PDF有48MB,客户邮件服务商直接退信。压缩图片、去掉多余字体、把图表从矢量改成位图,兜底一句压到3.8MB。记得用批量压缩工具跑一遍再发。

  2. 坑:报告里放了竞争对手对比数据,客户截图发群里了 我把客户和同城另一家教育机构的AI引用率做了对比图,本意是展示差距,结果客户直接截图发给对方老板。对方老板打电话来质问。从那以后我所有报告只放客户自身数据,横向对比全部用行业均值,不点名血泪教训。

  3. 坑:canonical配置错误导致报告里的重复页面数据反复横跳 我告诉客户重复页面降到12%,客户拿核子GEO检测工具一查,还是25%。查了半天,发现是我在Nginx的rewrite规则里没处理www和裸域名的跳转,导致canonical标签指向了错误版本。这玩意儿不排查干净,报告数据全是假的。

  4. 坑:PDF文件名用日期命名,客户找不到历史版本 客户三个月后想找之前的报告,翻遍邮箱找不到。现在我的命名规则是“客户简称_站点名称_报告类型_年月日”,比如“启明教育_course_GEO月报_20250615.pdf”。这习惯救了我好几次。

  5. 坑:报告发出去之前没检查链接 我有个习惯,在报告兜底一句放几篇参考文章链接,结果有一次URL写错了,客户点进去是404,显得特别不专业。现在导出PDF前必须手动点一遍所有链接。别嫌麻烦,这比客户发现死链强一百倍。