인프런 파이썬 입문 수업을 듣고 중요한 내용을 정리했습니다.
개인 공부 후 자료를 남기기 위한 목적이므로 내용 상에 오류가 있을 수 있습니다.
크롤링을 위한 지식(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개 숫자)를 *******로 바꾸는 코드이다.
'Python' 카테고리의 다른 글
Python - 크롤링 실전 연습(2) (1) | 2021.11.04 |
---|---|
Python - 크롤링을 위한 지식(문자열 함수) (0) | 2021.11.04 |
Python - 정부 공공 데이터 Open API로 크롤링 하기 (0) | 2021.11.03 |
Python - 네이버 검색 Open API를 활용한 크롤링 (1) | 2021.11.01 |
Python - 크롤링을 위한 지식(Open API, JSON) (0) | 2021.11.01 |
댓글