\n 牛客网:{nc_cnt} 篇\n 小红书:{xhs_cnt} 篇\n 合计:{nc_cnt + xhs_cnt} 篇\n
\n{"repo": "bcefghj/interview-experience-spider", "files": {"config.example.json": {"79a681316ee347dd00245cf0c99550d0a96ca4f3": {"t": 1785603940.1530845, "n": 1040, "h": "5de10c3d4fee6689", "c": "{\n \"_comment\": \"复制此文件为 config.json,填入你的配置。config.json 已加入 .gitignore,不会被上传。\",\n\n \"nowcoder\": {\n \"queries\": [\n \"字节飞书后端实习\",\n \"字节飞书 golang 面经\",\n \"字节飞书服务端实习面经\",\n \"字节飞书暑期实习面经\"\n ],\n \"max_pages\": 3,\n \"delay_seconds\": 0.8\n },\n\n \"xiaohongshu\": {\n \"enabled\": true,\n \"_cookie_help\": \"见 docs/get-xhs-cookies.md,Cookie 约1天过期\",\n \"cookies\": {\n \"a1\": \"填入你的 a1 cookie 值\",\n \"web_session\": \"填入你的 web_session cookie 值\",\n \"webId\": \"填入你的 webId cookie 值\",\n \"gid\": \"填入你的 gid cookie 值\"\n },\n \"queries\": [\n \"字节飞书面试\",\n \"字节飞书后端面经\",\n \"字节飞书实习 golang\"\n ],\n \"max_pages\": 3,\n \"delay_seconds\": 0.8\n },\n\n \"filter\": {\n \"mode\": \"all\",\n \"_mode_options\": \"all = 全部保留(去掉运营销售等) | backend = 只保留后端/Go相关\",\n \"backend_keywords\": [\"后端\", \"golang\", \"go语言\", \"服务端\", \"backend\", \"全栈\"],\n \"exclude_keywords\": [\"运营\", \"销售\", \"ux设计\", \"产品经理\", \"数据分析\", \"市场营销\"]\n },\n\n \"output\": {\n \"dir\": \"./output\",\n \"title\": \"面试备考手册\",\n \"formats\": [\"md\", \"html\"],\n \"max_content_length\": 2000\n }\n}\n"}}, "generate_report.py": {"c984b42597f21f331271aaa47f8a19b17fcf9e5c": {"t": 1785603941.445472, "n": 13706, "h": "55b5d6e43a7b4f5e", "c": "#!/usr/bin/env python3\n\"\"\"\nMD/HTML 报告生成器\n用法:\n python3 generate_report.py # 使用 config.json\n python3 generate_report.py --nc output/nc.json --xhs output/xhs.json\n python3 generate_report.py --title \"字节飞书备考\" --filter backend\n\"\"\"\nimport json, re, os, sys, argparse\n\ntry:\n import markdown as md_lib\nexcept ImportError:\n print(\"❌ 缺少依赖: pip3 install markdown\")\n sys.exit(1)\n\nBACKEND_KWS = [\"后端\", \"golang\", \"go语言\", \"服务端\", \"backend\", \"全栈\", \"开发工程师\"]\nEXCLUDE_KWS = [\"运营\", \"销售\", \"ux设计\", \"产品经理\", \"数据分析\", \"市场营销\", \"人力资源\"]\n\n\ndef filter_posts(posts: list, mode: str = \"all\") -> list:\n if mode == \"backend\":\n return [d for d in posts if any(\n kw in (d.get(\"title\", \"\") + d.get(\"content\", \"\")).lower()\n for kw in BACKEND_KWS\n )]\n else: # all\n return [d for d in posts if not any(\n kw in (d.get(\"title\", \"\") + d.get(\"content\", \"\")).lower()\n for kw in EXCLUDE_KWS\n )]\n\n\ndef build_md(nc_posts: list, xhs_posts: list, title: str,\n max_len: int = 2000) -> str:\n lines = []\n L = lines.append\n\n nc_cnt = len(nc_posts)\n xhs_cnt = len(xhs_posts)\n\n L(f\"# {title}\")\n L(\"\")\n L(f\"> 牛客网 **{nc_cnt}** 篇 + 小红书 **{xhs_cnt}** 篇,共 **{nc_cnt + xhs_cnt}** 篇真实面经\")\n L(\"\")\n L(\"---\")\n L(\"\")\n\n # 高频考点\n L(\"## 第一部分:高频考点速查\")\n L(\"\")\n\n topics = [\n (\"Golang / Go\", [\n \"GMP 调度模型(G/M/P 的含义 + work stealing)\",\n \"Goroutine vs 线程的区别\",\n \"Channel 底层实现(hchan 结构 + 阻塞原理)\",\n \"map 并发不安全 → sync.Map / Mutex 方案\",\n \"GC 三色标记法 + 写屏障\",\n \"slice 扩容规则(<256 翻倍,>256 增长 1.25)\",\n \"defer LIFO + 闭包陷阱\",\n \"interface 底层 iface/eface\",\n \"内存逃逸分析\",\n ]),\n (\"MySQL\", [\n \"B+ 树索引结构 + 为什么不用 B 树\",\n \"聚簇索引 vs 非聚簇索引(回表/覆盖索引)\",\n \"联合索引最左前缀 + 索引失效场景\",\n \"MVCC 原理(undo log + read view)\",\n \"四种隔离级别 + 幻读解决方案\",\n \"redo log / undo log / bin log 区别\",\n \"行锁 / 表锁 / 间隙锁 / Next-Key Lock\",\n \"慢 SQL 排查:explain + 索引优化\",\n ]),\n (\"Redis\", [\n \"为什么快:单线程 + IO多路复用 + 内存操作\",\n \"五种数据结构底层实现\",\n \"缓存穿透/击穿/雪崩 三问 ★★★\",\n \"RDB vs AOF 持久化\",\n \"分布式锁:SET NX EX + Redlock\",\n \"内存淘汰:LRU vs LFU\",\n \"热 Key / 大 Key 处理\",\n \"集群:主从/哨兵/Cluster\",\n ]),\n (\"计算机网络\", [\n \"TCP 三次握手/四次挥手 ★★★\",\n \"TIME_WAIT(2MSL 原因)\",\n \"TCP 可靠传输:序号/确认/重传/流控/拥塞控制\",\n \"HTTP 1.0/1.1/2.0/3.0 区别\",\n \"HTTPS TLS 握手过程\",\n \"URL → 页面全流程 ★★★\",\n \"DNS 解析(UDP 53 端口)\",\n ]),\n (\"操作系统\", [\n \"进程/线程/协程区别\",\n \"IO 多路复用:select/poll/epoll 区别\",\n \"虚拟内存 + 缺页中断\",\n \"死锁四条件 + 预防\",\n ]),\n (\"系统设计\", [\n \"秒杀系统设计(防超卖)★★★\",\n \"分布式 ID(雪花算法)\",\n \"限流:令牌桶/漏桶/滑动窗口\",\n \"短链服务设计\",\n \"敏感词过滤(Trie)\",\n ]),\n (\"算法(手撕)\", [\n \"★★★ LC3 无重复最长子串(滑动窗口)\",\n \"★★★ LC128 最长连续序列(HashSet O(n))\",\n \"★★★ LC25 K个一组翻转链表\",\n \"★★★ LC146 LRU 缓存\",\n \"★★ LC102 二叉树层序遍历\",\n \"★★ LC76 最小覆盖子串\",\n ]),\n ]\n\n for i, (topic, qs) in enumerate(topics):\n L(f\"### {i+1}. {topic}\")\n L(\"\")\n for q in qs:\n L(f\"- {q}\")\n L(\"\")\n\n L(\"---\")\n L(\"\")\n\n # 牛客面经\n L(f\"## 第二部分:牛客网面经(共 {nc_cnt} 篇,节选30篇原文)\")\n L(\"\")\n for i, d in enumerate(nc_posts[:30]):\n title = d.get(\"title\", \"无标题\")\n url = d.get(\"url\", \"\")\n content = d.get(\"content\", \"\").strip()\n if len(content) > max_len:\n content = content[:max_len] + \"\\n\\n...(内容截断,完整见原链接)\"\n L(f\"### {i+1}. {title}\")\n L(\"\")\n if url:\n L(f\"> 来源:[{url}]({url})\")\n L(\"\")\n L(content)\n L(\"\")\n L(\"---\")\n L(\"\")\n\n # 小红书面经\n L(f\"## 第三部分:小红书面经(共 {xhs_cnt} 篇,节选30篇原文)\")\n L(\"\")\n for i, d in enumerate(xhs_posts[:30]):\n title = d.get(\"title\", \"无标题\")\n url = d.get(\"url\", \"\")\n content = d.get(\"content\", \"\").strip()\n if len(content) > max_len:\n content = content[:max_len] + \"\\n\\n...(内容截断)\"\n L(f\"### {i+1}. {title}\")\n L(\"\")\n if url:\n L(f\"> 来源:[小红书]({url})\")\n L(\"\")\n L(content)\n L(\"\")\n L(\"---\")\n L(\"\")\n\n # 附录\n L(\"## 附录:全部面经索引\")\n L(\"\")\n L(f\"### 牛客网({nc_cnt} 篇)\")\n L(\"\")\n for i, d in enumerate(nc_posts):\n L(f\"{i+1}. [{d.get('title','无标题')}]({d.get('url','')})\")\n L(\"\")\n L(f\"### 小红书({xhs_cnt} 篇)\")\n L(\"\")\n for i, d in enumerate(xhs_posts):\n L(f\"{i+1}. [{d.get('title','无标题')}]({d.get('url','')})\")\n\n return \"\\n\".join(lines)\n\n\n_CSS = \"\"\"\n:root{--bg:#0f1117;--sb:#161b22;--card:#1c2128;--bdr:#30363d;--tx:#e6edf3;--mt:#8b949e;--ac:#58a6ff;--ac2:#3fb950;--ac3:#d29922;}\n*{box-sizing:border-box;margin:0;padding:0;}\nbody{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;background:var(--bg);color:var(--tx);display:flex;min-height:100vh;font-size:15px;line-height:1.7;}\n#sb{width:270px;min-width:270px;background:var(--sb);border-right:1px solid var(--bdr);position:fixed;top:0;left:0;bottom:0;overflow-y:auto;padding:16px 0;z-index:100;}\n#sbh{padding:0 14px 14px;border-bottom:1px solid var(--bdr);margin-bottom:6px;}\n#sbh h2{font-size:12px;color:var(--mt);text-transform:uppercase;letter-spacing:.8px;}\n#sb ul{list-style:none;}\n#sb a{display:block;padding:5px 14px;color:var(--mt);text-decoration:none;font-size:13px;border-left:3px solid transparent;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:all .15s;}\n#sb a:hover,#sb a.on{color:var(--ac);background:rgba(88,166,255,.07);border-left-color:var(--ac);}\n.h2a{font-weight:600;color:var(--tx)!important;}\n.h3a{padding-left:26px!important;font-size:12px!important;}\n#mn{margin-left:270px;flex:1;max-width:940px;padding:36px 48px;}\n.hero{background:linear-gradient(135deg,#1a2a4a,#0d1b2e);border:1px solid #1f6feb;border-radius:12px;padding:22px 26px;margin-bottom:28px;}\n.hero-t{font-size:20px;color:var(--ac);font-weight:700;margin-bottom:6px;}\n.badge{display:inline-block;padding:3px 9px;border-radius:20px;font-size:12px;font-weight:600;margin:3px 3px 0 0;}\n.bb{background:rgba(88,166,255,.15);color:#58a6ff;border:1px solid rgba(88,166,255,.3);}\n.bg{background:rgba(63,185,80,.15);color:#3fb950;border:1px solid rgba(63,185,80,.3);}\n.by{background:rgba(210,153,34,.15);color:#d29922;border:1px solid rgba(210,153,34,.3);}\nh1{display:none;}\nh2{font-size:19px;color:var(--ac);margin:36px 0 14px;padding-bottom:7px;border-bottom:1px solid var(--bdr);scroll-margin-top:20px;}\nh3{font-size:15px;color:var(--ac2);margin:22px 0 10px;scroll-margin-top:20px;}\np{margin:0 0 10px;}\nul,ol{padding-left:20px;margin:7px 0 10px;}\nli{margin:3px 0;}\na{color:var(--ac);text-decoration:none;}\na:hover{text-decoration:underline;}\nhr{border:none;border-top:1px solid var(--bdr);margin:28px 0;}\nblockquote{border-left:3px solid var(--ac3);padding:7px 14px;background:rgba(210,153,34,.07);border-radius:0 6px 6px 0;color:var(--mt);margin:10px 0;font-size:14px;}\ntable{width:100%;border-collapse:collapse;margin:14px 0;font-size:14px;}\nth{background:var(--card);color:var(--ac);padding:8px 11px;border:1px solid var(--bdr);text-align:left;}\ntd{padding:8px 11px;border:1px solid var(--bdr);vertical-align:top;}\ntr:nth-child(even) td{background:rgba(255,255,255,.015);}\ncode{font-family:'JetBrains Mono','Fira Code',Consolas,monospace;font-size:13px;background:var(--card);border:1px solid var(--bdr);padding:2px 5px;border-radius:4px;color:#e3b341;}\npre{background:var(--card);border:1px solid var(--bdr);border-radius:8px;padding:14px 18px;overflow-x:auto;margin:10px 0 14px;}\npre code{background:none;border:none;padding:0;color:#e6edf3;}\n#srch{width:100%;padding:7px 11px;background:var(--bg);border:1px solid var(--bdr);border-radius:6px;color:var(--tx);font-size:13px;margin-bottom:10px;outline:none;}\n#srch:focus{border-color:var(--ac);}\n#top{position:fixed;bottom:22px;right:22px;background:var(--ac);color:#000;width:38px;height:38px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:17px;font-weight:bold;box-shadow:0 3px 10px rgba(0,0,0,.4);text-decoration:none;opacity:.8;}\n#top:hover{opacity:1;transform:scale(1.1);}\n#prog{position:fixed;top:0;left:270px;right:0;height:3px;background:linear-gradient(90deg,var(--ac),var(--ac2));transform-origin:left;transform:scaleX(0);transition:transform .1s;z-index:200;}\n@media(max-width:768px){#sb{display:none;}#mn{margin-left:0;padding:18px;}#prog{left:0;}}\n\"\"\"\n\n_JS = \"\"\"\nwindow.addEventListener('scroll',()=>{\n const d=document.documentElement;\n document.getElementById('prog').style.transform=`scaleX(${d.scrollTop/(d.scrollHeight-d.clientHeight)||0})`;\n});\nconst hs=document.querySelectorAll('h2,h3');\nconst ls=document.querySelectorAll('#sb a');\nhs.forEach(h=>new IntersectionObserver(es=>{es.forEach(e=>{\n if(e.isIntersecting){ls.forEach(l=>l.classList.remove('on'));\n const a=document.querySelector(`#sb a[href=\"#${e.target.id}\"]`);\n if(a){a.classList.add('on');a.scrollIntoView({block:'nearest'});}\n }\n});},{rootMargin:'-20px 0px -80% 0px'}).observe(h));\nfunction fs(q){q=q.toLowerCase();document.querySelectorAll('#sb li').forEach(li=>{\n li.style.display=(!q||li.textContent.toLowerCase().includes(q))?'':'none';\n});}\n\"\"\"\n\n\ndef build_html(md_text: str, title: str, nc_cnt: int, xhs_cnt: int) -> str:\n html_body = md_lib.markdown(\n md_text,\n extensions=[\"tables\", \"fenced_code\", \"toc\", \"nl2br\"],\n extension_configs={\"toc\": {\"toc_depth\": \"2-3\"}}\n )\n toc_items = []\n for m in re.finditer(r'
\n 牛客网:{nc_cnt} 篇\n 小红书:{xhs_cnt} 篇\n 合计:{nc_cnt + xhs_cnt} 篇\n
\n