]
}}}
== # 기사 첫 절의 내용 ==
캘린더의 기사는 항상 "저자" 소개가 먼저 나오고, 그 다음 "시작하며" 절에서 해당 기사를 작성하게 된 동기라거나 소개할 기술에 대해 소개를 합니다. 2011년의 경우는 항상 "시작하며"라는 제목으로 되어 있고, 2010년의 경우는 기사들마다 좀 다릅니다. 가장 이상적인 거야 전체 기사의 요약문을 기사 작성자가 처음에 제공을 하거나 편집자가 따로 작성을 해서 제공하는 것이겠지만, 이는 쉽지 않은 일이고, 그게 없으니 위에 말한 "시작하며" 절의 내용을 일종의 소개글로 사용하도록 합시다.
그러면 우리가 뽑아내야 할 것은, 전체 html 파일의 내용 중에,
{{{#!vim html
저자
저자소개 내용...
시작하며
시작하며 절의 내용...
그 다음 절
그 다음 절의 내용
}}}
"저자소개 내용"을 뽑아낼 때와 거의 비슷하게 할 수 있을 것 같습니다. 그런데 그때와 크게 다른 점이 있는데, 저자소개 내용을 뽑아낼 때에는 h2 태그 안에 "저자"라고 적힌 부분을 찾으면 되는데, 지금 같은 경우는 항상 "시작하며"라고 적혀 있다는 보장이 없다는 것입니다. (대부분은 "시작하며"이지만)
그러면 앞에서부터 h2 태그를 만날 때마다 숫자를 세는 방법도 있고, 전체를 정규표현식으로 적어줄 수도 있겠습니다만... 사실 우리는 두번째 절의 제목이 무엇인지 정확히 알고 있습니다. 왜냐하면 목차를 추출할 때 각 h2 태그의 텍스트를 뽑아서 @toc
배열에 저장해 두었기 때문입니다. 이걸 사용하면 되겠군요!
그럼 일단, 저자 정보를 빼낼 때와 유사하게, 일단 저 영역의 html만 남기고 앞뒤를 제거해보도록 합시다.
{{{#!vim perl
# 정보6. "저자" 절 바로 다음 절의 내용
my $abstract = "";
my $abs_html = $wq->html();
$abs_html =~ s{
^ # 스트링의 제일 처음
.* # 임의의 문자열
]*> # 태그
\Q$toc[1]\E # 두번째 절의 제목 - @toc 의 두번째 원소
#
태그 닫고
(.+?) # 이 부분이 우리가 추출할 부분
]*> # 세번째 절이 시작되는 태그
.* # 임의의 문자열
$ # 스트링의 끝
}
{$1}isx;
}}}
저자 정보가 있는 영역을 추출할 때와 동일합니다, 다만 저번에는 그냥 "저자"라는 텍스트를 매치했는데, 이번에는 $toc[1]에 들어있는 문자열과 매치를 시키고 있습니다.
이 때 $toc[1]의 내용 중에 정규표현식에서 사용되는 특수 문자가 들어있을 수가 있습니다. 예를 들어 두번째 절의 제목이 "어떻게?
"라고 하면, 여기 들어있는 물음표 ?
가 정규식 내에서 해석되면서 이것은 "어떻
" 또는 "어떻게
" 두 가지 문자열에 매치되는 정규식이 되어 버립니다. 이러면 제대로 매치가 되지 않고 치환에 실패합니다.
따라서, $toc[1] 안에 들어있는 문자열은 무조건 "문자 그대로" 사용하도록 해야 합니다. 그러기 위해서 앞에 \Q
지시자를 붙여서 escape시켜주고, $toc[1] 뒤에는 \E
를 붙여서 그 이후부터는 다시 평범한 정규식으로 처리하도록 합니다.
이 시점에서 $abs_html 에는 해당 영역의 html만 남게 되었습니다. 이후 처리를 위해서, $abs_html 의 내용을 출력시켜보면 다음과 같습니다.
{{{#!vim html
오래 전 있었던 일입니다. 우연히 다음 그림과 같은 사진 파일을 보게 되었습니다.

그림 1. 여러 사진이 포함된 하나의 이미지
하나의 JPEG 사진 파일 안에 사진
세 장이 합쳐져 있는데 이게 모니터에 다 들어오지 않아 보기에 불편하더랍니다. 그래서 저
안에 있는 사진 세 장을 각각 별도의 파일로 저장하고 싶어졌습니다.
위와 같이 사진이 세
장 뿐이면 그림판에서 영역 지정해 저장하면 됩니다. 하지만 파일 하나에 사진이 수십 장
들어있다면, 그리고 그런 파일이 또 수십 개나 있다면 정말 골치아픈 일이 될 것입니다.
이것을 자동으로 할 수 없을까 궁리해 보았습니다.
}}}
이제 여기서 다시 html 태그를 떼어내고 텍스트만 남기도록 합시다.
html 태그를 떼어내는 데에도 Web::Query 모듈을 사용하도록 하겠습니다. 정규표현식 치환을 쓸 수도 있지만, 이쪽이 더 수월합니다.
'''1차 시도:'''
{{{#!vim perl
$abstract = wq($abs_html)->text();
}}}
$abs_html 의 내용만 가지고 새로운 Web::Query 오브젝트를 만든 후에, 그 오브젝트에 text() 메쏘드를 호출합니다.
그러나 이것은 실패합니다. $abs_html 안에 들어있는 내용이 제대로 구성된 html이 아니라 일부만 떼어놓은 것이라서 그런지, "Unknown source type:" 이라는 에러가 나며 스크립트가 죽습니다.
'''2차 시도:'''
{{{#!vim perl
$abstract = wq("$abs_html")->text();
}}}
약간 꼼수를 부려보았습니다. $abs_html 내용의 앞뒤에 html, /html 태그를 붙여서, 제대로 된 html 문서 형식인 것처럼 꾸며보았습니다. 이랬더니 제대로 실행은 되었습니다만, 막상 출력 결과를 보니 다음과 같이:
{{{#!vim
오래 전 있었던 일입니다. 우연히 다음 그림과 같은 사진 파일을 보게 되었습니다.그림 1. 여
러 사진이 포함된 하나의 이미지하나의 JPEG 사진 파일 안에 사진 세 장이 합쳐져 있는데 이
게 모니터에 다 들어오지 않아 보기에 불편하더랍니다. 그래서 저 안에 있는 사진 세 장을 각각
별도의 파일로 저장하고 싶어졌습니다.위와 같이 사진이 세 장 뿐이면 그림판에서 영역 지정해
저장하면 됩니다. 하지만 파일 하나에 사진이 수십 장 들어있다면, 그리고 그런 파일이 또 수십
개나 있다면 정말 골치아픈 일이 될 것입니다. 이것을 자동으로 할 수 없을까 궁리해 보았습니다.
}}}
전체가 하나의 단락이 되어 버렸습니다. 그래서 보기에 불편합니다.
$abs_html 안에는 여러 단락이
태그로 구분되어서 나옵니다. 그러면
태그마다 텍스트를 따로따로 추출해봅시다.
'''3차 시도:'''
{{{#!vim perl
my @abstract = wq("$abs_html")
->find('p')
->text();
$abstract = join "\n\n", @abstract
}}}
@abtract 라는 배열을 추가로 선언하고, wq()로 생성한 오브젝트에 find('p')를 호출하여 p태그들을 찾고, 각각에 대해 text()를 얻어오게 하고 있습니다.
(이 때 text()를 호출하는 과정이 리스트 문맥에서 이뤄져야 각각의 텍스트가 전부 담깁니다. 따라서 배열에 할당을 하고 있습니다. 만일 스칼라 변수에 할당을 하면, 즉 스칼라 문맥에서 호출하면 첫번째
에 해당하는 텍스트만 추출됩니다)
그 후 @abstract 에 들어 있는 각 원소들을 하나로 합치되, 원소 사이에 줄바꿈 문자 두 개를 삽입하도록 하고 있습니다.
실행 결과는:
{{{#!vim perl
오래 전 있었던 일입니다. 우연히 다음 그림과 같은 사진 파일을 보게 되었습니다.
그림 1. 여러 사진이 포함된 하나의 이미지
하나의 JPEG 사진 파일 안에 사진 세 장이 합쳐져 있는데 이게 모니터에 다 들어오지 않아 보기에 불편하더랍니다. 그래서 저 안에 있는 사진 세 장을 각각 별도의 파일로 저장하고 싶어졌습니다.
위와 같이 사진이 세 장 뿐이면 그림판에서 영역 지정해 저장하면 됩니다. 하지만 파일 하나에 사진이 수십 장 들어있다면, 그리고 그런 파일이 또 수십 개나 있다면 정말 골치아픈 일이 될 것입니다. 이것을 자동으로 할 수 없을까 궁리해 보았습니다.
}}}
만세! 우리가 원하는 대로 단락이 이쁘게 구분되었습니다.
여기까지 작성된 전체 코드입니다:
{{{#!vim perl number
#!perl
# t08.pl
# - URL에 해당하는 글 링크와 제목 표시
# - 가져올 기사의 날짜 부분을 명령행 인자로 받음
# - 저자 표시
# - 목차 표시
# - 언급된 모듈 표시
# - 두번째 절의 내용 표시
use 5.010;
use strict;
use warnings;
use utf8;
use Web::Query; # 웹 긁어오는 라이브러리
use List::MoreUtils qw/uniq/;
binmode STDOUT, ":encoding(cp949)";
# 정보1. URL
my $day = $ARGV[0] // 15;
my $url = sprintf "http://advent.perl.kr/2011/2011-12-%02d.html", $day;
my $wq;
eval {
$wq = wq($url);
};
die "$@" if $@;
# 정보2. 제목
my $title = $wq->find('h1')->text();
# 정보3. 저자
my $author;
my $author_html = $wq->html();
$author_html =~ s{
^ # 스트링의 제일 처음
.* # 임의의 문자열
]*> # 태그. 등과 같은 형태일 수도 있음
저자 # '저자'
#
태그 닫고
(.+?) # 저자 정보가 들어 있는 텍스트를 괄호로 묶음
]*> # 그 다음 절의 제목이 들어가는 태그
.* # 나머지 내용이 담긴 임의의 문자열
$ # 스트링의 끝
}
{$1}isx;
if ( $author_html =~ m'
@ # "@"로 시작
(.+?) # 이 부분이 아이디
(\s|