步天歌
1
一个正在开发的聚合搜索工具「易查辞典」(以下简称「易查」),想收集下用户的需求。
网站地址:
实机演示:
简单来说,其实就是一个「导航页」,但和一般的搜索导航站相比,「易查」不仅支持自定义,而且还多了一步「分析搜索结果」——判断对应网站是否存在有意义的搜索结果,并将结果显示在页面上。
另外还(会)实现一些功能:
- 分析整个句子,(基于收集的数据)预测用户可能想查的单词,尤其是光标附近的单词/词组/句型
- 日语会使用 Mikann 作为分词器
- 英语和其他语言会使用 spaCy 作为分词器
- 云同步
- 查词记录支持导出到 Anki
- 通过爬虫和 sitemaps 的缓存,提高「分析搜索结果」的速度
另外,公开一份我自己维护的在线辞典网站,欢迎大家补充。
https://hbq08jzhrer.sg.larksuite.com/wiki/QqCSwwHnCid6ADkJqqzlOQeIgHh?from=from_copylink&sheet=LCVb9e
最后,项目使用 AGPL-3.0 许可证开源,欢迎 PR!
5 个赞
hua
2
抓取词典网站单词是否存在的逻辑可以放在浏览器内并行执行,现在服务端执行的,可能会让目标网站觉得是爬虫,刚才查 apple ,/search 花了 16 秒。
1 个赞
步天歌
3
研究过这种方案,但没太搞懂怎么绕过同源限制,只能丢后台啦。
- 直接 fetch 到非同源站点需要目标站开启 CORS(Access-Control-Allow-Origin),否则浏览器会拦截响应内容,无法做“是否存在该词”的判定。
不过还是得怪我,后端直接用的最粗暴的方法:
def get_for_url(
url: str,
headers: dict | None,
not_found_text: str,
) -> bool:
"""向网站发起请求,如果网站返回的数据中不包含指定的关键字,返回 True,否则返回 False。
Args:
url: 需要发起请求的网站,只支持 Get 类型的提交。
headers: 发起请求时使用的请求头。
not_found_text: 当返回的结果中包含该参数的文字时,视为给网站未搜索到相关内容。
Returns:
当返回的网页中不包含指定的文字时返回 True,否则返回 False。
"""
result_text = do_get_request(headers, url)
if not_found_text in result_text:
return False
return True
def do_get_request(headers: dict | None, url: str) -> str:
"""向网站发起 Get 请求,返回网页内容。
Args:
headers: 发起请求时使用的请求头。
url: 需要发起请求的网站,只支持 Get 类型的提交。
Returns:
网站返回的内容。
"""
if headers is None:
headers = {
"User-Agent": DEFAULT_HEADER,
}
result = requests.get(url, headers=headers)
return result.text
本来是想换成异步的 httpx + lxml (查询「No results found for “XXX”.」所在的 XPath所在元素是否存在),但后来发现最快的还是提前用爬虫抓一遍和清洗 sitemaps 得到的缓存……所以把这个半成品分享出来想撺掇点坛友帮我踩坑233
步天歌
6
优化了下性能,现在查 apple,只需要4秒左右了 
技术细节
- 后端从 Flask 切换到支持异步的 Quart 框架
- 部署平台从 Heroku 迁移到 DigitalOcean App Platform,服务器冷启动时间缩短,异步并发效率也更高了。
接下来的目标
把查询耗时尽量优化到 1 秒内。
- 基于 《简明英汉字典》的数据,主动查询常见的英语单词,缓存查询结果。
- 保留用户提交的查询记录,分析其中的高频查询词和查询网站,并添加到上面的缓存中。