public bigdata

파이썬을 이용한 웹 스크래핑(edwith 강의) chapter 1 정규식-1 본문

Python programming/웹 크롤링

파이썬을 이용한 웹 스크래핑(edwith 강의) chapter 1 정규식-1

public bigdata 2019. 11. 17. 01:13

잘 알려주셔서 감사합니다.

pc 에서 보는 경우 코드가 길어서 보이지 않는데 (Shift + 마우스 드래그) 조합을 사용하면 쉽게 볼 수 있다.

1강 

## 정규식 간단한 요약 ## 

# ^           라인의 처음을 매칭
# $            라인의 끝을 매칭
# .             임의의 문자를 매칭 (와일드 카드)
# \s          공백 문자를 매칭
# \S         공백이 아닌 문자를 매칭
# *            바로 앞선 문자에 적용되고 0 혹은 그 이상의 앞선 문자와 매칭을 표기함.
# *?          바로 앞선 문자에 적용되고 0 혹은 그 이상의 앞선 문자와 매칭을 탐욕적이지 않은 방식으로 표기함.
# +           바로 앞선 문자에 적용되고 1 혹은 그 이상의 앞선 문자와 매칭을 표기함
# +?          바로 앞선 문자에 적용되고 1 혹은 그 이상의 앞선 문자와 매칭을 탐욕적이지 않은 방식으로 표기함.
# [aeiou]    명세된 집합 문자에 존재하는 단일 문자와 매칭. “a”, “e”, “i”, “o”, “u” 문자만 매칭되는 예제
# [a-z0-9]    - 기호로 문자 범위를 명세할 수 있다. 소문자이거나 숫자인 단일 문자만 매칭되는 예제.
# ( )         괄호가 정규표현식에 추가될 때, 매칭을 무시한다. 하지만 findall()을 사용 할 때 전체 문자열보다 매칭된 문자열의 상세한 부속 문자열을 추출할 수 있게 한다.


## 디렉토리 지정(스크립트의 현재 위치) ##
#1-1 텍스트에서 문자 패턴 찾기

hand = open('mbox-short.txt')
for line in hand:
    line = line.rstrip()
    if line.find('From:')>=0: # find 함수는 없으면 -1 반환
        print(line)

#1-2 정규식으로 패턴 찾기
import re
hand = open('mbox-short.txt')
for line in hand:
    line = line.strip()
    if re.search('From:', line):
        print(line)
#1-3 텍스트에서 시작하는 패턴으로 찾기
import re
hand = open('mbox-short.txt')        
for line in hand:
    line = line.strip()
    if line.startswith("From:"):
        print(line)

#1-4 텍스트에서 정규식으로 시작패턴 찾기
import re
hand = open('mbox-short.txt')
for line in hand:
    line = line.strip()
    if re.search("^From:", line):
        print(line)

## 특수 문자를 활용한 문자 패턴 찾기 ##
# ^:문장의 시작을 의미
# .:어떤 문자 한 글자
# *:앞의 문자가 여러 번 반복될 수 있음을 의미
# +:앞의 문자가 1번 이상 나타남을 의미
# \S:공백 문자가 아닌 한 개의 문자

## 예시 ##
# ^X-\S+: 'X-'로 시작하면서  ':'와의 사이에 공백이 아닌 문자 여러개 있는 경우 검색

 

 

다음과 같은 문자열들은 모두 '^X.*:'라는 패턴을 통해 찾을 수 있습니다.

  • X-Sieve: CMU Sieve 2.3

  • X-DSPAM-Result: Innocent

  • X-DSPAM-Confidence: 0.8475

  • X-Content-Type-Message-Body: text/plain

그리고 다음과 같은 문자열들은 '^X-\S+:' 패턴으로 찾을 수 있으며,

  • X-Sieve: CMU Sieve 2.3

  • X-DSPAM-Result: Innocent

다음의 문자열은 'X-'와 ':' 사이에 공백 문자가 아닌 문자가 포함되지 않았기 때문에 '^X-\S+:' 패턴으로 찾을 수 없습니다.

  • X-: Very short

  • X-Plane is behind schedule: two weeks

<궁금증>

1) *, *?의 차이

*는 앞의 문자가 여러번 반복될 수 있음을 즉. 앞선 문자가 0 또는 그 이상 반복될수 있다는 말이고, *?는 *과 동일하지만 차이점은 *?는 해당 정규식을 더 이상 만족하지 않을 때 까지 모두 검색해 본다. 따라서 "<abcd> sdfsdffds dsf<dssdf>"와 같은 문자가 있을 때 난 "<abcd>"부분만 매칭하고 싶은 경우 정규식을 "<.*>"라고 작성하면 "<abcd>"가 매칭될 것 같지만 *는 greedy(탐욕)하기 때문에 "<abcd>"가 매칭이 되는데도 불구하고 연장해서 매칭을 시도해 보기 때문에 "<abcd> sdfsdffds dsf<dssdf>"가 매칭된다.

"<abcd>"만 매칭하려면 탐욕적이지 않은 *?를 사용한다. "<.*?> 정규식을 사용해서 매칭하면 원했던 데로 "<abcd>"가 매칭된다.!!!

string1 = "<abcd> sdfsdffds dsf<dssdf>"

re.match("<.*>", string1) ## 출력 결과물 : <re.Match object; span=(0, 27), match='<abcd> sdfsdffds dsf<dssdf>'>
re.match("<.*?>", string1) ## 출력 결과물 : <re.Match object; span=(0, 6), match='<abcd>'>

위 예시를 보면 쉽게 이해가 될 것이다.

2) ()은 잘 이해가 안된다.

인터넷에서 위 캡쳐처럼 설명을 해두었는데, "abcd"라는 문자가 있을 때 "[abcd]"라는 정규식을 사용하면 "a"만 매칭된다. 가장 먼저 오기 때문에 즉 "[abcd]"는 이 중에서 단 하나 매칭되는 값을 검색한다.(아직 잘 모르겠음) "(abcd)"라고 정규식을 사용하면 아래처럼 된다.!

string1 = "<abcd> sdfsdffds dsf<dssdf>"

re.search("[abcd]", string1) ## 결과 : <re.Match object; span=(1, 2), match='a'>
re.search("(abcd)", string1) ## 결과 : <re.Match object; span=(1, 5), match='abcd'>