如何在re.findall中排除字符串?

3 投票
3 回答
6608 浏览
提问于 2025-04-18 13:16

这可能是个傻问题,但我只是想学习!

我正在尝试制作一个简单的电子邮件搜索工具,以便更好地了解Python。我正在修改一些开源代码来解析电子邮件地址:

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

然后我使用CSV模块将结果写入电子表格。

因为我想让域名后缀可以是几乎任何类型,所以我的结果中输出了一些带有电子邮件格式的图像文件:

例如:forbes@2x-302019213j32.png

我该如何添加代码来排除“png”这个字符串呢?

代码:

  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 个回答

2

我知道Joran已经给你回复了,但我想分享另一种用Python正则表达式的方法,我觉得很有意思。

这里有一个 (?!...) 的匹配模式,它的意思是:“无论你把这个匹配模式放在哪里,如果在字符串的那个点检查这个模式并且找到了匹配,那么这个匹配就失败。”

如果这个解释不太好,Python的文档解释得更清楚:https://docs.python.org/2/howto/regex.html#lookahead-assertions

另外,这里有一个实际的例子:

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
2

有很多方法可以做到这一点,但我最喜欢的是:

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

2

你现在已经在用一个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)

撰写回答