2017-07-19 63 views
10

Jestem naprawdę nowy w Pythonie i utknąłem z poniższym problemem, który muszę rozwiązać. Mam plik dziennika z Apache Log jak poniżej:Dodaj wartości kluczy i posortuj je według występowania kluczy na liście słowników w Pythonie

[01/Aug/1995:00:54:59 -0400] "GET /images/opf-logo.gif HTTP/1.0" 200 32511 
[01/Aug/1995:00:55:04 -0400] "GET /images/ksclogosmall.gif HTTP/1.0" 200 3635 
[01/Aug/1995:00:55:06 -0400] "GET /images/ksclogosmall.gif HTTP/1.0" 403 298 
[01/Aug/1995:00:55:09 -0400] "GET /images/ksclogosmall.gif HTTP/1.0" 200 3635 
[01/Aug/1995:00:55:18 -0400] "GET /images/opf-logo.gif HTTP/1.0" 200 32511 
[01/Aug/1995:00:56:52 -0400] "GET /images/ksclogosmall.gif HTTP/1.0" 200 3635 

mam wrócić 10 najbardziej żądanych obiektów i ich skumulowane bajtów przeniesione. Muszę uwzględnić tylko żądania GET z pomyślnymi odpowiedziami (HTTP 2xx).

Więc powyżej dziennika doprowadziłoby do:

/images/ksclogosmall.gif 10905 
/images/opf-logo.gif 65022 

Do tej pory mam następujący kod:

import re 
from collections import Counter, defaultdict 
from operator import itemgetter 
import itertools 
import sys 

log_file = "web.log" 
pattern = re.compile(
     r'\[(?P<date>[^\[\]:]+):(?P<time>\d+:\d+:\d+) (?P<timezone>[\-+]?\d\d\d\d)\] ' 
     + r'"(?P<method>\w+) (?P<path>[\S]+) (?P<protocol>[^"]+)" (?P<status>\d+) (?P<bytes_xfd>-|\d+)') 

dict_list = [] 

with open(log_file, "r") as f: 
    for line in f.readlines(): 
     if re.search("GET", line) and re.search(r'HTTP/[\d.]+"\s[2]\d{2}', line): 
      try: 
       log_line_data = pattern.match(line) 
       path = log_line_data["path"] 
       bytes_transferred = int(log_line_data["bytes_xfd"]) 
       dict_list.append({path: bytes_transferred}) 
      except: 
       print("Unexpected Error: ", sys.exc_info()[0]) 
       raise 
    f.close() 

print(dict_list) 

Ten kod drukuje następującą listę słownika.

[{'/images/opf-logo.gif': 32511}, 
{'/images/ksclogosmall.gif': 3635}, 
{'/images/ksclogosmall.gif': 3635}, 
{'/images/opf-logo.gif': 32511}, 
{'/images/ksclogosmall.gif': 3635}] 

nie wiem jak się do tego zabrać stąd aby uzyskać wynik jak:

/images/ksclogosmall.gif 10905 
/images/opf-logo.gif 65022 

Wynik ten jest w zasadzie dodanie wartości odpowiadających podobnych kluczy posortowanych według liczby razy wystąpiły szczególności klucz w porządku desc.

Uwaga: Próbowałem używać colllections.Counter bez skutku, tutaj chciałbym sortować według liczby razy, kiedy klucz wystąpił.

Każda pomoc zostanie doceniona.

Odpowiedz

8

Można użyć collections.Counter i update to zsumować bajtów przesłanych do każdego obiektu:

from collections import Counter 
c = Counter() 
for d in dict_list: 
    c.update(d) 
occurrences=Counter([list(x.keys())[0] for x in dict_list]) 
sorted(c.items(), key=lambda x: occurrences[x[0]], reverse=True) 

wyjściowa:

[('/images/ksclogosmall.gif', 10905), ('/images/opf-logo.gif', 65022)] 
+0

próbowałem tego, ale to nie jest wh w razie potrzeby. Muszę posortować listę słowników przez liczbę czasu, w którym wystąpił dany klucz, a jednocześnie dodać ich wartości, a ostateczne wyniki będą najwyższe do najniższych, takich jak: /images/ksclogosmall.gif 10905 /images/opf -logo.gif 65022 –

+0

OK, zaktualizowałem swoją odpowiedź. – Imran

+0

Wciąż ten sam, posortowany według wartości. Chcę go posortować według czasu, w którym wystąpił dany klucz, więc mimo że wartość odpowiadająca kluczowi "/images/opf-logo.gif" wynosi 65022 w porównaniu z wartością "/images/ksclogosmall.gif", która wynosi 10905 , nadal powinno być na wierzchu, ponieważ klucz "/images/ksclogosmall.gif" wystąpił 3 razy na liście słowników w porównaniu z drugim, który wystąpił tylko dwa razy. Przepraszam za wszelkie zamieszanie, od jakiegoś czasu utknąłem w tej części. –

0

Można pętla swoją dict i przechowywania wartości w nowy dyktafon:

results = {} 
for d in dict_list: 
    for k, v in d.items(): 
     total = results.get(k, 0) # get previously stored value, 0 if none 
     results[k] = total + v 
0

To może nie być najbardziej eleganckie rozwiązanie, jednak r to będzie działać:

freq = {} 
with open('test.txt') as f: 
    lines = f.read().splitlines() 

    for line in lines: 
     if 'GET' in line and 'HTTP' in line and '200' in line: 
      path = line.split()[3] 
      occur = int(line.split()[-1]) 
      freq[path] = freq.get(path, 0) + occur 

frequency = {k: v for k, v in sorted(freq.items(), key=lambda x: x[1])} 

więc dla swojej przewidzianego fragmentu dziennika:

print(frequency) 
>>> {'/images/ksclogosmall.gif': 10905, '/images/opf-logo.gif': 65022} 
+0

Rzeczywiście mam to wyjście, czego potrzebuję, to wynik posortowany według liczby wystąpień klucza na tej liście słowników i ich łącznych wartości. /images/ksclogosmall.gif 10905 /images/opf-logo.gif 65022 –

+0

@ leo_21 ahh przepraszam, że źle zrozumiałem i zaktualizowałem moją odpowiedź. – flevinkelming

+0

Bez obaw. Myślę, że wciąż daje odpowiedź posortowaną według wartości, prawda? Przepraszam, nie jestem pewien, czy mam prawidłowy kod. Jak mogę sortować według liczby wystąpień klucza w poleceniu desc, a także dodawać ich łączną wartość? –

5

pierwsze, lista słowników naprawdę nie ma sensu do tego typu danych. Ponieważ każdy słownik ma tylko jedną parę klucz-wartość, po prostu skonstruuj listę krotek (lub listę namedtuples, jeśli chcesz mieć większą czytelność).

tuple_list.append((path, bytes_transferred)) 

Teraz uzyskanie pożądanego wyniku będzie prostsze. Osobiście użyłbym defaultdict.

from collections import defaultdict 

tracker = defaultdict(list) 
for path, bytes_transferred in tuple_list: 
    tracker[path].append(bytes_transferred) 
# {'/images/ksclogosmall.gif': [3635, 3635, 3635], '/images/opf-logo.gif': [32511, 32511]} 

print([(p, sum(b)) for p, b in sorted(tracker.items(), key=lambda i: -len(i[1]))]) 
# [('/images/ksclogosmall.gif', 10905), ('/images/opf-logo.gif', 65022)] 
0

innej opcji, dwa wiersze

.... 
path = log_line_data["path"] 
if [x for x in range(len(dict_list)) if path in dict_list[x].keys()]: 
    continue 

wyjście

[{'/images/opf-logo.gif': 32511}, {'/images/ksclogosmall.gif': 3635}] 
0

Jeśli chcesz, aby zwinąć

[{'/images/opf-logo.gif': 32511}, 
{'/images/ksclogosmall.gif': 3635}, 
{'/images/ksclogosmall.gif': 3635}, 
{'/images/opf-logo.gif': 32511}, 
{'/images/ksclogosmall.gif': 3635}] 

do słownika i wynieść się wartości z tego samego klucz:

  1. Utwórz nowy, pusty słownika
  2. pętli każdego słownika, należy sprawdzić, czy klucz istnieje w nowym słowniku
  3. Jeśli klawisz (ścieżka pliku) nie istnieje, skopiuj go
  4. jeśli to istnieją, dodać wartość

`` `

total = {} 

for d in all: 
    for k, v in d.items(): 
      if k in total: 
        total[k] += v 
      else: 
        total[k] = v 

print(total) 
{'/images/opf-logo.gif': 65022, '/images/ksclogosmall.gif': 10905}