본문 바로가기
Python

Python - 크롤링을 위한 지식(정규 표현식)

by DGK 2021. 11. 4.

 

인프런 파이썬 입문 수업을 듣고 중요한 내용을 정리했습니다.
개인 공부 후 자료를 남기기 위한 목적이므로 내용 상에 오류가 있을 수 있습니다.

 

크롤링을 위한 지식(Python)

예제를 통해, 크롤링을 위한 파이썬의 정규 표현식을 다루고자 한다.

 

정규 표현식

정규 표현식이란?

 

정규 표현식은 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식이다.

일반적으로 정규 표현식은 일정한 규칙을 가지고 작성되며, 필요한 패턴은 직접 만들 수 있다. (아래 표 참고)

 

정규 표현식 예시

 

1. Dot(.)

  • Dot(.) 메타 문자는 줄바꿈 문자인 \n을 제외한 모든 문자(한 개)를 의미한다.
  • 예를 들어, D.A는 D + 모든 문자(한 개) + A를 의미한다. (ex. DAA, DvA, D1A)

 

import re
pattern = re.compile('D.A')

 

re 라이브러리는 정규 표현식 관련 함수를 가지고 있다.

pattern = re.compile('D.A') 코드는 정규 표현식의 패턴을 만든 것이다.(re 라이브러리의 compile() 함수 활용)

 

 

print(pattern.search("DAA"))
print(pattern.search("D1A"))
print(pattern.search("d0A D1A 0111"))

 

```

결과 :

<re.Match object; span=(0, 3), match='DAA'>

<re.Match object; span=(0, 3), match='D1A'>

<re.Match object; span=(4, 7), match='D1A'>

 

```

 

참고로, search() 함수는 인자로 들어온 문자열을 정규 표현식과 매치하여 패턴과 맞는 문자열을 찾아준다.

 

 

print(pattern.search("D00A"))
print(pattern.search("DA"))
print(pattern.search("d0A"))

 

```

결과 :

None

None

None

 

```

 

앞서 정한 정규 표현식 패턴에 맞지 않는 인자를 넣으면 None 값이 반환된다.

 

 

pattern = re.compile('D\.A')

print(pattern.search("D.A"))
print(pattern.search("DDA"))


pattern = re.compile('D[.]A')

print(pattern.search("D.A"))
print(pattern.search("DDA"))

 

```

결과 :

<re.Match object; span=(0, 3), match='D.A'>

None

 

<re.Match object; span=(0, 3), match='D.A'>

None

 

```

 

만약, 문자열에서 문자 Dot(.)이 들어간 패턴을 찾으려면 Dot(.)을 \. 또는 [.]으로 표시하면 된다.

 

 

string = "DDA D1A DDA DA"

print(re.sub('D.A', 'Dave', string))

 

```

결과 : 

Dave  Dave  Dave  DA

 

```

 

re 라이브러리의 sub() 함수를 사용하여, 정규 표현식 패턴과 매칭되는 문자열을 찾아 다른 문자열로 바꿀 수 있다.

sub() 함수의 첫 번째 인자는 정규식 표현, 두 번째 인자는 교체할 데이터, 세 번째 인자는 기존의 문자열을 의미한다.

 

 

2. 반복( ?,  *,  + )

  • ?는 앞 문자가 0번 또는 1번 표시되는 패턴이다. (없어도 되고, 한 번 있어도 되는 패턴)
  • *는 앞 문자가 0번 또는 1번 이상 반복되는 패턴이다. (없어도 되고, 한 번 이상 있어도 되는 패턴)
  • +는 앞 문자가 1번 또는 그 이상 반복되는 패턴이다. (없으면 안되고, 적어도 한 번 이상 있어야 되는 패턴)

 

pattern = re.compile('D?A')

print(pattern.search("A"))
print(pattern.search("DA"))
print(pattern.search("DDDDDDA"))
print(pattern.search('DDDDAA'))

 

  • ? 사용 예시

``` 

결과 :

 

<re.Match object; span=(0, 1), match='A'>

<re.Match object; span=(0, 2), match='DA'>

<re.Match object; span=(5, 7), match='DA'>

<re.Match object; span=(3, 5), match='DA'>

 

```

 

? 앞의 문자인 D가 없거나, 1번 나오고 마지막에 A가 나오는 문자열을 찾는 패턴이다.

 

 

pattern = re.compile('D*A')

print(pattern.search("A"))
print(pattern.search("DA"))
print(pattern.search("DDDDDDDDDDDDDDDDDDDDDDDDDDDDA"))

 

  • * 사용 예시

```

결과 :

 

<re.Match object; span=(0, 1), match='A'>

<re.Match object; span=(0, 2), match='DA'>

<re.Match object; span=(27, 29), match='DA'>

 

```

 

* 앞의 문자인 D가 없거나, 최소 1번 이상 반복되고 마지막에 A가 나오는 문자열을 찾는 패턴이다.

 

 

pattern = re.compile('D+A')

print(pattern.search("A"))
print(pattern.search("DA"))
print(pattern.search("DDDDDDDDDDDDDDDDDDDDDDDDDDDDA"))

 

  • + 사용 예시

```

결과 :

 

None
<re.Match object; span=(0, 2), match='DA'>

<re.Match object; span=(0, 29), match='DDDDDDDDDDDDDDDDDDDDDDDDDDDDA'>

 

```

 

+ 앞의 문자인 D가 1번 또는 그 이상 나오고, 마지막에 A가 나오는 문자열을 찾는 패턴이다.

 

 

3. 또 다른 반복 표현({n},  {m,n})

  • {n}은 앞 문자가 n번 반복되는 패턴이다.
  • {m,n}은 앞 문자가 m번 부터 n번까지 반복되는 패턴이다.

 

pattern = re.compile('AD{2}A')

print(pattern.search("ADA"))
print(pattern.search("ADDA"))
print(pattern.search("ADDDA"))

 

  • {n} 사용 예시

```

결과 :

None

<re.Match object; span=(0, 4), match='ADDA'>

None

 

```

 

맨 앞과 맨 뒤에 A가 나오고, D가 2번 반복되는 문자열을 찾는 패턴이다.

 

 

pattern = re.compile('AD{2,6}A')

print(pattern.search("ADDA"))
print(pattern.search("ADDDA"))
print(pattern.search("ADDDDDDA"))

 

  • {m,n} 사용 예시

```

결과 :

<re.Match object; span=(0, 4), match='ADDA'>

<re.Match object; span=(0, 5), match='ADDDA'>

<re.Match object; span=(0, 8), match='ADDDDDDA'>

 

```

 

맨 앞과 맨 뒤에 A가 나오고, D가 2~6번까지 반복되는 문자열을 찾는 패턴이다.

참고로, {m,n}에서 m과 n은 ,로 붙여서 써야한다.(m과 n 그리고 , 사이에 공백이 있으면 에러가 발생함)

 

 

4. [ ] 괄호 안에 들어가는 문자가 들어 있는 패턴

  • 예를 들면, [abc]는 a, b, c 중 하나가 들어있는 패턴을 의미한다.
  • 또한, 하이픈(-)을 이용하면 알파벳 전체 혹은 숫자 전체를 의미하는 패턴을 만들 수 있다. (ex. [a-z], [A-Z], [0-9] )
  • [ ] 괄호 안에서 [ 바로 뒤에 ^을 쓰면, 그 뒤에 오는 문자가 아닌 패턴을 찾겠다는 의미이다.
  • 문자는 알파벳, 숫자, 특수문자, whitespace(스페이스, 탭, 엔터 등)로 분류된다.
  • [^ \t \n \r \f \v]의 코드는 whitespace이 아닌 알파벳, 숫자, 특수문자를 찾는 패턴을 의미한다.

 

pattern = re.compile('[abcdefgABCDEFG]')

print(pattern.search("a1234"))
print(pattern.search("z1234"))

 

  • [ ] 사용 예시

```

결과 :

<re.Match object; span=(0, 1), match='a'>

<re.Match object; span=(0, 1), match='z'>

 

```

 

 

pattern = re.compile('[a-z]')    

print(pattern.search("k1234"))
print(pattern.search("Z1234"))

 

  • 하이픈(-)을 이용한 [ ] 사용 예시

```

결과 :

<re.Match object; span=(0, 1), match='k'>

None

 

```

 

 

pattern = re.compile('[a-zA-Z]') 

print(pattern.search("Z1234"))

 

  • 하이픈(-)을 이용한 [ ] 사용 예시

```

결과 :

<re.Match object; span=(0, 1), match='Z'>

 

```

 

[a-zA-Z]로 표기하면, 대·소문자를 모두 포함해서 알파벳 전체를 찾는 패턴을 만들 수 있다.

 

 

pattern = re.compile('[a-zA-Z0-9]')

print(pattern.search("1234---"))
print(pattern.search("---------------!@#!@$!$%#%%%#%%@$!$!---") )

 

  • 하이픈(-)을 이용한 [ ] 사용 예시

```

결과 :

<re.Match object; span=(0, 1), match='1'>

None

 

```

 

[a-zA-Z0-9]로 표기하면, 대·소문자를 모두 포함한 알파벳 전체와 숫자 전체를 찾는 패턴을 만들 수 있다.

즉, [a-zA-Z0-9]는 특수문자를 제외한 모든 패턴의 문자열을 의미한다.

 

 

pattern = re.compile('[^a-zA-Z0-9]') 

print(pattern.search("---------------!@#!@$!$%#%%%#%%@$!$!---") )

 

  • ^를 이용한 [ ] 사용 예시

```

결과 :

<re.Match object; span=(0, 1), match='-'>

 

```

 

[^a-zA-Z0-9]는 대·소문자, 숫자를 제외한 모든 문자열 패턴이므로 특수문자 문자열을 찾는 패턴을 의미한다.

 

 

pattern = re.compile('[^ \t\n\r\f\v]') 

print(pattern.search("-"))
print(pattern.search(" "))

 

  • ^를 이용한 [ ] 사용 예시

```

결과 :

<re.Match object; span=(0, 1), match='-'>

None

 

```

 

공백과 whitespace(스페이스, 탭, 엔터 등)이 아닌 모든 문자열()을 찾는 패턴을 의미한다.

 

 

pattern = re.compile('[가-힣]')

print(pattern.search("안"))

 

  • 하이픈(-)을 이용한 [ ] 사용 예시

```

결과 :

<re.Match object; span=(0, 1), match='안'>

 

```

 

[가-힣]의 코드는 모든 한글을 찾는 문자열 패턴을 의미한다. 

 

 

import re

pattern = re.compile('[a-zA-Z]+')
matched = pattern.search("Dave")

print(matched)

 

  • 응용 예시

```

결과 :

<re.Match object; span=(0, 4), match='Dave'>

 

```

 

 

정규 표현식 라이브러리 함수 활용

  • match() 함수는 문자열의 첫 글자부터 정규식과 매칭되는 패턴을 찾아서 리턴한다.
  • search() 함수는 문자열 전체에서 정규식과 매칭되는 패턴을 찾아서 리턴한다.
  • findall() 함수는 문자열에서 정규 표현식과 매칭되는 모든 문자(열)를 리스트 객체로 리턴한다.
  • split() 함수는 사용자가 선택한 패턴을 기준으로 특정 문자열을 분리시켜 리턴한다. 
  • sub() 함수는 정규 표현식의 패턴을 다른 문자열 또는 기호로 변경시켜준다.

 

import re

pattern = re.compile('[a-z]+')
matched = pattern.match('Dave')
searched = pattern.search("Dave")

print(matched)
print(searched)

 

  • match(), search()

```

결과 :

None

<re.Match object; span=(1, 4), match='ave'>

 

```

 

소문자 알파벳이 적어도 1번 이상 나오는 문자열을 찾는 패턴이다.

참고로, match() 함수는 해당 문자열의 첫 글자부터 정규 표현식과 매치되지 않으면 바로 None 값을 리턴한다.

 

 

import re

pattern = re.compile('[a-z]+')
findalled = pattern.findall('Game of Life in Python')
print(findalled)

 

  • findall() 

```

결과 :

['ame', 'of', 'ife', 'in', 'ython']

 

```

 

findall() 함수 인자로 들어온 문자열에서 소문자 알파벳이 적어도 1번 이상 나오면 모두 찾아서 리턴해준다.

 

 

import re

pattern2 = re.compile('[A-Za-z]+')
findalled2 = pattern2.findall('Game of Life in Python')
print(findalled2)

 

  • findall()

```

결과 :

['Game', 'of', 'Life', 'in', 'Python']

 

```

 

findall() 함수 인자로 들어온 문자열에서 대·소문자 알파벳이 적어도 1번 이상 나오면 모두 찾아서 리턴한다.

[A-Za-z]+ 정규식 패턴은 특정 문자열에서 단어를 찾아서 리스트로 변환할 때 많이 사용한다.

 

 

import re
pattern = re.compile('[a-z]+')
findalled = pattern.findall('GAME')
if len(findalled) > 0:
    print ("정규표현식에 맞는 문자열이 존재함")
else:
    print ("정규표현식에 맞는 문자열이 존재하지 않음")

 

  • findall()

```

결과 :

정규표현식에 맞는 문자열이 존재하지 않음

 

```

 

findall() 함수를 사용해서 함수의 인자로 들어간 문자열에서 사용자가 원하는 패턴의 문자 패턴이 존재하는지를

확인하는 코드이다.

 

 

import re

pattern2 = re.compile(':')
splited = pattern2.split('python:java')
print(splited)

 

  • split()

```

결과 :

['python', 'java']

 

```

 

split() 함수를 사용하여, 함수의 인자로 들어온 문자열을 사용자가 원하는 패턴으로 기준삼아 분리할 수 있다.

 

 

import re

pattern2 = re.compile(' [A-Z]{2} ')
splited = pattern2.split('python VS java')
print(splited)

 

  • split()

```

결과 :

['python', 'java']

 

```

 

위의 예시와 동일한 원리이다.

 

 

import re

pattern2 = re.compile('-')
subed = pattern2.sub('*', '801210-1011323')
print(subed)

 

  • sub()

```

결과 :

801210*1011323

 

```

 

sub() 함수를 사용하여, 함수의 인자로 받은 문자열에 포함된 정규 표현식을 다른 문자 또는 기호로 바꿀 수 있다.

참고로, sub() 함수의 첫 번째 인자는 기존의 정규 표현식 패턴을 대체할 문자열 혹은 기호이며, 두 번째 인자는 원래의

문자열을 의미한다.

즉, 기존 문자열 안에 존재하는 정규 표현식의 패턴이 sub() 함수의 첫 번째 인자로 바뀌는 것이다.

 

 

subed = re.sub('-', '*', '801210-1011323')

print(subed)

 

  • sub()

```

결과 :

801210*1011323

 

```

 

re 라이브러리명에 곧 바로 sub() 함수를 붙여서 사용할 수 있다.

참고로, sub() 함수의 첫 번째 인자는 원래 문자열의 정규식 패턴이고, 두 번째 인자는 정규식 패턴을 대체할 문자 또는

기호이며, 세 번째 인자는 원래의 문자열을 의미한다.

 

 

import openpyxl

work_book = openpyxl.load_workbook('data_kr.xlsx')
work_sheet = work_book.active
for each_row in work_sheet.rows:
    print(re.sub('-[0-9]{7}', '-*******', each_row[1].value))

work_book.close()

 

  • 활용 예시

```

결과 :

 

주민등록번호

800215-*******

821030-*******

841230-*******

790903-*******

800125-*******

820612-*******

 

```

 

엑셀 파일 안에 저장된 6개의 주민등록번호 뒷자리(7개 숫자)를 *******로 바꾸는 코드이다.

 

댓글