파이썬 스크레이핑 - 정규표현식(regular expression)이란

안녕하세요 jay입니다.
지난 시간에는 urllib.request를 활용하여 웹크롤링을 해봤습니다.
이번 시간에는 크롤링한 정보를 가지고 스크레이핑을 하기 위해
정규표현식에 대해서 알아보도록 하겠습니다.


1. 정규표현식(regular expression : regex)이란?

정규 표현식은 특정한 규칙을 가진 문자열의 패턴을 표현하는 데 사용하는 표현식(Expression)으로 텍스트에서 특정 문자열을 검색하거나 치환할 때 흔히 사용됩니다.
HTML을 단순한 문자열로 취급하고, 필요한 부분을 추출해야합니다. 정규표현식에선 ₩(\)가 자주 나옵니다. 이때 r""을 사용합니다.

print('\n is an escape code') # \n을 명령기호(이스케이프문자)로 인식하여 줄바꾸기가 됨 is an escape code
print(r'\n is an escape code') # \n을 문자열로 취급함 \n is an excape code

2. re모듈을 사용해서 스크레이핑하기

형식 : re.함수명(정규표현식, 스크레이핑할 데이터가 들어있는 HTML 또는 RSS)

import re

#search()함수 문자열 전체를 검색하여 정규식과 매치되는지 조사하여 매치가 되면 Match 객체를 돌려줌
print(re.search(r"a.*c", 'abc123DEF')) # <re.Match object; span=(0, 3), match='abc'>
print(re.search(r"a.*d", 'abc123DEF')) # None

3. 문자 클래스 []

문자 클래스로 만들어진 정규식은 "[]"사이의 문자들과 매치라는 것을 의미
문자 클래스 "[]" 안에는 어떤 문자 또는 메타 문자라도 사용할 수 있습니다.
메타 문자 : 문자가 가진 원래의 의미가 아닌 특별한 용도로 사용되는 문자를 말합니다.
문자 클래스 안에 ^ 메타 문자를 사용할 경우에는 반대(not)라는 의미

예시 :

[a-zA-Z] : 알파벳 모두
[0-9] : 숫자

* 자주 사용하는 문자 클래스
-\d - 숫자와 매치, [0-9]와 동일한 표현식
-\D = 숫자가 아닌 것과 매치, [^0-9]와 동일한 표현식
-\s - whitespace 문자와 매치, [\t\n\r\f\v]와 동일한 표현식
-\S -whitespace 문자가 아닌 것과 매치, [^ \t\n\r\f\v]와 동일한 표현식
-\w - 문자 + 숫자와 매치, [a-zA-Z0-9_]와 동일한 표현식
-\W - 문자 + 숫자와 매치, [^a-zA-Z0-9_]와 동일한 표현식

import re

# \d - 숫자와 매치, [0-9]와 동일한 표현식

print(re.search(r"\d", 'abc123DEF')) # <re.Match object; span=(3, 4), match='1'>
print(re.search("[\d]", 'abc123DEF')) # <re.Match object; span=(3, 4), match='1'>
print(re.search("[0-9]", 'abc123DEF')) # <re.Match object; span=(3, 4), match='1'>

# \D - 숫자가 아닌 것과 매치, [^0-9]와 동일한 표현식
print(re.search(r"\D" , "abcd(342)")) # <re.Match object; span=(0, 1), match='a'>
print(re.search("\D" , "(abcd(342)")) # <re.Match object; span=(0, 1), match='('>
print(re.search("[^0-9]", 'abc123*DEF')) # <re.Match object; span=(0, 1), match='a'>

# \s - whitespace 문자와 매치, [\t\n\r\f\v]와 동일한 표현식
print(re.search(r"\s", ' abc123DEF')) # <re.Match object; span=(0, 1), match=' '>
print(re.search("[\t]", '\tabc123DEF')) # <re.Match object; span=(0, 1), match='\t'>

# \S - whitespace 문자가 아닌 것과 매치, [^ \t\n\r\f\v]와 동일한 표현식
print(re.search(r"\S", '   abc123DEF')) # <re.Match object; span=(3, 4), match='a'>

# \w - 문자+숫자(alphanumeric)와 매치, [a-zA-Z0-9_]와 동일한 표현식
print(re.search(r"\w", 'abc123DEF')) # <re.Match object; span=(0, 1), match='a'>
print(re.search("[\w]", ")!@#(!@)12342")) # <re.Match object; span=(8, 9), match='1'>

# \W - 문자+숫자(alphanumeric)가 아닌 문자와 매치, [^a-zA-Z0-9_]와 동일한 표현식
print(re.search(r"\W",'abc123D()!@$EF')) # <re.Match object; span=(7, 8), match='('>

4. 정규표현식 패턴

패턴설명예제
^이 패턴으로 시작해야 함^abc : abc로 시작해야 함 (abcd, abc12 등)
$이 패턴으로 종료되어야 함xyz$ : xyz로 종료되어야 함 (123xyz, strxyz 등)
[문자들]문자들 중에 하나이어야 함. 가능한 문자들의 집합을 정의함.[Pp]ython : "Python" 혹은 "python"
[^문자들][문자들]의 반대로 피해야할 문자들의 집합을 정의함.[^aeiou] : 소문자 모음이 아닌 문자들
|두 패턴 중 하나이어야 함 (OR 기능)a | b : a 또는 b 이어야 함
?앞 패턴이 없거나 하나이어야 함 (Optional 패턴을 정의할 때 사용)\d? : 숫자가 하나 있거나 없어야 함
+앞 패턴이 하나 이상이어야 함\d+ : 숫자가 하나 이상이어야 함
*앞 패턴이 0개 이상이어야 함\d* : 숫자가 없거나 하나 이상이어야 함
패턴{n}앞 패턴이 n번 반복해서 나타나는 경우\d{3} : 숫자가 3개 있어야 함
패턴{n, m}앞 패턴이 최소 n번, 최대 m 번 반복해서 나타나는 경우 (n 또는 m 은 생략 가능)\d{3,5} : 숫자가 3, 4개 혹은 5개 있어야 함
참고 : http://pythonstudy.xyz/python/article/401-%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D-Regex

예시 :


import re

# Dot(.) : 줄바꿈문자인 \n을 제외한 모든문자와 매치됨을 의미
# 정규식을 작성할 때 re.DOTALL 옵션을 주면 \n 문자와도 매치됨

print(re.search("\w.\d", 'abc123DEF')) # <re.Match object; span=(1, 4), match='bc1'>
print(re.search(r"a.c", 'a\nc123DEF')) # None
print(re.search(r"a.c", 'a\nc123DEF', re.DOTALL)) # <re.Match object; span=(0, 3), match='a\nc'>


# 반복(*) : * 앞에 나온 문자가 0부터 무한대로 반복될 수 있다는 의미
print(re.search(r"a.*d", "abc123d")) # <re.Match object; span=(0, 7), match='abc123d'>
print(re.search(r"a*d", "abc123d")) # <re.Match object; span=(6, 7), match='d'>
print(re.search("[^\d].*[\d]" , "wsasdaska3jsklda")) # <re.Match object; span=(0, 10), match='wsasdaska3'>


# 반복(+) : + 바로 앞에 있는 문자가 최소 1번이상 반복될 수 있다는 의미
print(re.search(r"ca+t", 'abcat')) # <re.Match object; span=(2, 5), match='cat'>
print(re.search(r"ca+t", 'abcaaaaaaat')) # <re.Match object; span=(2, 11), match='caaaaaaat'>
print(re.search("[^\d]+a" , "wsasdaska3jsklda")) # <re.Match object; span=(0, 9), match='wsasdaska'>


# 반복({m,n}) {m,n} : {m,n} 표현식 앞에 있는 문자가 m부터 n까지 반복될 수 있다는 의미
# {m,}이나{,n}과 같이 사용할 수 있음
print(re.search(r"\D{3,5}", "4832avcd42ds")) # <re.Match object; span=(4, 8), match='avcd'>
print(re.search("\d{3,4}-\d{4,}-\d{,4}", "010-1234-5678")) # <re.Match object; span=(0, 13), match='010-1234-5678'>


# ^ : 이 패턴으로 시작해야 함, $ : 이 패턴으로 끝내야함
print(re.search("(^[\D])","BASDASD124@ASF")) # <re.Match object; span=(0, 1), match='B'>
print(re.search("(\d{4}$)","010-1234-5688")) # <re.Match object; span=(9, 13), match='5688'>

# group : ()를 사용, 그룹핑을 활용하여 
text = "내가 찾을 양식은 031-123-1234입니다."
regex = re.compile(r'(\d{3})-(\d{3}-\d{4})') # 정규 표현식
matchobj = regex.search(text)
areaCode = matchobj.group(1)
num = matchobj.group(2)
print("지역번호는 : ",areaCode," 뒷번호는 : ", num)

다음 시간에는 re 모듈에 대해 알아보도록 하겠습니다.

댓글