Skip to content

Instantly share code, notes, and snippets.

@re4lfl0w
Last active September 20, 2020 06:50
Show Gist options
  • Save re4lfl0w/4dc18d1e0b4f8877476d069036cbec95 to your computer and use it in GitHub Desktop.
Save re4lfl0w/4dc18d1e0b4f8877476d069036cbec95 to your computer and use it in GitHub Desktop.
앞에 url만 추출할 수 있는 방법

앞에 url만 추출할 수 있는 방법 문의

import re

s1 = 'http://naver.com/path/ab1.htmlhttp'
s2 = 'http://naver.com/path/ab1.htmlhttp://daum.net/aaa/1.html'

http_regex = re.compile(r'''
    #     ^                     # Anchor to start of string.
        (?:https?://)?
        (?!.{1, 256})            # Whole domain must be 255 or less.
        (?:                   # One or more sub-domains.
          [a-z0-9]            # Subdomain begins with alpha-num.
          (?:                 # Optionally more than one char.
            [a-z0-9-]{0,61}   # Middle part may have dashes.
            [a-z0-9]          # Starts and ends with alpha-num.
          )?                  # Subdomain length from 1 to 63.
          \.                  # Required dot separates subdomains.
        )+                    # End one or more sub-domains.
        (?:                   # Top level domain (length from 1 to 63).
          [a-z]{1,63}         # Either traditional-tld-label = 1*63(ALPHA).
        | xn--[a-z0-9]{1,59}  # Or an idn-label = Restricted-A-Label.
        )                     # End top level domain.
        /[-a-zA-Z0-9]+(?!http)
    #     $                     # Anchor to end of string.'''
                             , re.X | re.I | re.M)
                             
print(http_regex.findall(s1))
# http://naver.com/path/ab1.htmlhttp
print(http_regex.findall(s2))
# http://naver.com/path/ab1.htmlhttp
  • [-a-zA-Z0-9]+ 이 부분에 의해서 마지막에 있는 http까지 매칭이 되어 버리네요.
  • 이걸 어떻게 해결할 수 있을까요?
  • 정규표현식으로 뽑아내고 다시 한 번 정규표현식을 돌려서 앞에것만 가져오게 하면 되긴 하는데. 한 번의 정규표현식으로 해결 할 수는 없을까요?
  • 예를 들어 다 가져온 다음에 http 뒤로만 다 짤라내면 해결은 되는데 정규표현식 한 방에 해결이 안되네요.
@re4lfl0w
Copy link
Author

re4lfl0w commented Sep 18, 2020

(?=^https?://.https?://.)((https?://.)https?://.)|(.*)$
뒤에 url scheme이 더 붙는지 조건에 따라... 캡처링 그룹이 분기되는 정규식입니다. 이건 잘 되는거 같군요 ㅎㅎ;
마지막 캡처링 그룹이 원하시는 결과가 될 것 같네요.

이렇게 댓글 남겨주셨는데 이걸로 어느 정도는 되는데 또 다른 에외 케이스는 어렵네요. 정규식이 너무 복잡해져서 저도 해석하기가 힘드니 1차로 추출하고 2차로 뒤의 http만 찾아서 짤라내는걸로 끝내야겠어요.
답변 감사합니다!

@ychoi-kr
Copy link

ychoi-kr commented Sep 19, 2020

끝내셨지만.. 호기심에 한번 풀어봤습니다.

테스트를 편하게 하려고 unittest를 만들었고요.
문제 해결을 위해 정규 표현식의 lookahead assertion이라는 기능을 이용했습니다((?=http)). 그렇게 하려고 find() 함수에서 텍스트 끝에 'http'를 붙여서 검색을 했습니다.
그리고 URL에서 path를 찾는 부분은 0회 이상 반복되는 것을 매치하려고 괄호로 감싸서 *를 붙였습니다.

import re, unittest

http_regex = re.compile(r'''
    #     ^                     # Anchor to start of string.
        (?:https?://)?
        (?!.{1, 256})            # Whole domain must be 255 or less.
        (?:                   # One or more sub-domains.
          [a-z0-9]            # Subdomain begins with alpha-num.
          (?:                 # Optionally more than one char.
            [a-z0-9-]{0,61}   # Middle part may have dashes.
            [a-z0-9]          # Starts and ends with alpha-num.
          )?                  # Subdomain length from 1 to 63.
          \.                  # Required dot separates subdomains.
        )+                    # End one or more sub-domains.
        (?:                   # Top level domain (length from 1 to 63).
          [a-z]{1,63}         # Either traditional-tld-label = 1*63(ALPHA).
        | xn--[a-z0-9]{1,59}  # Or an idn-label = Restricted-A-Label.
        )                     # End top level domain.
        (?:/[-a-zA-Z0-9.]+)*
        (?=http)
    #     $                     # Anchor to end of string.'''
                             , re.X | re.I | re.M)

def find(text):
    if not text.endswith('http'):
        text += 'http'
    return http_regex.findall(text)

class TestRegex(unittest.TestCase):

    def test_1(self):
        self.assertEqual(
            find('http://naver.com/path/ab1.htmlhttp')[0],
            'http://naver.com/path/ab1.html')

    def test_2_1(self):
        self.assertEqual(
            find('http://naver.com/path/ab1.htmlhttp://daum.net/aaa/1.html')[0],
            'http://naver.com/path/ab1.html')

    def test_2_2(self):
        self.assertEqual(
            find('http://naver.com/path/ab1.htmlhttp://daum.net/aaa/1.html')[1],
            'http://daum.net/aaa/1.html')



if __name__ == '__main__':
    unittest.main()

@kellywoo
Copy link

kellywoo commented Sep 20, 2020

저도 재미삼아 남겨봅니다. 무조건 http url이 붙은 것이 전제가 되어있어..
http가 아니거나 다른 문자에 대해서까지 처리 하려면 사실 정규식은 안 좋은 것 같습니다.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment