题目重点:从多个 URL 文本文件及 next URL 链读取评论,按负面关键词筛选、按电影归组,说明环去重、文本匹配口径和大规模抓取时的幂等性。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版以 `fetch_text(url)` 返回 `(reviews,next_url)`,其中 review 为 `(movie_id,text)`;这是把网络 I/O 与解析隔离的示例接口,非题目公开格式。
- 关键词按 Unicode casefold 后做子串匹配,任一命中即算负评;题目未说明词边界、语言、否定词或分类模型。
- 同一 URL 只处理一次,防多起点重复与 next 环;相同 review 若在不同文件重复,训练版保留两条,因为没有稳定 review id。
解题路线
- 对每个起点沿 next URL 逐个读取,seen_urls 防重复与环;遇空 next 或已访问 URL 即停止该链。
- 解析出 movie id 和评论正文,统一大小写后逐个检查负面关键词;命中则追加到该电影的结果列表。关键词字典若包含权重或类别,应先向面试官确认如何用于判负,不能把 dict 的值忽略并宣称复现原题。
- 扩容时把 URL 发现与抓取分离:持久队列承载待抓 URL,URL 规范化及去重键防重复,worker 设置超时、重试、域名限速与死信;解析结果按 movie id 分区,写入用稳定 review id 做幂等。大文件流式读取,输出可落对象存储/数据库,避免全部评论留内存。
手动走一遍
训练例 A 包含电影 M1 的‘bad ending’,next 指 B;B 包含 M2 的‘great’和 M1 的‘boring’,next 又指 A。关键词 bad/boring 会收集 M1 两条,M2 不入结果,seen_urls 阻止 A↔B 无限循环。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def collect_negative_reviews(start_urls, fetch_text, keywords):
"""fetch_text(url) returns (reviews, next_url); each review is (movie_id, text)."""
words = {word.casefold() for word in keywords if word}
if not words:
return {}
seen_urls = set()
result = {}
for start in start_urls:
url = start
while url is not None and url not in seen_urls:
seen_urls.add(url)
reviews, url = fetch_text(url)
for movie_id, text in reviews:
folded = text.casefold()
if any(word in folded for word in words):
result.setdefault(movie_id, []).append(text)
return result
复杂度
U 个去重 URL、R 条评论、K 个关键词,朴素子串检测约 O(总文本长度×K);URL 集合 O(U),聚合结果 O(命中评论量)。实际网络时间与文件大小主导;关键词很多时可建多模式匹配自动机,但需先固定词法口径。
易错点与追问
容易失分的边界
- 只遍历初始 URL,漏掉文件中的 next URL。
- next 构成环或多个起点汇合导致重复抓取。
- 把关键词命中当语义情感分类,例如 ‘not bad’ 会产生假阳性。
- 题目仅说 negative keyword dict,却擅自假定字典 value 的权重语义。
- 扩容只加线程,不考虑抓取幂等、目标站限流和下游结果去重。
继续深挖
- 若 next URL 跨域或重定向,如何规范化与防 SSRF?
- 同一电影有百万条评论,如何分页/增量存储?
- 关键词更新后如何重算历史评论?
- 需要保留每条评论的来源 URL 与抓取时间以便审计吗?
