드라마 대본은 많은 문자열이 나름의 규칙을 가지고(대사,지문 등) 있어서 정규표현식을 연습하기에 매우 좋은 자료라고 할 수 있다.
이번 포스팅에서는 앞에서 공부한 입출력과 정규 표현식을 활용해 미드 '프렌즈' 대본 텍스트 파일을 가공해 볼 것이다.
먼저 사용할 모듈을 임포트 하자.
import os, re
그리고 파이썬 실행 위치를 텍스트 파일 저장 경로로 이동한다.
os.chdir(r'C:\Users\82105\Desktop') #friends.txt파일을 저장한 폴더로 이동
텍스트 파일을 불러와 객제 f에 저장하고, 이때 인코딩 오류가 발생할 수 있으므로 불러오는 방식을 utf-8로 지정하자.
그 뒤, 객체 f를 읽기 모드로 열어서 script101이라는 객제에 저장한다.
f = open('friends101.txt', 'r', encoding = 'utf8')
script101 = f.read()
1. 특정 등장인물의 대사만 모으기
첫번째 실습으로 모니카의 대사만 모아 출력해보자.
앞에서 re.search, re.match도 공부하였지만, 여기서는 re.findall을 사용할 것이다.
findcall은 찾은 조건식을 모두 리스트로 만들어 준다는 장점이 있다.
모니카의 대사들을 검색조건에 따라 sript101에서 모두 찾아 리스트로 반환해 Line 변수에 저장하자.
Line = re.findall(r'Monica:.+', script101)
#'Monica:'다음에 아무 문자나 반복되는(.+) 패턴을 script에서 찾아 리스트로 반환
명령어를 제대로 인식했는지 확인하기 위해 세 문장만 출력해보자.
for item in Line[:3]:
print(item) #Line리스트 요소 중 앞에서 3개 까지 출력
Monica: There's nothing to tell! He's just some guy I work with!
Monica: Okay, everybody relax. This is not even a date. It's just two people going out to dinner and- not having sex.
Monica: And they weren't looking at you before?!
실행하면 위와 같이 잘 출력된다.
f.close()
※파일을 open한 뒤에는 close 해줘야한다는 걸 잊지말자!
그러면 앞에서 리스트로 저장한 모니카의 대사를 텍스트 파일로 저장해보자.
먼저 저장할 파일 'monica.txt' 를 쓰기 모드로 만든다.
f = open('monica.txt', 'w' , encoding='utf8')
텍스트 파일에 대사를 저장하기 위해서는 Line리스트에 저장한 모니카 대사 텍스트를 문자열 형식으로 다시 저장해야한다.
따라서 빈 문자열 monica 객체를 생성한다.
Line리스트의 모든 원소를 가져와 monica 문자열에 추가하자.
monica = ''
for i in Line:
monica += i #Line리스트의 원소를 monica 문자열에 추가
이제 객체 f를 이용해서 monica.txt파일에 monica 문자열을 써보자.
f.write(monica)
f.close()
컴퓨터에서 monica.txt 파일을 찾아보면 정상적으로 대사가 잘 저장된 것을 확인할 수 있다.
2. 등장인물 리스트 만들기
이번에는 1화에 등장하는 등장인물 이름을 모두 모아보자.
이 작업을 위해서는 대본에 어떤 규칙이 있는지 꼼꼼하게 살펴보는 것이 좋다.
잘 살펴보면 등장인물이 'Monica:'와 같이 표기된 것을 알 수 있다.
즉 '대문자로 시작+소문자 반복+콜론(:)' 이다.
char 객체를 생성해서 위의 패턴을 찾는 검색 조건을 저장하자.
char = re.compile(r'[A-Z][a-z]+:') #[A_Z}는 대문자 전체,[a-z]는 소문자 전체를 의미
fincdall을 사용해 패턴 문자열을 모두 모아보자.
re.findall(char, script101)
그 뒤 출력하면
['Note:', 'Scene:', 'Monica:', 'Joey:', 'Chandler:', 'Phoebe:', 'Phoebe:', 'Monica:', 'Chandler:', 'Chandler:', 'All:', 'Chandler:', 'Joey:', 'Chandler:', 'Joey:', 'Phoebe:', 'Chandler:', 'Monica:', 'Chandler:', 'Ross:', 'Joey:', 'Monica:', 'Ross:', 'Chandler:', 'Monica:', 'Joey:', 'Monica:', 'Ross:', 'Phoebe:', 'Ross:', 'Phoebe:', 'Ross:', 'Monica:', 'Ross:', 'Joey:', 'Ross:', 'Chandler:', 'Ross:', 'Monica:', 'Ross:', 'Joey:', 'Joey:', 'Ross:', 'Chandler:', 'Monica:', 'Rachel:', 'Waitress:', 'Monica:', 'Rachel:', 'Ross:', 'Monica:', 'Rachel:', 'Monica:', 'Rachel:', 'Scene:', 'Monica:', 'Chandler:', 'Ross:', 'Rachel:', 'Phoebe:', 'Chandler:', 'Joey:', 'Joey:', 'Rache .....]
와 같이 저장된다.
이는 많이 인물의 이름이 중복되어 있으므로, 중복된 값을 제거해야한다.
중복된 값을 제거하는 방법에는 여러가지가 있지만, 여기서는 리스트를 '집합(set)'으로 만드는 방법을 사용할 것이다.
리스트는 원소의 중복이 허용되지만, 집합은 원소의 중복이 허용되지 않기 때문에 중복을 없앨 때 유용하게 쓸 수 있다.
set()키워드를 이용하여 리스트를 집합으로 바꿔보자.
set(re.findall(char, script101))
{'Note:', 'Chandler:', 'Monica:', 'Rachel:', 'Paul:', 'Scene:', 'Waitress:', 'Ross:', 'Frannie:', 'Phoebe:', 'All:', 'Joey:', 'Customer:'}
그러면 위와 같이 집합으로 바뀌면서 중복이 사라진 채로 출력된다.
아직 해결해야할 2개의 문제가 더 남아있다.
1. Note와 All처럼 형식에는 맞지만 등장인물이 아니면 이것을 예외 조항으로 분리해야한다.
2. 등장인물 뒤에 바로 이어 나오는 콜론(:)을 지워야한다.
2번째 문제를 해결해보자. (콜론을 지워보자)
sub()를 이용해서 문자열을 추출할 수 도 있지만, 더 간단한 방법으로는 슬라이싱 기능이 있다.
콜론은 모든 원소의 맨 마지막에 있기 때문에, 문자열 슬라이싱 기능을 사용하면 원하는 위치의 문자를 제외하고 값을 출력할 수 있다.
문자열 슬라이싱을 사용하여 앞에서 만든 리스트 요소에서 맨 뒤의 콜론(:)을 모두 지워보자.
y = set(re.findall(char, script101)) #등장인물 리스트를 집합으로 바꿔 y객체에 저장
z = list(y) #for문에 사용하기 위해 다시 리스트로 변경
character = [] #등장인물 리스트를 새로 담을 객체 character를 생성
for i in z:
character += [i[:-1]] #맨 뒤 콜론을 지우고 character에 저장
print(character)
위와 같이 작성 후 코드를 실행하면,
다음과 같이 잘 출력되는 것을 확인할 수 있다.
['Phoebe', 'Ross', 'Waitress', 'Scene', 'All', 'Joey', 'Rachel', 'Monica', 'Customer', 'Frannie', 'Chandler', 'Paul', 'Note']
위의 코드를 단 한 줄로 간단하게 표현할 수도 있다.
character = [x[:-1] for x in list(set(re.findall(r'[A-Z][a-z]+:', script101)))]
이렇게 작성해도 결과값은 동일하다.
3. 지문만 출력하기
대본에는 대사와 지문이 모두 있다. 대본을 보면 지문을 괄호 한에 들어가 있다는 것을 알 수 잇다.
이번에는 대본에서 지문에 해당하는 내용만 추출해보자.
지문의 규칙을 찾는 것이 중요하다.
지문은 소문자 바로 다음에 닫는 괄호가 나오고, 여는 괄호 바로 다음에 공백 없이 문자가 시작되고, 문자 또는 공백으로 진행되며, 마침요 바로 다음에 닫는 괄호가 나옴을 알 수 있다.
이름 정규 표현식으로 표현하면 다음과 같다.
r'\([A-Za-z].+[a-z|\.]\)'
이제 이 규칙을 이용해 script101객체에서 지문을 추출해 보자. 이때 잘 출력되는지 확인하기 위해 6개 원소만 출력해보자.
print(re.findall(r'\([A-Za-z].+[a-z|\.]\)', script101)[:6])
그러면
['(The Pilot-The Uncut Version)', '(Note: The previously unseen parts of this episode are shown in blue text.)', '(They all stare, bemused.)', '(mortified)', '(explaining to the others)', '(to Ross)']
위와 같이 잘 출력된다.
앞서 지금까지 했던 모든 것들의 코드를 한번에 표현하면 아래와 같다.
import os, re
os.chdir(r'C:\Users\82105\Desktop') #friends.txt파일을 저장한 폴더로 이동
f = open('friends101.txt', 'r', encoding = 'utf8')
script101 = f.read()
Line = re.findall(r'Monica:.+', script101)
#for item in Line[:3]:
#print(item) #Line리스트 요소 중 앞에서 3개 까지 출력
f.close()
f = open('monica.txt', 'w' , encoding='utf8')
monica = ''
for i in Line:
monica += i + '\n' #Line리스트의 원소를 monica 문자열에 추가
f.write(monica)
f.close()
char = re.compile(r'[A-Z][a-z]+:') #[A_Z}는 대문자 전체,[a-z]는 소문자 전체를 의미
y = set(re.findall(char, script101)) #등장인물 리스트를 집합으로 바꿔 y객체에 저장
z = list(y) #for문에 사용하기 위해 다시 리스트로 변경
character = [] #등장인물 리스트를 새로 담을 객체 character를 생성
for i in z:
character += [i[:-1]] #맨 뒤 콜론을 지우고 character에 저장
print(character)
#character = [x[:-1] for x in list(set(re.findall(r'[A-Z][a-z]+:', script101)))]
print(re.findall(r'\([A-Za-z].+[a-z|\.]\)', script101)[:6])
4. 특정 단어의 예문만 모아 파일로 저장하기
이번에는 특정 단어를 사용한 대사만 추출해보자.
초반은 앞서 했던 내용과 중복되므로, 쉽게 이해하고 넘어갈 수 있을 것이다.
import os, re
os.chdir(r'C:\Users\82105\Desktop') # friends.txt파일을 저장한 폴더로 이동
f = open('friends101.txt', 'r', encoding='utf8')
f.read(100) # 100번째 글자까지 읽어봄
f.seek(0) # 읽은 다음에는 커서를 맨 앞으로 이동
sentences = f.readlines() # 객체 f안의 모든 문장을 원소로 하는 리스트를 만든다.
print(sentences[:3])
for i in sentences[:20]: # 먼저 문장 20개만 가져와 실험
if re.match(r'[A-Z][[a-z]+:', i): # match 문으로 문장 맨 앞에서 패턴을 찾는다.
print(i)
그리고 출력하면, 다음과 같이 잘 출력된다.
Joey: C'mon, you're going out with the guy! There's gotta be something wrong with him!
Chandler: All right Joey, be nice. So does he have a hump? A hump and a hairpiece?
Phoebe: Wait, does he eat chalk?
Phoebe: Just, 'cause, I don't want her to go through what I went through with Carl- oh!
여기까지 이해가 됐다면 would가 들어간 문장만 추출하는 것은 어렵지 않다.
would가 있으면 저장하라는 조먼만 if 문에 추가하면 된다.
would = [i for i in sentences if re.match(r'[A-Z][a-z]+:', i) and re.search('would', i)]
print(would)
would 객체에 would가 들어간 문장만 저장하고 추출하면 다음과 같다.
["Rachel: Ooh, I was kinda hoping that wouldn't be an issue... [Scene: Monica's Apartment, everyone is there and watching a Spanish Soap on TV and are trying to figure out what is going on.]\n", "Chandler: I would have to say that is an 'L'-shaped bracket.\n", 'Monica: Why?! Why? Why, why would anybody do something like that?\n', 'Rachel: You would be too if you found John and David boots on sale, fifty percent off!\n', 'Ross: Oh. Listen, do you think- and try not to let my intense vulnerability become any kind of a factor here- but do you think it would be okay if I asked you out? Sometime? Maybe?\n', "Joey: Oh, you wouldn't know a great butt if it came up and bit ya.\n"]
좀 더 깔끔하게 출력하면 다음과 같다.
for i in would:
print(i)
Rachel: Ooh, I was kinda hoping that wouldn't be an issue... [Scene: Monica's Apartment, everyone is there and watching a Spanish Soap on TV and are trying to figure out what is going on.]
Chandler: I would have to say that is an 'L'-shaped bracket.
Monica: Why?! Why? Why, why would anybody do something like that?
Rachel: You would be too if you found John and David boots on sale, fifty percent off!
Ross: Oh. Listen, do you think- and try not to let my intense vulnerability become any kind of a factor here- but do you think it would be okay if I asked you out? Sometime? Maybe?
Joey: Oh, you wouldn't know a great butt if it came up and bit ya.
자, 이제 추출한 문장을 파일로 만들어 저장하자.
'would.txt' 라는 이름의 새로운 파일을 쓰기 모드로 열고, would 리스트에 들어있는 원소를 넣은 뒤, 파일을 닫으며 저장하자!
newf = open('would.txt','w') #파일을 쓰기 모드로 생성
newf.writelines(would) #would리스트의 원소를 꺼내 한 줄씩 쓴다
newf.close()
위와 같이 실행하고 나면 컴퓨터에서 would.txt 파일을 확인할 수 있다.
출처: