浏览器功能定制5:搜索引擎命令
在前面的探索中,主要做了两件事:一是将网页转为 Markdown,而是给浏览器添加了网络拦截能力。再加上 qutebrowser 本质上就是一个大 SDK。我在向解决信息过载问题继续迈进。
今天探索一个新的话题,创建一条新命令,打通搜索引擎。本文以 bing 搜索引擎为例。
基于 qutebrowser 实现流程如下:
- 跳转到搜索引擎
- 解析渲染后的 HTML,提取出信息条目
- 浏览下一页,重复 Step4
- 直至命中结束条件
扩展加载完毕的监听
在自定义命令中,拿到 AbstractTab 代表当前 Tab,调用 load_url 可以跳转地址。
但是对于页面加载完成的监听,AbstractTab 没有提供回调方法。因此修改 qutebrowser 代码,在 AbstractTab 中添加回调注册能力。
首先在 AbstractTab 添加一个数组属性,用于保存回调。
on_load_finish_cbs: List[Callable[[bool], None]] = []
在 AbstractTab 已有的槽函数中,遍历调用:
@pyqtSlot(bool)
def _on_load_finished(self, ok: bool) -> None:
assert self._widget is not None
if self.is_deleted():
return
# 新增
for cb in self.on_load_finish_cbs:
cb(ok)
# ……
置于回调的注册与删除,图省事,调用方直接访问 on_load_finish_cbs
即可。
这样,命令可以形成如下的框架:
def feed_bing(cur_tab: browsertab.AbstractTab,
keyword: str):
url = f"https://cn.bing.com/search?q={keyword}"
def on_load_finish(ok: bool):
cur_tab.on_load_finish_cbs.remove(on_load_finish)
if not ok:
return
cur_tab.on_load_finish_cbs.append(on_load_finish)
cur_tab.load_url(QUrl(url))
新命令 bing
接下来创建一条新命令,就叫 bing,其特点是通过交互式命令来输入关键词。
@cmdutils.register(instance='command-dispatcher', scope='window')
@cmdutils.argument('keyword')
def bing(self, keyword=None):
feed_bing(self._cntwidget(), keyword)
这样,在 qutebrowser 中输入 :bing ubuntu
即可跳转到搜索引擎。
提取页面元素
我能拿到 HTML。如何将页面内容高效提取出来呢?答案是 XPath。我的 XPath 技能仍然比较生疏,花了不少时间才提取成功,如果熟练的话,这个过程会很快。
封装函数:
def extract_info(html: str):
tree = etree.HTML(html)
results = tree.xpath('//ol[@id="b_results"]/li')
for result in results:
title = result.xpath('string(.//h2)')
url = result.xpath('.//h2/a[1]/@href')
if len(url) == 0:
continue
description = result.xpath('string(.//p)')
print("===================")
print(title)
print(url)
print(description)
print("===================")
效果:
===================
Install | Flutter
['https://docs.flutter.dev/get-started/install']
Web1 day ago · Install Flutter and get started. Downloads available for Windows, macOS, Linux, and ChromeOS operating systems.
===================
===================
安装和环境配置 - Flutter 中文文档 - Flutter 中文开发者网站 ...
['https://flutter.cn/docs/get-started/install']
WebAug 5, 2023 · Flutter安装和上手起步教程, 下载 Windows、macOS、Linux 和 ChromeOS 系统的 Flutter SDK。
===================
翻页
qutebrowser 中有一个神奇命令——navigate,它专门处理这种多页页面的上一页、下一页问题。
首先,通过 functools.partial
封装出一个简版的下一页函数:
next_page_func = functools.partial(self.navigate, 'next')
整体逻辑代码如下
cur_page = 0
def feed_bing(cur_tab: browsertab.AbstractTab,
keyword: str,
next_page_func: callable = None):
url = f"https://cn.bing.com/search?q={keyword}"
def on_load_finish(ok: bool):
global cur_page
cur_tab.on_load_finish_cbs.remove(on_load_finish)
if cur_page == 3:
cur_page = 0
return
if not ok:
return
def on_html(html: str):
global cur_page
print('extracting page: ', cur_page)
extract_info(html)
if cur_page < 3:
cur_page += 1
cur_tab.on_load_finish_cbs.append(on_load_finish)
next_page_func()
cur_tab.dump_async(on_html)
cur_tab.on_load_finish_cbs.append(on_load_finish)
cur_tab.load_url(QUrl(url))
注:代码有点乱,而且有全局变量不好维护,后续考虑封装成一个类。
翻页命令 navigate 实现原理
首先,browsertab 对 DOM 也有一层封装,并且支持 find_css
以 CSS 进行查询。代码如下:
browsertab.elements.find_css(
link_selector,
callback=_prevnext_cb,
error_cb=lambda err: message.error(str(err)))
_prevnext_cb
是一个回调。内部调用 _find_prevnext
寻找符合要求的元素。从注释中我们能理解它的规则:
"""Find a prev/next element in the given list of elements."""
# First check for <link rel="prev(ious)|next"> as well as
# e.g. <a class="nav-(prev|next)"> (Hugo)
简单理解为预制了一些规则,bing 就正好满足这一规则。
小结
在本文中我实现了:bing
命令,而且能够自动翻页。这个命令手动调用很方便,更多的是为后续进行自动化信息采集做铺垫,后者的价值更高。
本文作者:Maeiee
本文链接:浏览器功能定制5:搜索引擎命令
版权声明:如无特别声明,本文即为原创文章,版权归 Maeiee 所有,未经允许不得转载!
喜欢我文章的朋友请随缘打赏,鼓励我创作更多更好的作品!