Файлы журнала HTTP, которые я пытаюсь проанализировать с помощью панд, иногда имеют неожиданные строки.Вот как я загружаю свои данные:
df = pd.read_csv('mylog.log',
sep=r'\s(?=(?:[^"]*"[^"]*")*[^"]*$)(?![^\[]*\])',
engine='python', na_values=['-'], header=None,
usecols=[0, 3, 4, 5, 6, 7, 8,10],
names=['ip', 'time', 'request', 'status', 'size',
'referer','user_agent','req_time'],
converters={'status': int, 'size': int, 'req_time': int})
Это прекрасно работает для большинства журналов, которые у меня есть (которые приходят с того же сервера).Однако при загрузке некоторых журналов возникает исключение:
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'
или
ValueError: invalid literal for int() with base 10: '"GET /agent/10577/bdl HTTP/1.1"'
Для примера приведем строку, которая вызывает второе исключение:
22.111.117.229, 22.111.117.229 - - [19/Sep/2018:22:17:40 +0200] "GET /agent/10577/bdl HTTP/1.1" 204 - "-" "okhttp/3.8.0" apibackend.site.fr 429282
Чтобы найти номер инкриминированной строки, я использовал следующую (ужасно медленную) функцию:
def search_error_dichotomy(path):
borne_inf = 0
log = open(path)
borne_sup = len(log.readlines())
log.close()
while borne_sup - borne_inf>1:
exceded = False
search_index = (borne_inf + borne_sup) // 2
try:
pd.read_csv(path,...,...,nrows=search_index)
except:
exceded = True
if exceded:
borne_sup = search_index
else:
borne_inf = search_index
return search_index
Я хотел бы получить что-то вроде этого:
try:
pd.read_csv(..........................)
except MyError as e:
print(e.row_number)
где e.row_number - номер грязной строки.
Заранее спасибо.
РЕШЕНИЕ Все кредиты devssh, чье предложение не только ускоряет процесс, но и позволяет мне сразу получить все неожиданные строки.Вот что я сделал из этого:
Загрузить фрейм данных без конвертеров.
df = pd.read_csv(path,
sep=r'\s(?=(?:[^"]*"[^"]*")*[^"]*$)(?![^\[]*\])',
engine='python', na_values=['-'], header=None,
usecols=[0, 3, 4, 5, 6, 7, 8,10],
names=['ip', 'time', 'request', 'status', 'size',
'referer', 'user_agent', 'req_time'])
Добавить столбец «index» с помощью .reset_index().
df = df.reset_index()
Напишите пользовательскую функцию (которая будет использоваться с apply), которая преобразует в int, если это возможно, в противном случае сохраняет запись и индекс в словаре неправильных строк
wrong_lines = {}
def convert_int_feedback_index(row,col):
try:
ans = int(row[col])
except:
wrong_lines[row['index']] = row[col]
ans = pd.np.nan
return ans
Используйте применить к столбцам, которые я хочу преобразовать (например, col = 'status', 'size' или 'req_time')
df[col] = df.apply(convert_int_feedback_index, axis=1, col=col)