Как исключить строку из re.findall?

Это может быть глупый вопрос, но я просто пытаюсь учиться!

Я пытаюсь создать простой инструмент поиска по электронной почте, чтобы узнать больше о Python. Я изменяю некоторый открытый исходный код, чтобы разобрать адрес электронной почты:

emails = re.findall(r'([A-Za-z0-9\.\+_-]+@[A-Za-z0-9\._-]+\.[a-zA-Z]*)', html)

Затем я записываю результаты в электронную таблицу, используя модуль CSV.

Поскольку я хочу оставить расширение домена доступным практически для любого, мои результаты выводят файлы изображений в формате электронной почты:

пример: forbes@2x-302019213j32.png

Как я могу добавить, чтобы исключить строку "png" из re.findall

Код:

  def scrape(self, page):
    try:
        request = urllib2.Request(page.url.encode("utf8"))
        html    = urllib2.urlopen(request).read()
    except Exception, e:
        return
       emails = re.findall(r'([A-Za-z0-9\.\+_-]+@[A-Za-z0-9\._-]+\.[a-zA-Z]*)', html)
       for email in emails:
        if email not in self.emails:  # if not a duplicate
            self.csvwriter.writerow([page.title.encode('utf8'), page.url.encode("utf8"), email])
            self.emails.append(email)

3 ответа

Решение

Вы уже действуете только в случае if ... просто включите проверку if ... ... это будет намного проще, чем пытаться исключить его из регулярного выражения

if email not in self.emails and not email.endswith("png"):  # if not a duplicate
        self.csvwriter.writerow([page.title.encode('utf8'), page.url.encode("utf8"), email])
        self.emails.append(email)

Есть много способов сделать это, но мой любимый это:

pat = re.compile(r'''
          [A-Za-z0-9\.\+_-]+ # 1+ \w\n.+-_
          @[A-Za-z0-9\._-]+  # literal @ followed by same
          \.png              # if png, DON'T CAPTURE
          |([A-Za-z0-9\.\+_-]+@[A-Za-z0-9\._-]+\.[a-zA-Z]*)
                             # if not png, CAPTURE''', flags=re.X)

Поскольку регулярные выражения вычисляются слева направо, если строка начинает совпадать, то она будет соответствовать левой стороне | первый. Если строка заканчивается .pngтогда он будет использовать эту строку, но НЕ перехватывать ее. Если это не заканчивается .pngправая сторона | начнет потреблять это и будет захватывать это. Для более глубокого разговора об этой уловке, смотрите здесь. Чтобы использовать это сделать:

matches = filter(None,pat.findall(html))

Любая строка соответствует левой стороне (например, все png файлы, которые совпадают, но НЕ являются частью группы захвата), будут отображаться как пустая строка в вашем findall. filter(None, iterable) удаляет все пустые строки из вашей итерации, оставляя вам только те данные, которые вы хотите.

Кроме того, вы можете фильтровать после того, как вы захватите все

pat = re.compile(r'''[A-Za-z0-9\.\+_-]+@[A-Za-z0-9\._-]+\.[a-zA-Z]*''')
# same regex you have currently
matches = filter(lambda x: not x.endswith('png'), pat.findall(html))

Обратите внимание, что в дальнейшем вы должны сделать self.emails множество. Похоже, что нет необходимости сохранять его порядок, и поиск по порядку намного быстрее, чем поиск по списку. Не забудьте использовать set.add вместо list.append хоть.

Я знаю, что Джоран уже дал вам ответ, но вот еще один способ сделать это с помощью регулярного выражения Python, который я нашел классным.

E сть (?!...) шаблон сопоставления, который по существу говорит: "Где бы вы ни разместили этот шаблон сопоставления, если в этой точке строки проверяется этот шаблон и обнаруживается совпадение, то это совпадение не выполняется".

Если это плохое объяснение, документ Python делает работу намного лучше: https://docs.python.org/2/howto/regex.html

Также вот рабочий пример:

y = r'([A-Za-z0-9\.\+_-]+@[A-Za-z0-9\._-]+\.(?!png)[a-zA-z]*)'
s = 'forbes@2x-302019213j32.png'
re.findall(y, s) # Will return an empty list

s2 = 'myname@email2018529391230.net'
re.findall(y, s2) # Will return a list with s2 string

s3 = s + ' ' + s2 # Concatenates the two e-mail-formatted strings
re.findall(y, s3) # Will only return s2 string in list
Другие вопросы по тегам