내 웹사이트를 방문하는 각 사용자(IP 주소)가 일련의 페이지를 보는 시간을 비교하여 웹사이트에서 사람이 아닌 활동을 식별하고 IP 주소를 분석에서 제외하고 싶습니다.
나는 awk를 배우고 싶고 개선하고 싶기 때문에 가능한 한 많이 사용하고 싶습니다(저는 GAWK를 사용하고 있습니다). 그러나 나는 bash에서 다른 도구를 사용하는 데 개방적입니다.
다음 형식의 수정된 로그 파일(output.csv)이 있습니다.
29/Oct/2020:07:41:42|111.111.111.111|200|/page-a/
29/Oct/2020:08:30:40|000.111.026.111|200|/page-a/
29/Oct/2020:08:30:44|000.111.026.111|200|/page-b/
29/Oct/2020:08:30:45|000.111.026.111|200|/page-c/
29/Oct/2020:08:30:47|000.111.026.111|200|/page-d/
29/Oct/2020:08:30:47|220.171.008.221|200|/page-h/
29/Oct/2020:08:30:48|000.111.026.111|200|/page-e/
29/Oct/2020:08:41:49|221.651.943.323|200|/page-a/
29/Oct/2020:08:41:52|060.121.125.144|200|/page-f/
29/Oct/2020:08:41:52|060.121.125.144|200|/page-g/
29/Oct/2020:08:41:54|000.111.026.111|200|/page-k/
29/Oct/2020:08:41:55|060.121.125.144|200|/page-l/
29/Oct/2020:08:41:57|060.121.125.144|200|/page-n/
29/Oct/2020:08:41:58|060.121.125.144|200|/page-s/
나는 다음을 수행하고 싶습니다 :
- 모든 고유 IP 검색
output.csv
- 이 IP의 인스턴스가 5개 이상인 경우 각 행의 첫 번째 날짜/시간과 다섯 번째 날짜/시간 사이의 차이(초)를 계산합니다.
- 15초 안에 5페이지에 접속한 IP 주소 분리
- 이 IP 주소를 다음에 추가하십시오.
file.txt
내가 시도한 것
특정 수의 IP 주소 인스턴스 간의 시간 차이(초)를 얻으려면 다음 명령 세트를 사용했습니다.
egrep "000.111.000.111" output.csv | awk 'BEGIN{FS="|"; ORS=" "} NR==1 || NR==5 {print $1,$2}' | sed -e 's/[\/:]/\ /g' -e 's/Jan/1/g' -e 's/Feb/2/g' -e 's/Mar/3/g' -e 's/Apr/4/g' -e 's/May/5/g' -e 's/Jun/6/g' -e 's/Jul/7/g' -e 's/Aug/8/g' -e 's/Sep/9/g' -e 's/Oct/10/g' -e 's/Nov/11/g' -e 's/Dec/12/g' | awk '{print $3,$2,$1,$4,$5,$6 "," $10,$9,$8,$11,$12,$13","$14}' | awk -F, '{d2=mktime($2);d1=mktime($1);print d2-d1, $3}' | awk '{if($1<15)print $2}' >> file.txt
특정 IP 주소가 15초 내에 5페이지를 방문하면 위 명령은 IP를 파일에 추가합니다.
이것이 작동하는 동안 단일 명령/스크립트로 모든 고유 IP에서 이 작업을 수행할 수 있는 방법을 찾고 있습니다.
나는 또한 내가 번거롭다고 생각하기 때문에 더 우아한 접근 방식에 열려 있습니다.
원하는 결과
원하는 결과는 14초 동안 5페이지 이상의 속도로 서버에 액세스한 IP 주소 목록이 포함된 파일입니다(시간 조정 가능).
예를 들어. file.txt
위의 예에 따르면 내용은 다음과 같습니다.
000.111.026.111
060.121.125.144
이상적으로는 귀하의 방법을 단계별로 설명하여 그것이 어떻게 작동하는지 설명해 주시면 감사하겠습니다. 그러면 제가 배우는 데 도움이 될 것입니다.
답변1
GNU awk를 사용하여 mktime()을 실행합니다.
$ cat tst.awk
BEGIN { FS = "|" }
(++count[$2]) ~ /^[15]$/ {
split($1,t,"[/:]")
monthNr = (index("JanFebMarAprMayJunJulAugSepOctNovDec",t[2])+2)/3
currSecs = mktime(t[3] " " monthNr " " t[1] " " t[4] " " t[5] " " t[6])
if ( count[$2] == 1 ) {
firstSecs[$2] = currSecs
}
else if ( (currSecs - firstSecs[$2]) < 15 ) {
print $2
}
}
$ awk -f tst.awk file
000.111.026.111
060.121.125.144
나는 이것이 무엇을 하고 있는지 매우 명확하다고 생각하므로 텍스트 설명을 추가할 필요가 없지만 질문이 있으면 언제든지 문의하십시오.
아, 특정 문제를 해결하는 데 충분할 보다 포괄적인 예를 게시할 수 있도록 IP 주소를 더미 값으로 변환하는 방법을 알고 싶다고 댓글에서 언급하셨습니다.
$ awk '
BEGIN { FS=OFS="|" }
!($2 in map) { ip=sprintf("%012d",++cnt); gsub(/.../,"&.",ip); sub(/.$/,"",ip); map[$2]=ip }
{ $2=map[$2]; print }
' file
29/Oct/2020:07:41:42|000.000.000.001|200|/page-a/
29/Oct/2020:08:30:40|000.000.000.002|200|/page-a/
29/Oct/2020:08:30:44|000.000.000.002|200|/page-b/
29/Oct/2020:08:30:45|000.000.000.002|200|/page-c/
29/Oct/2020:08:30:47|000.000.000.002|200|/page-d/
29/Oct/2020:08:30:47|000.000.000.003|200|/page-h/
29/Oct/2020:08:30:48|000.000.000.002|200|/page-e/
29/Oct/2020:07:41:49|000.000.000.004|200|/page-a/
29/Oct/2020:08:41:52|000.000.000.005|200|/page-f/
29/Oct/2020:08:41:52|000.000.000.005|200|/page-g/
29/Oct/2020:08:41:54|000.000.000.002|200|/page-k/
29/Oct/2020:08:41:55|000.000.000.005|200|/page-l/
29/Oct/2020:08:41:57|000.000.000.005|200|/page-n/
29/Oct/2020:08:41:58|000.000.000.005|200|/page-s/
편집: 내 스크립트에서 생성된 출력과 실행한 Daves 스크립트 버전에서 생성된 출력 간의 차이점 조사를 시작할 수 있습니다.
$ awk -f morton-botfilter.awk.txt output3test.csv > morton.out
$ awk -f dave-botfilter.awk.txt output3test.csv > dave.out
$ ip=$(comm -13 <(sort morton.out) <(sort dave.out) | head -1)
$ grep "$ip" output3test.csv | head -5
03/Nov/2020:07:52:55|000.000.000.007|200|/page-7/
03/Nov/2020:08:05:32|000.000.000.007|200|/page-11/
03/Nov/2020:11:28:56|000.000.000.007|200|/page-77/
03/Nov/2020:13:52:32|000.000.000.007|200|/page-143/
03/Nov/2020:13:52:33|000.000.000.007|200|/page-144/
위의 첫 번째 타임스탬프와 마지막 타임스탬프 사이의 간격이 15초를 훨씬 넘는다는 점에 유의하세요. 이는 dave-botfilter.awk.txt의 스크립트가 손상되었음을 나타냅니다. 자세한 내용은 아래 의견을 참조하세요.
답변2
당신은 awk를 배우고 싶고 분명히 가지고 있기 때문에암소 비슷한 일종의 영양awk -f script <logfile
다음 을 script
포함하는 awk(gawk)
BEGIN{ split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec",n2m);
for(i=1;i<=12;i++) m2n[n2m[i]]=i; FS="|"; }
function fixtime(str ,tmp){ split(str,tmp,"[:/]");
return mktime(tmp[3] OFS m2n[tmp[2]] OFS tmp[1] OFS tmp[4] OFS tmp[5] OFS tmp[6]) }
++count[$2]==1 { first[$2]=fixtime($1) }
count[$2]==5 && fixtime($1)-first[$2]<15 { print $2 }
처음 두 줄은 1월을 1로, 2월을 2로 매핑하는 배열 m2n(월을 숫자로)을 설정하고 필드 구분 기호를 로 설정합니다 |
. (등의 작업을 수행하는 대신 사용할 수 있지만 m2n["Jan"]=1; m2n["Feb"]=2;
더 지루합니다.
/
다음 두 줄은 all 및 구분 기호를 사용하여 시간 형식을 분할하고 :
(먼저 공백으로 변환하지 않고) 월 이름을 숫자로 변환하고 필요에 따라 재정렬하고 mktime()
(gawk 전용)에 제공하는 함수를 정의합니다. OFS 대신 텍스트를 사용할 수 있지만(기본값은 공백 1개이며 변경되지 않았습니다) " "
저는 이것이 더 보기 흉하다고 생각합니다.
다섯 번째와 여섯 번째 줄이 발견되었습니다.첫 번째발생하고 해당 타임스탬프를 기억하는 모든 IPaddr다섯동일한 IPaddr이 발생하는지 감지하고 해당 타임스탬프를 기억된 타임스탬프와 비교하여 간격이 15초 미만인지 확인합니다. 어떤 사람들은 ;next
다섯 번째와 여섯 번째 스크립트 줄이 동일한 레코드(예: 데이터 행)에서 실행되지 않는다는 점을 분명히 하기 위해 다섯 번째 줄의 작업에 하나를 추가하지만 저는 신경쓰지 않습니다.
양질의 교육 기금.
원하는 경우 '...'
스크립트 파일을 사용하는 대신 전체 스크립트를 명령줄에 넣을 수 있지만 100자를 넘는 작업은 좋아하지 않습니다.
답변3
#!/bin/bash
awk -v mon=$(locale abmon) -v FS='[/:|]' '
BEGIN {for(n=split(mon, M, ";"); n; n--) Mn[M[n]]=n}
!A[$7]++ {IP[$7] = mktime($3" "Mn[$2]" "$1" "$4" "$5" "$6)}
A[$7]==5 && mktime($3" "Mn[$2]" "$1" "$4" "$5" "$6) - IP[$7] < 15 {print $7}
' file > bot_ip
-v mon=$(locale abmon)
- 변수에는 mon
다음 라인이 할당됩니다. Jan;Feb;Mar;Apr;May;Jun;Jul;Aug;Sep;Oct;Nov;Dec
for(n=split(mon, M, ";"); n; n--)
- 이 함수는 루프 split
에서 카운터를 시작하는 배열 요소의 수를 반환합니다.for
답변4
두 번째 필드의 첫 번째 및 다섯 번째 항목만 보는 대신 5개 항목 간격의 모든 항목을 볼 수 있습니다. 슬라이딩 윈도우 방법:
awk '
{
n = c[$7] = ++c[$7] % 4
m = index("..JanFebMarAprMayJunJulAugSepOctNovDec",$2)/3
s = mktime($3 " " m " " $1 " " $4 " " $5 " " $6)
if (s - t[$7,n] < 15 && !seen[$7]++) {
print
}
t[$7,n] = s
}
' FS='[/:|]' output.csv