Python, сопоставляя списки с неравной длиной - PullRequest
2 голосов
/ 20 ноября 2011

Пожалуйста, подготовьтесь к длительному чтению.Я нахожусь в тупике и не знаю, где искать ответ / что еще попробовать.Излишне говорить, что я немного новичок в программировании.За последние пару недель взломал этот проект.

ПРОБЛЕМА

Я получил эту таблицу, 25 строк, 2 столбца.Каждая строка имеет следующую структуру:

Необходимое событие

<td align=center>19/11/11<br>12:01:21 AM</td>
<td align=center><font color=#006633><a href=profiles.php?XID=1><font color=#006633>player1</font></a> hospitalized <a href=profiles.php?XID=2><font color=#006633>player2</font></a></font></td>

НЕ Необходимое событие CASE A

<td align="center">19/11/11<br />12:58:03 AM</td>
<td align="center"><font color="#AA0000">Someone hospitalized <a href=profiles.php?XID=1><font color="#AA0000">player1</font></a></font></td>

НЕ НЕОБХОДИМОЕ событие CASE B

<td align="center">19/11/11<br />12:58:03 AM</td>
<td align=center><font color=#006633><a href=profiles.php?XID=3><font color=#006633>player3</font></a> attacked <a href=profiles.php?XID=1><font color=#006633>player1</font></a> and lost </font></td>

Я использовал регулярное выражение для очистки необходимых данных.Моя проблема в том, что 2 списка не совпадают.Дата и время не всегда соответствуют точному событию.


1-я ПОПЫТКА при решении задачи

import mechanize  
import re

htmlA1 = br.response().read()

patAttackDate = re.compile('<td align=center>(\d+/\d+/\d+)<br>(\d+:\d+:\d+ \w+)')
patAttackName = re.compile('<font color=#006633>(\w+)</font></a> hospitalized ')
searchAttackDate = re.findall(patAttackDate, htmlA1)
searchAttackName = re.findall(patAttackName, htmlA1)

pairs = zip(searchAttackDate, searchAttackName)

for i in pairs:
print (i)

Но это дает мне wrong time - correct eventтип списка.

например:

(('19/11/11', '9:47:51 PM'), 'user1') <- mismatch 
(('19/11/11', '8:21:18 PM'), 'user1') <- mismatch
(('19/11/11', '7:33:00 PM'), 'user1') <- As a consequence of the below, the rest upwards are mismatched 
(('19/11/11', '7:32:38 PM'), 'user2') <- NOT a match, case B
(('19/11/11', '7:32:22 PM'), 'user2') <- match ok
(('19/11/11', '7:26:53 PM'), 'user2') <- match ok
(('19/11/11', '7:25:24 PM'), 'user3') <- match ok
(('19/11/11', '7:24:22 PM'), 'user3') <- match ok
(('19/11/11', '7:23:25 PM'), 'user3') <- match ok

2-я ПОПЫТКА при решении проблемы

Так думал раздеть newlineсо всей страницы и почистите таблицу, но:

import mechanize
import re
from BeautifulSoup import BeautifulSoup

htmlA1 = br.response().read()

stripped = htmlA1.replace(">\n<","><") #Removed all '\n' from code

soup = BeautifulSoup(stripped)

table = soup.find('table', width='90%')
table2 = table.findNext('table', width='90%')
table3 = table2.findNext('table', width='90%') #this is the table I need to work with

patAttackDate = re.compile('<td align="center">(\d+/\d+/\d+)<br />(\d+:\d+:\d+ \w+)')
searchAttackDate = re.findall(patAttackDate, table3)
print searchAttackDate

это дает мне ошибку:

return _compile(pattern, flags).findall(string)
TypeError: expected string or buffer

Что мне не хватает?

Бонусный вопрос:Есть ли способ объяснить, что XID является динамической переменной, но обойти его при использовании регулярного выражения / Beautifulsoup (или другой метод очистки)?По мере роста проекта мне может понадобиться включить часть кода XID, но я не хочу соответствовать ей.(не уверен, если это понятно)

Спасибо за ваше время


РЕДАКТИРОВАТЬ 1 : добавлен пример списка
РЕДАКТИРОВАТЬ 2 : Более четкое разделение кода
EDIT 3 : добавлен пример кода для данного решения, которое не работает

Test = '''<table><tr><td>date</td></tr></table>'''
soupTest = BeautifulSoup(Test)
test2 = soupTest.find('table')
patTest = re.compile('<td>(.*)</td>')
searchTest = patTest.findall(test2.getText())
print test2 # gives: <table><tr><td>date</td></tr></table> 
print type(test2) # gives: <class 'BeautifulSoup.Tag'>
print searchTest #gives: []

EDIT 4 - Solution

import re
import mechanize
from BeautifulSoup import BeautifulSoup

htmlA1 = br.response().read()
stripped = htmlA1.replace(">\n<","><") #stripped '\n' from html
soup = BeautifulSoup(stripped)

table = soup.find('table', width='90%')
table2 = table.findNext('table', width='90%')
table3 = table2.findNext('table', width='90%') #table I need to work with

print type(table3) # gives <class 'BeautifulSoup.Tag'>
strTable3 = str(table3) #convert table3 to string type so i can regex it

patFinal = re.compile(('(\d+/\d+/\d+)<br />(\d+:\d+:\d+ \w+)</td><td align="center">'
                      '<font color="#006633"><a href="profiles.php\?XID=(\d+)">'
                      '<font color="#006633">(\w+)</font></a> hospitalized <a'), re.IGNORECASE)
searchFinal = re.findall(patFinal, strTable3)

for i in searchFinal:
    print (i)

Пример вывода

('19/11/11', '1:08:07 AM', 'ID_user1', 'user1')
('19/11/11', '1:06:55 AM', 'ID_user1', 'user1')
('19/11/11', '1:05:46 AM', 'ID_user1', 'user1')
('19/11/11', '1:04:33 AM', 'ID_user1', 'user1')
('19/11/11', '1:03:32 AM', 'ID_user1', 'user1')
('19/11/11', '1:02:37 AM', 'ID_user1', 'user1')
('19/11/11', '1:00:43 AM', 'ID_user1', 'user1')
('19/11/11', '12:55:35 AM', 'ID_user2', 'user2')

EDIT 5 - намного более простое решение (с 1-й попытки - без Beautifulsoup)

import re

reAttack = (r'<td\s+align=center>(\d+/\d+/\d+)<br>(\d+:\d+:\d+\s+\w+)</td>\s*'
            '<td.*?' #accounts for the '\n'
            '<font\s+color=#006633>(\w+)</font></a>\s+hospitalized\s+')

for m in re.finditer(reAttack, htmlA1):
    print 'date: %s; time: %s; player: %s' % (m.group(1), m.group(2), m.group(3))

Пример вывода

date: 19/11/11; time: 1:08:07 AM; player: user1
date: 19/11/11; time: 1:06:55 AM; player: user1
date: 19/11/11; time: 1:05:46 AM; player: user1
date: 19/11/11; time: 1:04:33 AM; player: user1
date: 19/11/11; time: 1:03:32 AM; player: user1
date: 19/11/11; time: 1:02:37 AM; player: user1
date: 19/11/11; time: 1:00:43 AM; player: user1
date: 19/11/11; time: 12:55:35 AM; player: user2

Ответы [ 4 ]

3 голосов
/ 20 ноября 2011

Из вашего описания я еще не понял, что именно вы пытаетесь сделать.Но сейчас я могу сказать вам одну вещь: с регулярными выражениями строки Python - ваш друг.

Попробуйте использовать r'pattern' вместо 'pattern' в вашей программе BeautifulSoup.

ТакжеКогда вы работаете с регулярными выражениями, иногда полезно начать с простых шаблонов, убедиться, что они работают, а затем создать их.Вы перешли прямо к сложным шаблонам, и я уверен, что они не работают, так как вы не использовали необработанные строки, и обратные слеши не будут правильными.

1 голос
/ 20 ноября 2011

Это работает для меня:

reAttack = r'<td\s+align=center>(\d+/\d+/\d+)<br>(\d+:\d+:\d+\s+\w+)</td>\s*<td.*?<font\s+color=#006633>(\w+)</font></a>\s+hospitalized\s+'

for m in re.finditer(reAttack, htmlA1):
  print 'date: %s; time: %s; player: %s' % (m.group(1), m.group(2), m.group(3))

живая демонстрация

Выполнение всего в одном регулярном выражении создает более сложное регулярное выражение, но это намного проще, чем сопоставлять каждый TD отдельно и пытаться синхронизировать их впоследствии, как вы делаете. .*? около середины регулярного выражения работает в предположении, что все элементы разделены символами новой строки, как в ваших исходных примерах. Если вы не можете этого допустить, вам следует заменить .*? на (?:(?!/?td>).)*, чтобы сохранить совпадение в текущем элементе TD.

К вашему сведению, в ваших примерах данных были некоторые несоответствия. Некоторые значения атрибутов были заключены в кавычки, а большинство - нет, и у вас было сочетание тегов <br> и <br />. Я нормализовал все для моей демонстрации, но если это соответствует вашим реальным данным, вам понадобится гораздо более сложное регулярное выражение. Или вы могли бы переключиться на чистое решение DOM, что, вероятно, было бы проще в первую очередь. ;)

1 голос
/ 20 ноября 2011

Методы .findNext() вернут объект BeautifulSoup.Tag, который нельзя передать в re.findall.Поэтому вам нужно использовать .getText() (или аналогичный метод, чтобы получить текст из объекта Tag. Или .contents, чтобы получить HTML внутри этого тега).Кроме того, при использовании re.compile возвращаемый объект - это то, что вам нужно вызвать findall on.

Это:

soup = BeautifulSoup(stripped)

table = soup.find('table', width='90%')
table2 = table.findNext('table', width='90%')
table3 = table2.findNext('table', width='90%') #this is the table I need to work with

patAttackDate = re.compile('<td align="center">(\d+/\d+/\d+)<br />(\d+:\d+:\d+ \w+)')
searchAttackDate = re.findall(patAttackDate, table3)

Должно быть написано так (последняя строка - этоединственное, что нужно изменить):

soup = BeautifulSoup(stripped)

table = soup.find('table', width='90%')
table2 = table.findNext('table', width='90%')
table3 = table2.findNext('table', width='90%')

patAttackDate = re.compile('<td align="center">(\d+/\d+/\d+)<br />(\d+:\d+:\d+ \w+)')
searchAttackDate = patAttackDate.findall(table3.getText())

# or, to search the html inside table3 and not just the text
# searchAttackDate = patAttackDate.findall(str(table3.contents[0])) 

Документация BeautifulSoup

Из re документов :

re.compile(pattern, flags=0)

Скомпилируйте шаблон регулярного выражения в объект регулярного выражения .

Это:
result = re.match(pattern, string)

эквивалентно:
prog = re.compile(pattern)
result = prog.match(string)

0 голосов
/ 20 ноября 2011

для решения Beautifulsoup вы можете использовать это (без проверки регулярного выражения - также я уверен, что @steveha прав насчет добавления r ''):

searchAttackDate = table3.findAll(patAttackDate)
for row in searchAttackDate:
   print row
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...