Почему это регулярное выражение является жадным и почему код примера повторяется вечно? - PullRequest
0 голосов
/ 01 мая 2020

Я схожу с ума, пытаясь понять это. Прошло уже 3 дня, и я готов сдаться. Ниже код должен вернуть список без повторов всех телефонных номеров и электронных писем в буфере обмена.

#! python 3
#! Phone number and email address scraper

#take user input for:
#1. webpage to scrape
# - user will be prompted to copy a link
#2. file & location to save to
#3. back to 1 or exit

import pyperclip, re, os.path

#function for locating phone numbers
def phoneNums(clipboard):
    phoneNums = re.compile(r'^(?:\d{8}(?:\d{2}(?:\d{2})?)?|\(\+?\d{2,3}\)\s?(?:\d{4}[\s*.-]?\d{4}|\d{3}[\s*.-]?\d{3}|\d{2}([\s*.-]?)\d{2}\1\d{2}(?:\1\d{2})?))$')
        #(\+\d{1,4})?                   #Optional country code (optional: +, 1-4 digits)
        #(\s)?                          #Optional space
        #(\(\d\))?                      #Optional bracketed area code
        #(\d\d(\s)?\d | \d{3})          #3 digits with optional space between
        #(\s)?                          #Optional space
        #(\d{3})                        #3 digits
        #(\s)?                          #Optional space
        #(\d{4})                        #Last four
        #)
        #)', re.VERBOSE)
    #nos = phoneNums.search(clipboard)  #ignore for now. Failed test of .group()

    return phoneNums.findall(clipboard)

#function for locating email addresses
def emails(clipboard):
    emails = re.compile(r'''(
        [a-z0-9._%+-]*     #username
        @                  #@ sign
        [a-z0-9.-]+        #domain name
        )''', re.I | re.VERBOSE)
    return emails.findall(clipboard)


#function for copying email addresses and numbers from webpage to a file
def scrape(fileName, saveLoc):
    newFile = os.path.join(saveLoc, fileName + ".txt")
    #file = open(newFile, "w+")
    #add phoneNums(currentText) +
    print(currentText)
    print(emails(currentText))
    print(phoneNums(currentText))
    #file.write(emails(currentText))
    #file.close()

url = ''
currentText = ''
file = ''
location =  ''

while True:
    print("Please paste text to scrape. Press ENTER to exit.")
    currentText = str(pyperclip.waitForNewPaste())
    #print("Filename?")
    #file = str(input())
    #print("Where shall I save this? Defaults to C:")
    #location = str(input())
    scrape(file, location)

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

[('+ 30 210 458 6600', '+30' , '', '', '210', '', '', '458', '', '6600'), ('+30 210 458 6601', '+30', '', '', ' 210 ',' ',' ',' 458 ',' ',' 6601 ')]

Как видите, числа правильно определены, но мой код жадный, поэтому я стараюсь добавив "+?":

def phoneNums(clipboard):
    phoneNums = re.compile(r'''(
        (\+\d{1,4})?                   #Optional country code (optional: +, 1-4 digits)
        (\s)?                          #Optional space
        (\(\d\))?                      #Optional bracketed area code
        (\d\d(\s)?\d | \d{3})          #3 digits with optional space between
        (\s)?                          #Optional space
        (\d{3})                        #3 digits
        (\s)?                          #Optional space
        (\d{4})                        #Last four
        )+?''', re.VERBOSE)

Радости нет. Я попытался вставить пример регулярного выражения отсюда: Найти номера телефонов в python script

Теперь я знаю, что это работает, потому что кто-то другой проверил это. То, что я получаю, это:

Please paste text to scrape. Press ENTER to exit. 
[] [] 
Please paste text to scrape. Press ENTER to exit. 
[] [('', '', '', '', '', '', '','', '', '')] 
...forever...

Этот последний даже не позволяет мне копировать в буфер обмена. .waitForNewPaste () должен делать то, что говорит на жестяной коробке, но в тот момент, когда я запускаю код, программа извлекает что-то из буфера обмена и пытается его обработать (плохо). в моем коде, но я не могу видеть это. Есть идеи?

1 Ответ

2 голосов
/ 01 мая 2020

Как вы указали, регулярное выражение работает.

Входная часть '+30 210 458 6600' сопоставляется один раз, и в результате получается кортеж всех захваченных подгрупп: ('+30 210 458 6600 ',' +30 ',' ',' ',' 210 ',' ',' ',' 458 ',' ',' 6600 ')

Обратите внимание, что первый элемент в кортеже является полным совпадением.

Если вы сделаете все группы не записывающими , вставив ?: после открывающей скобки, то не останется никаких групп захвата, и результат будет только полное совпадение «+30 210 458 6600» как str.

    phoneNums = re.compile(r'''
        (?:\+\d{1,4})?                   #Optional country code (optional: +, 1-4 digits)
        (?:\s)?                          #Optional space
        (?:\(\d\))?                      #Optional bracketed area code
        (?:\d\d(?:\s)?\d | \d{3})        #3 digits with optional space between
        (?:\s)?                          #Optional space
        (?:\d{3})                        #3 digits
        (?:\s)?                          #Optional space
        (?:\d{4})                        #Last four
        ''', re.VERBOSE)

код «повторяется навсегда», поскольку блок while True: представляет собой бесконечный l oop. Если вы хотите остановиться после, скажем, одной итерации, вы можете поместить оператор break в конце блока, останавливая l oop.

while True:
    currentText = str(pyperclip.waitForNewPaste())
    scrape(file, location)
    break
...