我有一个小的(40mb)服务器日志,链接here
我有一个正则表达式,我用它来解析代码,该代码需要花费不少时间(5分钟以上)才能完成。我对正则表达式比较陌生,所以我不确定为什么这么长的文件需要这么长时间
这是表达式:
valid=re.findall(r'(\d+/[a-zA-Z]+/\d+).*?(GET|POST)\s+(http://|https//)([a-zA-Z]+.+?)\.[^/].*?\.([a-zA-Z]+)(/|\s|:).*?\s200\s', line)
当我在行尾添加“200”时,事情真的开始突然出现了
这是整个代码:
import re
#todo
#specify toplevel domain lookback
######
fhandle=open("access_log.txt", "rU")
access_log=fhandle.readlines()
validfile=open("valid3.txt", "w")
invalidfile=open("invalid3.txt", "w")
valid_dict=dict()
invalid_list=list()
valid_list=list()
#part 1
#read file. apply regex and append into internal data structure (a 2d dictionary)
for line in access_log:
valid=re.findall(r'(\d+/[a-zA-Z]+/\d+).*?(GET|POST)\s+(http://|https//)([a-zA-Z]+.+?)\.[^/].*?\.([a-zA-Z]+)(/|\s|:).*?\s200\s', line)
#valid=re.findall(r'(\d+/[a-zA-Z]+/\d+).*?(GET|POST)\s+(http://|https://)([a-zA-Z]+.+?)\.[^/].*?\.([a-zA-Z]+)(/|\s|:).*?\s+200\s', line)
if valid:
date=valid[0][0]
domain=valid[0][4].lower()
valid_list.append(line)
#writes results into 2d dcitonary (dictionary of dictonaries)
if date not in valid_dict:
valid_dict[date]={}
else:
if domain in valid_dict[date]:
valid_dict[date][domain]+=1
else:
valid_dict[date][domain]=1
#writes remainder files into invalid file log
else:
invalid_list.append(line)
#step 2
#format output file for tsv
#ordered chronologically, with Key:Value pairs orgainzed alphabeticallv by key (Domain Name)
date_workspace=''
domain_workspace=''
for date in sorted(valid_dict.iterkeys()):
date_workspace+=date + "\t"
for domain_name in sorted(valid_dict[date].iterkeys()):
domain_workspace+="%s:%s\t" % (domain_name, valid_dict[date][domain_name])
date_workspace+=domain_workspace
date_workspace+="\n"
domain_workspace=''
# Step 3
# write output
validfile.write(date_workspace)
for line in invalid_list:
invalidfile.write(line)
fhandle.close()
validfile.close()
invalidfile.close()
答案 0 :(得分:1)
假设您要保留域名扩展名,可以像这样更改代码的正则表达式部分:
pattern = re.compile(r'^[^[]+\[(\d+/[a-zA-Z]+/\d+)[^]]+] "(?:GET|POST) https?://[a-zA-Z]+[^?/\s]*\.([a-zA-Z]+)[?/ :][^"]*" 200 ')
for line in access_log:
valid=pattern.search(line)
if valid:
date=valid.group(1)
domain=valid.group(2).lower()
valid_list.append(line)
改进:5分钟 - > 2S
由于您逐行阅读文件,因此一行中只有一个可能匹配,最好使用返回第一个匹配而不是re.search
的{{1}}。
每行使用一次模式,这就是我选择在循环之前编译模式的原因。
模式现在以字符串锚点re.findall
的开头锚定,现在用^
描述该行的开头(所有这些都不是[^[]+\[
1}}一次或多次后跟[
)。这种改进非常重要,因为它避免了正则表达式引擎在线的每个字符处尝试模式的开始。
由于两个原因(至少),所有[
都很慢:
延迟量词必须测试以下子模式是否与每个字符匹配。
如果模式稍后失败,因为.*?
可以匹配所有字符,正则表达式引擎没有最小的理由来停止其回溯。换句话说,好的方法是尽可能明确。
要做到这一点,你必须用负字符类和贪心量词替换所有.*?
。
所有未被捕获的捕获组已被非捕获组.*?
替换。
其他一些微不足道的变化,如(?:...)
或(http://|https://) => https?://
。所有(/|\s|:) => [?/ :]
都已替换为空格。
作为旁听,我确信有很多日志解析器/分析器可以帮助你。另请注意,您的日志文件使用csv格式。