Python 过滤访问细节,下面介绍AccessDetails
对象的几个过滤器。第一个用于排除我们不感兴趣的大量无用文件。第二个过滤器会作为分析函数的一部分,稍后将展开讨论。
函数path_filter()
具有以下3个功能:
- 排除空路径;
- 排除特定文件名;
- 排除具有特定扩展名的文件。
优化版的path_filter()
函数如下所示:
def path_filter(
access_details_iter: Iterable[AccessDetails]
) -> Iterable[AccessDetails]:
name_exclude = {
'favicon.ico', 'robots.txt', 'index.php', 'humans.txt',
'dompdf.php', 'crossdomain.xml',
'_images', 'search.html', 'genindex.html',
'searchindex.js', 'modindex.html', 'py-modindex.html',
}
ext_exclude = {
'.png', '.js', '.css',
}
for detail in access_details_iter:
path = detail.url.path.split('/')
if not any(path):
continue
if any(p in name_exclude for p in path):
continue
final = path[-1]
if any(final.endswith(ext) for ext in ext_exclude):
continue
yield detail
我们对每个独立的AccessDetails
对象使用3个过滤器测试。如果路径名实际为空,或者含有一个需要排除的名称,又或者路径名称末尾包含需要排除的扩展名,则静默地忽略路径名。如果路径名不满足其中任何一个条件,那么我们可能会对它感兴趣,它也将成为path_filter()
函数最终结果的一部分。
这是一种优化技术,因为所有测试都使用了命令式的for
循环体。
另一种设计方法是将每个测试定义为独立的、过滤器风格的头等函数。例如可用以下函数处理空路径:
def non_empty_path(detail: AccessDetails) -> bool:
path = detail.url.path.split('/')
return any(path)
该函数简单地确保了路径中必须包含一个名称。可如下所示使用filter()
函数:
filter(non_empty_path, access_details_iter)
可以为non_excluded_names()
和non_excluded_ext()
函数编写类似的测试。整个filter()
函数序列如下所示:
filter(non_excluded_ext,
filter(non_excluded_names,
filter(non_empty_path, access_details_iter)))
其中每个filter()
函数都作用于前一个filter()
函数的结果。空路径以及该子集中需要排除的名称和扩展名都会被拒接。还可以将上述示例声明为一系列赋值语句,如下所示:
non_empty = filter(non_empty_path, access_details_iter)
nx_name = filter(non_excluded_names, non_empty)
nx_ext = filter(non_excluded_ext, nx_name)
该版本的优点是当添加新的过滤条件时易于扩展。
使用生成器函数(如
filter()
函数)意味着我们并没有创建大的中间结果对象。每一个中间变量(ne
、nx_name
和nx_ext
)都作为合适的惰性生成器函数,不会在客户端进程处理完数据之前执行任何处理。
尽管看上去简单,但由于每个函数都需要解析AccessDetails
对象中的路径,因此效率很低。为了提高效率,可使用lru_cache
属性封装一个path.split('/')
函数。