想当年我还是个研一小萌新,超爱逛牛客网、脉脉、小红书,扒拉各种面试经验贴。看到那些热门面试题和“八股文”,就忍不住把自己代入进去,琢磨着怎么回答才完美。
但是,问题也随之而来:题目多如牛毛,新题层出不穷,记性又不太好使,简直让人头大!🤯
于是,一个大胆的想法在我脑海中萌芽——“为啥不自己动手,丰衣足食,搞一个专属面试题库呢?”
不过嘛,人生就是这么充满戏剧性。刚把数据集吭哧吭哧搜集完毕,实习的橄榄枝就砸了过来,然后…然后这个伟大的项目就被我暂时“鸽”了 ( ̄▽ ̄)/。
虽然项目没完全落地,但从扒拉数据到清洗数据的整个过程,我感觉自己像是打通了任督二脉,学到了不少好东西。所以,独乐乐不如众乐乐,今天就把它写成博客,跟小伙伴们分享一下这段奇妙的旅程吧!
万事开头难,获取数据是搭建任何数据集的“万里长征第一步”。想拿到网页上的宝贝数据,咱们通常有两大招式:
模拟网络请求(API大法好!): 这招就像是给网站API发个“暗号”,用 Chrome开发者工具 或是 Charles 这种抓包神器,找到那些藏在深闺的API接口。然后,再模仿用户的Cookie或Session,就能直接拿到相对干净、原始的数据啦。 写代码的时候也超简单,JavaScript里有 axios
或 fetch
,Python里有 requests
库,几行代码就搞定,主打一个直接高效!
无头浏览器(“隐身”访问大法): 有时候网站的“保安系统”(反爬虫机制)太强,API大法不好使了。这时候,就轮到无头浏览器出马了!它就像一个没有界面的浏览器,能模拟用户的真实操作,直接“看”到啥就抓啥。 常用的“隐身衣”有 Playwright 和 Selenium。当然啦,就算披了隐身衣,也别太浪,不然还是会被网站识破是爬虫哦!所以,写代码时得耍点小聪明,比如随机User-Agent、随机请求头、随机访问间隔,让你的爬虫看起来更像个“真人”。
辛辛苦苦爬下来的原始数据,打开一看,哇塞!经常是这样的(随便抓了个脱敏数据瞅瞅):
<section data-v-5ea596c4="" data-v-405cdcf2=""><!----> <div data-v-5ea596c4="" class="tw-flex"><h1 data-v-5ea596c4="" class="tw-mb-5 tw-font-medium tw-text-size-title-lg-pure tw-text-gray-800">宇宙厂一面</h1> <!----></div> <!----> <!----> <div data-v-5ea596c4="" class="feed-content-text tw-text-gray-800 tw-mb-4 tw-break-all">base北京 2024年4月1日<br>上来三道算法题<br>1.最大公共前缀<br>2.最大子序列<br>3.重复字符串<br><br>开始聊项目细节,看到我的项目涉及数据存储相关,问我存储的内容;看到有智能代理相关项目,询问具体工作内容...<br>后面开始从项目延伸出考察知识点:<br>流式输出;<br>大文件上传<br>场景题:如果每次向智能程序询问的问题都是一样,你会怎么减少性能开销。<br>询问对前端框架的熟悉情况,我说对vue框架了解更多,面试官询问了该框架的相关内容,我阐述了一些基础响应式原理相关内容后,未被要求继续展开。<br>询问学习前端的时长和学习方式。<br>反问环节<br><br>全程1个小时9分钟,前面算法题用时接近40分钟(理论预计30分钟完成)。整体感觉还行,面试官没有深入追问更多细节。<br><br><a href="/creation/subject/3871fa77df8f40faa40474d054dbe5e3" target="_blank" class="subject font-green" nc-subject-type="0" nc-subject-tag-id="0">#面试问题记录#</a> <a href="/creation/subject/e7e06396e28b43b2928970fed37fc9ea" target="_blank" class="subject font-green" nc-subject-type="0" nc-subject-tag-id="0">#面试被问“你的缺点是什么?”怎么答#</a> </div> <!----> <!----> <!----> <!----> <!----> <!----> <!----> <div data-v-5ea596c4="" class="tw-my-3"><!----> <!----></div></section>
是不是感觉眼睛要瞎了?🤯 这么多HTML标签,简直没法直接用!
这时候,就需要给数据“洗个澡”,做个预处理。但注意哦,不能粗暴地把所有标签都删掉,那样会破坏文本原有的段落结构和语义。聪明的做法是,把HTML标签替换成空格或者空行,这样既能去掉“杂质”,又能保留原汁原味的语义结构。
一番操作下来,当当当当~ 数据就变得清爽多啦:
宇宙厂一面
base北京 2024年4月1日
上来三道算法题
1.最大公共前缀
2.最大子序列
3.重复字符串
开始聊项目细节,看到我的项目涉及数据存储相关,问我存储的内容;看到有智能代理相关项目,询问具体工作内容...
后面开始从项目延伸出考察知识点:
流式输出;
大文件上传;
场景题:如果每次向智能程序询问的问题都是一样,你会怎么减少性能开销。
询问对前端框架的熟悉情况,我说对vue框架了解更多,面试官询问了该框架的相关内容,我阐述了一些基础响应式原理相关内容后,未被要求继续展开。
询问学习前端的时长和学习方式。
反问环节
全程1个小时9分钟,前面算法题用时接近40分钟(理论预计30分钟完成)。整体感觉还行,面试官没有深入追问更多细节。
#面试问题记录#
#面试被问“你的缺点是什么?”怎么答#
经过一番“洗礼”,我们拿到了一堆纯文本。但这些文本就像一锅大杂烩,各种信息(公司、时间、问题、难度…)都混在一起,还是非结构化的,机器宝宝表示看不懂,没法直接处理。咋办呢?
这时候就要祭出“知识抽取”大法,把这些乱糟糟的文本变成整整齐齐的结构化JSON数据啦!
咱们可以召唤强大的大语言模型(LLM),再配合点“提示工程”(Prompt Engineering)的小技巧。比如,用经典的 COSTAR 框架来组织我们的“咒语”(提示词),并告诉LLM输出格式的必须是JSON格式。看我的“咒语”示例:
# CONTEXT(上下文)
你将处理一段来自面试经验分享的非结构化文本。这些文本通常包含面试公司信息、面试时间、面试内容、技术问题、面试难度等多种元素。文本已经过初步清洗,但仍需进一步结构化处理。
# OBJECTIVE(目标)
将非结构化的面试文本转换为结构化的JSON数据,提取关键信息。
# STYLE(风格)
- 精确:确保提取的信息准确无误
- 完整:尽可能提取所有相关信息
- 结构化:遵循严格的JSON结构
- 简明:去除冗余信息,保留核心内容
# TONE(语调)
专业、中立、客观,不添加个人评价或判断。
# AUDIENCE(受众)
数据处理者和开发人员,他们将使用这些结构化数据构建面试题库。
# RESPONSE(响应)
请返回一个JSON数组,包含以下字段(如果信息不存在则使用null):
[{
"company": "面试公司名称",
"level": "面试轮次(如一面、二面等)",
"tag": "问题类别(如数据结构与算法、数据库、计算机网络等)",
"difficulty": "难度等级(1-5的整数)",
"question": "具体的面试问题内容(每条数据仅包含一个问题)",
"time": "面试日期(YYYY-MM-DD格式)"
},...]
不过有个小tips:咱们通常要处理海量数据,一条一条请求LLM太慢,并发请求又怕钱包顶不住。好消息是,现在很多LLM服务都提供了批量API(Batch API)的选项!你可以把所有请求打包成一个 .jsonl
文件(每行一个JSON请求),一次性丢给模型。等模型慢慢处理完(它们可能会挑个非高峰期,还能用上提示词缓存之类的省钱大法),再把结果一口气还给你。这种方式不仅方便快捷,还超级省钱,简直是我等“贫民窟”开发者的福音啊!
经过LLM的“点化”,我们就能得到像下面这样漂亮的JSON数据啦(这里只展示一条原始文本提取出的多条面试题数据):
[
{
"company": "宇宙厂",
"level": "一面",
"tag": "数据结构与算法",
"difficulty": 3,
"question": "最大公共前缀",
"time": "2024-04-01"
},
{
"company": "宇宙厂",
"level": "一面",
"tag": "项目经历",
"difficulty": 2,
"question": "聊项目细节",
"time": "2024-04-01"
},
{
"company": "宇宙厂",
"level": "一面",
"tag": "数据库",
"difficulty": 2,
"question": "项目涉及数据存储相关,存储的内容是什么?",
"time": "2024-04-01"
},
{
"company": "宇宙厂",
"level": "一面",
"tag": "计算机网络",
"difficulty": 3,
"question": "流式输出的实现或原理",
"time": "2024-04-01"
},
{
"company": "宇宙厂",
"level": "一面",
"tag": "技术知识",
"difficulty": 4,
"question": "大文件上传的实现或方案",
"time": "2024-04-01"
},
{
"company": "宇宙厂",
"level": "一面",
"tag": "性能优化",
"difficulty": 4,
"question": "如果每次向智能程序询问的问题都是一样,你会怎么减少性能开销?",
"time": "2024-04-01"
},
...
]
拿到LLM处理过的数据,是不是以为可以高枕无忧了?Too young, too simple! 😉
仔细瞅瞅,可能会发现标签、公司名、面试轮次还是五花八门,有点“逼死强迫症”的意思。比如,“宇宙厂”、“抖Y”、“字J”、“bytedance”其实说的都是一家公司。
这时候,就需要我们拿出“火眼金睛”,手动或者用脚本(Excel大法、Python/JS小脚本都行)再精炼一下。
比如,把“宇宙厂”、“抖音”、“字节”、“bytedance”这些都统一成“字节跳动”;面试轮次也规范成“一面”、“二面”、“三面”这种标准格式。
清洗完毕,数据就更规整啦:
{
"id": "3w5s8a",
"company": "字节跳动",
"level": "一面",
"tag": "计算机网络",
"difficulty": 3,
"question": "流式输出的实现或原理",
"time": "2024-04-01"
}
当我们的数据要在网络上传输时,体积大小可是个敏感问题,毕竟流量也是钱啊!仔细观察一下JSON数据,你会发现好多key(像"company"
, "question"
这些)都是重复的,占了不少空间。
这时,机智的我们就可以给key“瘦瘦身”:
比如,把完整的key缩写成单个字母:
{
"i": "3w5s8a",
"c": "字节跳动",
"l": "一面",
"t": "计算机网络",
"d": 3,
"q": "流式输出的实现或原理",
"m": "2024-04-01"
}
更狠一点,直接省略key,用数组形式(当然,这需要前后端约定好顺序):
["3w5s8a","字节跳动","一面","计算机网络",3,"流式输出的实现或原理","2024-04-01"]
这么一顿操作,能轻松减少近30%的数据体积,是不是很香?💰
基于上面折腾出来的这套数据集,我顺手搭了个前端面试刷题小站:FE Rusher (没错,就是为了督促自己刷题,哈哈!)。
代码也光荣地躺在GitHub上了:ni00/FERusher,数据集在仓库的 ./data/fe.json
路径下,欢迎围观和Star⭐!
这个小破站有啥好玩的呢?
🛠️ 技术栈揭秘:
所以呀,如果你也想搞一个后端版、算法版,甚至产品经理版的刷题网站,简直不要太简单!直接在 data
文件夹下塞入你的JSON数据,稍微改改代码就能跑起来啦。期待看到更多小伙伴的创意作品!🥳