[첫화면으로]Perl/펄크리스마스달력요약집만들기

마지막으로 [b]

([네이버 펄스터디 까페]에 올린 "펄 크리스마스 달력 요약집 만들기" 강좌글 합본)

1. 개요
2. 주소와 제목
3. 명령행 인자 처리
4. 저자 정보 추출
5. 목차 추출
6. 모듈 목록 추출
7. 기사 첫 절의 내용
8. 마무리
9. 기타 & Comments

1. 개요

지난 번 "가계도 분석 프로그램"처럼, 적당한 분량에 적당한 내용이 담긴 프로그램을 만들게 되어서 소개해볼까 합니다.

다루는 내용은

정도가 되겠습니다.

상황은 다음과 같습니다.

까페에도 소개가 되었었지만 ( http://cafe.naver.com/perlstudy/937 ) "펄 크리스마스 달력" 사이트가 있습니다. 12월 1일부터 24일까지, 하루에 하나씩 펄 유저들이 유용한 팁,재미있는 프로그램 등을 소개하는 글을 써서 올립니다.

올해 한국판 달력의 대문은 http://advent.perl.kr/2011/ 각 기사는 대문에 있는 방울을 클릭하면 볼 수 있습니다. 예를 들어 제가 올렸던 기사는 http://advent.perl.kr/2011/2011-12-15.html 여기에 있습니다.

문제는 저 대문을 보면서 "어느 날짜의 글이 무슨 내용을 담고 있었더라..."는 걸 나중에 다시 찾아보기가 좀 불편하다는 거죠. 그래서 제 홈페이지에다가 각 날짜별 기사의 링크와 제목만 옮겨서 죽 나열하여 적어뒀습니다. 이건 그냥, 대문 html 소스에서 텍스트 에디터로 목록 부문만 적당히 치환해서 금방 되었습니다.

그런데 이러고나니까 또 좀 아쉬워져서, 각 기사 내용의 각 절의 제목만 뽑아서 목차로 만들어 보이고 싶어졌습니다. 물론 십수개의 기사를 일일이 웹브라우저로 보면서 마우스로 긁어 붙여도 할 수 있을 일입니다. (솔직히 말해서, 여기까지만 할 거면 그냥 마우스로 긁어 붙이는 게 프로그램 만드는 것보다 실제로 더 빠릅니다 -_-;)

목차를 만들고 나니까 그 다음은 각 기사에서 소개된 모듈들의 목록을 뽑아서 보여주고 싶어졌습니다.

그러고 나니까 이왕이면 논문의 abstract 처럼, 각 기사의 요약문까지 볼 수 있으면 좋겠다 싶어졌습니다.

그래서 최종적으로는,

 http://gypark.pe.kr/wiki/Perl/AdventCalendar
이렇게 제 홈페이지에 올라가 있습니다.

좀 더 솔직히 말하면... 사실 올해 달력 기사가 당시 1일부터 17일까지 열 일곱 개 있었기 때문에... 위에 적은 일을 전부 다 해도 사실 그냥 눈으로 확인하고 마우스로 긁어 복사하고 붙여넣는 게 결과적으로 더 빨랐을 것 같긴 합니다만 -_-;;;

이런 단순 반복 작업을 하는 건 맘에 안 듭니다. 그래서 프로그램을 만들어 보기로 하였습니다.

문제를 간단히 요약하면 다음과 같습니다.

이런 웹페이지가 있다: http://advent.perl.kr/2011/2011-12-15.html

위 페이지를 읽어서, 아래와 같은 형태의 텍스트로 출력해라:


* 링크: [http://advent.perl.kr/2011/2011-12-15.html 열다섯번째 날: 한 이미지 안에 들어있는 사진들 추출하기 - Prima 모듈]
* 저자: gypark
* 목차: <code>저자</code> - <code>시작하며</code> - <code>아이디어</code> - <code>Prima 모듈 설치</code> - <code>Prima 코드 예제</code>
- <code>완성</code> - <code>실행 결과</code> - <code>정리하며</code> - <code>참고 문서</code>
* 언급된 모듈: <index(Prima)> , <index(Prima::Image)>
* '시작하며' 절의 내용:
<blockquote>
오래 전 있었던 일입니다. 우연히 다음 그림과 같은 사진 파일을 보게 되었습니다.

 그림 1. 여러 사진이 포함된 하나의 이미지

하나의 JPEG 사진 파일 안에 사진 세 장이 합쳐져 있는데 이게 모니터에 다 들어오지 않아 보기에 불편하더랍니다. 그래서 저 안에 있는 사진
세 장을 각각 별도의 파일로 저장하고 싶어졌습니다.

위와 같이 사진이 세 장 뿐이면 그림판에서 영역 지정해 저장하면 됩니다. 하지만 파일 하나에 사진이 수십 장 들어있다면, 그리고 그런 파일이
또 수십 개나 있다면 정말 골치아픈 일이 될 것입니다. 이것을 자동으로 할 수 없을까 궁리해 보았습니다.
</blockquote>

(이 텍스트를 제 홈페이지 위키에 올리면 위에도 링크했듯이 http://gypark.pe.kr/wiki/Perl/AdventCalendar 여기서 보이는 형태로 보이게 됩니다)

정말 주먹구구 식으로 만든 거라서... 실제 강좌도 그 주먹구구식으로 만들어지는 과정을 (최대한 기억을 더듬어서) 따라가 보도록 하겠습니다. 이런 과정은 제대로 된 프로그램을 만들기에 적절하진 않겠지만, Perl로 간단한 프로그램을 작성하는 데에는 쓸만할 것이라 생각합니다.

2. 주소와 제목

첫 작업으로는, URL을 주면 그 URL에 해당하는 기사 페이지에 들어가서 그 기사의 제목을 얻어와 봅시다.
http://advent.perl.kr/2011/2011-12-15.html"
위 주소를 주면 열다섯번째 날: 한 이미지 안에 들어있는 사진들 추출하기 - Prima 모듈 이 부분을 추출해내야 합니다.

(사실 제목과 링크만 추출하는 걸로 끝난다면, 대문 페이지의 html 소스에 각 날짜의 기사 링크와 제목이 있으니까 거길 가져오면 더 쉽습니다만, 우리는 그 외 다른 정보도 추출할 예정이니까 개별 기사 페이지를 대상으로 작업을 합시다)

참으로 다행스럽게도, 펄 크리스마스 달력을 편집하는 am0c님께서 제목을 <h1>...</h1>태그 안에 담아 두었고, 더 다행스러운 건 전체 html 소스 내에 h1 태그는 오직 제목에만 한 번 등장한다는 겁니다. 따라서 /정규표현식으로도 아주 쉽게 할 수 있습니다.

if ( $html =~ m|<h1>(.+)</h1>| ) {
    $title = $1;
}

하지만 정규표현식은, 웹 페이지의 소스가 조금이라도 달라지면 제대로 동작하지 않을 위험이 있습니다. 예를 들어 위의 코드처럼 짜 놨는데 어느날 제목이 <h1 class="title">...</h1>와 같이 class 속성이 붙어버리면 저 정규식은 동작하지 않습니다. 그때마다 수정하는 것도 일입니다. 뒤에 다른 내용을 추출하기 위해서라도 정규식 말고 좀 더 세련된 방법을 써 보도록 합시다.

Cpan:Web::Query 모듈을 사용하면, html 파일에 들어있는 각종 엘리멘트들을 분석한 후 원하는 조건의 원소를 찾아내어 가공할 수 있습니다.

실제 코드는 다음과 같습니다.

 1  #!perl
 2  # t01.pl - URL에 해당하는 글 링크와 제목만 표시
 3  use strict;
 4  use warnings;
 5
 6  use Web::Query;     # 웹 긁어오는 라이브러리
 7
 8  binmode STDOUT, ":encoding(cp949)";
 9
10  # 정보1. URL
11  my $url = 'http://advent.perl.kr/2011/2011-12-15.html';
12
13  my $wq;
14  eval {
15      $wq = wq($url);
16  };
17  die "$@" if $@;
18
19  # 정보2. 제목
20  my $title = $wq->find('h1')->text();
21
22  # 출력
23  print "[$url]\n";
24  print "[$title]\n";

문제는 $url에 해당하는 페이지가 없어서 404에러가 나는 등 에러 코드를 받을 경우는 wq()가 그 시점에서 죽어버리면서 프로그램이 종료된다는 것입니다. 그래서 이런 경우는 그냥 지나치도록 하기 위해서, 해당 코드를 eval { } 블록으로 둘러쌓습니다. 이 때 $wqmy로 선언하는 건 블록 바깥쪽에 두어야 나머지 코드에서도 계속 $wq를 접근할 수 있습니다.

일단 eval로 둘러쌓인 코드는, 내부에서 die하더라도 블록 바깥쪽으로 계속 실행이 이어집니다.

만일 eval 블록 내에서 die나 warn 등이 발생한다면, 그 내용은 특수변수 $@에 담깁니다. 따라서 17번 라인에서 그 변수의 값을 체크하여서 빈 스트링이 아닐 경우는 경고를 내어주고 프로그램을 종료하고 있습니다. (어차피 종료할 건데 왜 eval 블록을 썼느냐 하면, 이 다음부터는 종료하지 않고 스킵할 예정이기 때문입니다)

이렇게 find()의 인자로 들어가는 것은 CSS3 Selector 를 지원한다고 합니다. 저도 이건 잘 모르지만, http://www.w3.org/TR/selectors/#selectors 를 보시면 될 것 같습니다.

우리는 h1 태그를 찾을 거니까,
$wq->find('h1')
으로 충분합니다. 그러면 이 find는 h1 엘리멘트를 찾아 오브젝트 형태로 반환합니다.

다시 <h1></h1>사이에 있는 텍스트를 찾아야 합니다. 이것은 방금 찾은 h1 엘리멘트 오브젝트에다가 text()메쏘드를 호출하면 됩니다.

my $tag = $wq->find('h1');
my $title = $tag->text();
한번에
my $title = $wq->find('h1')->text();

실행결과:

D:\Work\Perl\advent_abstract>t01.pl
[http://advent.perl.kr/2011/2011-12-15.html]
[열다섯번째 날: 한 이미지 안에 들어있는 사진들 추출하기 - Prima 모듈]

잘 나오는 걸 확인할 수 있습니다. 다른 날짜의 기사에 대해 테스트해도 잘 동작합니다.

3. 명령행 인자 처리

이제 다른 날짜의 기사를 가지고 테스트 하려고 하니까, 매번 $url 변수에 들어 있는 주소를 바꿔줘야 합니다.

my $url = 'http://advent.perl.kr/2011/2011-12-15.html';

게다가 최종적으로는 한 달치 기사 전부를 가져오려고 하고 있기 때문에, 저 $url에 주소를 저렇게 직접 넣으면 안 되겠습니다.

따라서, 년-월-일 중에 일에 해당하는 부분을 실행할 때 옵션으로 줄 수 있게 하면 편할 것 같습니다.

# 정보1. URL
my $day = $ARGV[0];
my $url = "http://advent.perl.kr/2011/2011-12-$day.html";  # 위에서는 홑따옴표였는데 여기서는 쌍따옴표야 함에 유의

@ARGV배열은, Perl 을 실행할 때 스크립트 이름 뒤에 따라오는 명령행 인자(Command Line Argument)들이 자동으로 담기는 배열입니다.

간단한 테스트 코드를 보면:

#!perl
foreach my $arg ( @ARGV ) {
    print "[$arg]\n";
}

실행:

d:\Work\Perl\advent_abstract>perl test.pl a b c
[a]
[b]
[c]

d:\Work\Perl\advent_abstract>perl test.pl hello perl
[hello]
[perl]

d:\Work\Perl\advent_abstract>perl test.pl "this is" a "sample program"
[this is]
[a]
[sample program]

기본적으로 공백을 기준으로 원소를 나누어서 $ARGV[0], $ARGV[1], ... 이렇게 들어가고, 공백이 포함된 스트링을 하나의 원소로 처리하게 하고 싶으면 따옴표로 둘러싸 주면 됩니다.

다시 원래 코드로 돌아와서, 이제 날짜 부분을 명령행 인자로 받아서 넣게 고쳤으니, 제대로 동작하는지 테스트해봅니다.

d:\Work\Perl\advent_abstract>t02.pl 15
[http://advent.perl.kr/2011/2011-12-15.html]
[열다섯번째 날: 한 이미지 안에 들어있는 사진들 추출하기 - Prima 모듈]

d:\Work\Perl\advent_abstract>t02.pl 14
[http://advent.perl.kr/2011/2011-12-14.html]
[열네번째 날: 크리스마스 애인 만들기]

d:\Work\Perl\advent_abstract>t02.pl 16
[http://advent.perl.kr/2011/2011-12-16.html]
[열여섯번째 날: perlbrew, local::lib, smartcd 를 이용하여 Perl 환경 구축하기]

d:\Work\Perl\advent_abstract>t02.pl 1
404 Not Found at D:/strawberry/perl/site/lib/Web/Query.pm line 50.

d:\Work\Perl\advent_abstract>t02.pl 01
[http://advent.perl.kr/2011/2011-12-01.html]
[첫째 날: 네모 반듯한 표 그리고 한글]

15일, 14일, 16일 다 잘 가져오는 것을 확인했습니다. 그런데 1일자 기사를 가져오려고 하니까 문제가 생겼습니다. 그런 페이지가 없다고 404에러가 나는군요!

펄 캘린더 사이트에는 1일부터 9일까지의 기사는 주소가 01부터 09까지, 앞에 0이 하나 붙은 상태로 되어 있습니다. 따라서 인자를 1로 주면 2011-12-1.html 을 가져오려고 해서 404에러가 납니다. 실행할 때마다 01, 02, 이런 식으로 인자를 주면 되겠지만, 귀찮습니다. 1이라고만 주어도 알아서 0을 붙이도록 합시다.

my $day = $ARGV[0];
my $url = sprintf "http://advent.perl.kr/2011/2011-12-%02d.html", $day;

C에서의 printf, sprintf 와 똑같습니다. 포맷스트링과 추가 인자를 받아서, 포맷스트링에 값을 넣은 결과를 스트링으로 반환합니다. (자세한 것은 perldoc -f sprintf)

$day의 값이 포맷스트링의 %02d 자리에 들어가게 되고, "2자리의 정수, 만일 자리가 남을 경우 앞에 0을 채움"으로 지정했기 때문에 1부터 9는 각각 01부터 09로 변환됩니다.

실행결과는 생략.

계속 진행을 하려다보니, 앞으로도 한동안은 15일자에 있는 제가 쓴 기사를 가지고 테스트를 할 텐데, 매번 실행할 때마다 15라고 인자를 덧붙여주자니 이게 또 귀찮습니다. 따라서,

수정해 봅시다.

# 정보1. URL
my $day;
if ( defined $ARGV[0] ) {
    $day = $ARGV[0];
}
else {
    $day = 15;
}
my $url = sprintf "http://advent.perl.kr/2011/2011-12-%02d.html", $day;

$ARGV[0]의 값이 정의되어 있으면 그 값을 쓰고, 그렇지 않으면 15를 $day에 넣습니다.

if 문을 쓰니 너무 번잡해 보입니다. ? : 연산자를 씁시다.

# 정보1. URL
my $day = defined $ARGV[0] ? $ARGV[0] : 15;
my $url = sprintf "http://advent.perl.kr/2011/2011-12-%02d.html", $day;

Perl 5.10 부터는 defined-or 연산자를 쓸 수 있습니다. ( http://cafe.naver.com/perlstudy/474 참고하세요)

# 정보1. URL
use 5.010;
my $day = $ARGV[0] // 15;
my $url = sprintf "http://advent.perl.kr/2011/2011-12-%02d.html", $day;

여기까지 완성된 코드 전체는 다음과 같습니다.

 1  #!perl
 2  # t04.pl
 3  # URL에 해당하는 글 링크와 제목만 표시
 4  # 가져올 기사의 날짜 부분을 명령행 인자로 받음
 5  use 5.010;
 6  use strict;
 7  use warnings;
 8
 9  use Web::Query;     # 웹 긁어오는 라이브러리
10
11  binmode STDOUT, ":encoding(cp949)";
12
13  # 정보1. URL
14  my $day = $ARGV[0] // 15;
15  my $url = sprintf "http://advent.perl.kr/2011/2011-12-%02d.html", $day;
16
17  my $wq;
18  eval {
19      $wq = wq($url);
20  };
21  die "$@" if $@;
22
23  # 정보2. 제목
24  my $title = $wq->find('h1')->text();
25
26  # 출력
27  print "[$url]\n";
28  print "[$title]\n";

실행결과는 다음과 같습니다.

d:\Work\Perl\advent_abstract>t04.pl
[http://advent.perl.kr/2011/2011-12-15.html]
[열다섯번째 날: 한 이미지 안에 들어있는 사진들 추출하기 - Prima 모듈]

d:\Work\Perl\advent_abstract>t04.pl 1
[http://advent.perl.kr/2011/2011-12-01.html]
[첫째 날: 네모 반듯한 표 그리고 한글]

d:\Work\Perl\advent_abstract>t04.pl 7
[http://advent.perl.kr/2011/2011-12-07.html]
[일곱째 날: 윈도우 환경에서 화면 캡쳐 후 자동 저장 기능의 구현]

d:\Work\Perl\advent_abstract>t04.pl 11
404 Not Found at D:/strawberry/perl/site/lib/Web/Query.pm line 50.

11일의 경우는 아예 기사가 없기 때문에 어쩔 수 없고, 인자를 주지 않으면 자동으로 15일자 기사를 가져오고, 그 외의 경우는 인자로 받은 날짜의 기사를 가져오는 것을 확인할 수 있습니다.

4. 저자 정보 추출

여전히 우리는 각 기사의 URL을 받아서 해당 기사의 제목만 추출하는 상태입니다.

이번 글에서는 저자의 아이디를 추출해 보도록 하겠습니다. 달력 기사 http://advent.perl.kr/2011/2011-12-15.html 의 소스를 보면, 기사마다 내용은 다르지만 다들 다음과 같은 형식으로 작성되어 있습니다.

        <h2>저자</h2>

<p><a href="http://twitter.com/gypark">@gypark</a> -
개인 자료라고 믿기 어려울 정도의 방대한 Perl 자료를
제공하고 있는 <a href="http://gypark.pe.kr/wiki/Perl">
gypark.pe.kr</a>의 주인장, Raymundo라는 닉을 사용하기도 한다.
네이버 Perl 카페 회원</p>

<h2>시작하며</h2>

(저자의 소개글이 너무 낯뜨거운데... 제가 적은 게 아니고 달력 편집자께서 적으신 거니까 그러려니 하고 넘어갑시다)

여러 기사의 소스를 비교해보면 저자 아이디를 어떻게 추출하면 될 지 방법이 떠오릅니다.

이 경우 트위터 아이디가 없는 경우는 @로 시작하지 않을테니 문제가 될 것인데... 다행히 지금까지 올라왔던 기사들은 전부 트위터 아이디가 적혀 있었습니다. 그리고 2010년 기사의 경우는 링크가 걸리지 않은 경우도 있었으니, 이것까지는 처리할 수 있도록 해 봅시다.

 1  #!perl
 2  # t05.pl
 3  # - URL에 해당하는 글 링크와 제목 표시
 4  # - 가져올 기사의 날짜 부분을 명령행 인자로 받음
 5  # - 저자 표시
 6  use 5.010;
 7  use strict;
 8  use warnings;
 9  use utf8;
10
11  use Web::Query;     # 웹 긁어오는 라이브러리
12
13  binmode STDOUT, ":encoding(cp949)";
14
15  # 정보1. URL
16  my $day = $ARGV[0] // 15;
17  my $url = sprintf "http://advent.perl.kr/2011/2011-12-%02d.html", $day;
18
19  my $wq;
20  eval {
21      $wq = wq($url);
22  };
23  die "$@" if $@;
24
25  # 정보2. 제목
26  my $title = $wq->find('h1')->text();
27
28  # 정보3. 저자
29  my $author;
30  my $author_html = $wq->html();
31  $author_html =~ s{
32                        ^             # 스트링의 제일 처음
33                        .*            # 임의의 문자열
34                        <h2[^>]*>     # <h2> 태그. <h2 id="author">등과 같은 형태일 수도 있음
35                        저자          # '저자'
36                        </h2>         # </h2> 태그 닫고
37                        (.+?)         # 저자 정보가 들어 있는 텍스트를 괄호로 묶음
38                        <h2[^>]*>     # 그 다음 절의 제목이 들어가는 <h2> 태그
39                        .*            # 나머지 내용이 담긴 문자열
40                        $             # 스트링의 끝
41                   }
42                   {$1}isx;
43  if ( $author_html =~ m'
44                          @           # "@"로 시작
45                          (.+?)       # 이 부분이 아이디
46                          (\s|</a>)   # </a>로 끝나는 경우도 있고, 그냥 공백으로 끝나는 경우도 있음
47                        'x ) {
48      $author = $1;
49  }
50  else {
51      warn "can't extract author";
52      $author = "";
53  }
54
55  # 출력
56  print "링크 [$url]\n";
57  print "제목 [$title]\n";
58  print "저자 [$author]\n";

t04.pl 과 비교해서 수정된 부분은

저자 정보를 추출하는 것도 제목을 추출하는 것처럼 Web::Query모듈에서 특정 html원소를 찾아서 처리할 수 있으면 좋겠지만, 이 경우는 저자 정보가 어떤 태그 안에 들어있는게 아니다보니 전체 html을 하나의 스트링으로 저장해서 처리해야 할 것 같습니다.

(주의: 이렇게 얻어낸 html은, 웹페이지의 원본html과 동일하지 않습니다. Web::Query모듈이 자신이 분석한 내용을 바탕으로 다시 재가공을 하기 때문에, 정확히 알려면 저 스트링을 다시 출력해서 눈으로 확인해봐야 합니다.)
정규표현식 치환을 할 때 가장 많이 쓰는 구분자는 슬래쉬(/)이고
s/패턴/치환할문자열/옵션
형태로 쓰지만, 여기서는 중괄호를 구분자로 사용해서
s{패턴}{치환할문자열}옵션
이렇게 썼습니다. 왜냐하면 패턴 내에 슬래쉬가 들어가므로, 슬래쉬 앞에 \를 붙여서 \/처럼 써야 되는데 그러면 눈으로 볼 때 복잡하기 때문입니다.
37번 라인에서 <h2>저자</h2>....<h2> 부분의 "...." 영역을 캡춰합니다. 괄호로 둘러싼 부분이 캡춰가 되어, 괄호가 나타난 순서에 따라 $1, $2, $3,... 순으로 변수에 할당됩니다.

이제 실행결과를 봅시다.

d:\Work\Perl\advent_abstract>t05.pl
링크 [http://advent.perl.kr/2011/2011-12-15.html]
제목 [열다섯번째 날: 한 이미지 안에 들어있는 사진들 추출하기 - Prima 모듈]
저자 [gypark]

d:\Work\Perl\advent_abstract>t05.pl 1
링크 [http://advent.perl.kr/2011/2011-12-01.html]
제목 [첫째 날: 네모 반듯한 표 그리고 한글]
저자 [keedi]

d:\Work\Perl\advent_abstract>t05.pl 20
링크 [http://advent.perl.kr/2011/2011-12-20.html]
제목 [스무번째 날: Mojolicious, HTML5, WebSocket을 이용한 비동기 채팅]
저자 [eeyees]

저자 아이디가 잘 추출되는 것을 확인할 수 있습니다.

5. 목차 추출

이번 글에서는, 기사의 목차 부분만 추출해보겠습니다. 목차라고는 하지만 거창한 게 아니라, 각 절의 제목 부분만 추출하는 겁니다. 그리고 펄 크리스마스 달력 기사들은 전부 각 절의 제목이 <h2> </h2> 태그 안에 들어 있습니다. (이렇게, 웹페이지 자체가 일관성 있는 규칙에 따라 작성되어 있으면 프로그램을 써서 정보를 추출하고 가공하기가 매우 수월해집니다)

코드를 바로 보겠습니다. 앞 글의 t05.pl 에서 추가된 부분만 쓰겠습니다.

# 정보4. 목차
my $toc = "";
my @toc;
$wq->find('h2')
   ->each(
           sub {
                   push @toc, $_->text;
               }
           );
$toc = join " - ", map { "<code>$_</code>" } @toc;

# 출력
print "링크 [$url]\n";
print "제목 [$title]\n";
print "저자 [$author]\n";
print "목차 [$toc]\n";

코드만으로는 감이 잘 안 오니, 간단한 예문을 가지고 실행 순서를 따라가 봅시다.

원래의 html 코드가 다음과 같이 있다고 가정하면:

<h2>첫번째 절</h2>
...
<h2>두번째 절</h2>
...
<h3>세번째 절</h3>
...

$wq->find('h2") 이 구문은 <h2>..</h2> 태그 노드들을 찾아 반환합니다.

이 반환된 노드에 다시 ->each() 메쏘드를 호출하면, 각각의 <h2>노드에 대하여 each()의 인자로 들어간 서브루틴 레퍼런스를 호출합니다.

그러면 제일 처음 찾은 h2태그는 <h2>첫번째 절</h2>입니다. 이 때 다음 서브루틴을 호출하면서,

sub {
         push @toc, $_->text;
}

서브루틴의 인자로는 두 가지 값이 들어갑니다.

따라서 만일 다음과 같은 익명 서브루틴을 each()에 넘겨줬다면:

sub {
      my ( $i, $elem ) = @_;
      print "[$i]\n";
      print $elem->text(), "\n";
}

매번 호출되면서 다음과 같이 출력될 것입니다.

[0]
첫번째 절  <-- 여기까지가 첫번째 호출의 결과
[1]
두번째 절  <-- 두번째 호출
[2]
세번째 절  <-- 세번째 호출

그러나 우리는 카운터는 필요가 없고, 이 익명 서브루틴은 호출될 때 $_ 변수를 $elem의 별칭(alias)으로 설정합니다. 따라서 굳이 $i나 $elem변수를 써서 인자를 따로 받지 않았습니다.

다시 원래 코드로 돌아와서, 여기서는 @toc 변수에 $_->text의 결과를 push하고 있습니다.

따라서 첫번째 호출이 끝나면, @toc에는 첫번째 h2태그 내의 텍스트가 들어갑니다.

@toc = ( "첫번째 절" )

마찬가지로 두번째, 세번째 h2 태그에 대해서도 each()가 호출되고, each()의 인자인 익명 서브루틴이 호출되고, 그 안에서 push를 거쳐서 최종적으로 @toc는 다음과 같이 구성됩니다.

@toc = ( "첫번째 절", "두번째 절", "세번째 절" )

65번 라인에서 @toc의 원소를 다 합쳐서 하나의 문자열로 만듭니다. 복잡해보이지만 오른쪽에서부터 읽으면,

결과적으로 map 의 결과는
( "<code>첫번째 절</code>" , "<code>두번째 절</code>", "<code>세번째 절</code>" )
가 되며,
$toc 는 "<code>첫번째 절</code> - <code>두번째 절</code> - <code>세번째 절</code>"
가 됩니다.

이제 실제로 제대로 출력되는지 확인해보겠습니다.

d:\Work\Perl\advent_abstract>t06.pl
링크 [http://advent.perl.kr/2011/2011-12-15.html]
제목 [열다섯번째 날: 한 이미지 안에 들어있는 사진들 추출하기 - Prima 모듈]
저자 [gypark]
목차 [<code>저자</code> - <code>시작하며</code> - <code>아이디어</code> - <code>Prima 모듈 설치</code>
- <code>Prima 코드 예제</code> - <code>완성</code> - <code>실행 결과</code> - <code>정리하며</code>
- <code>참고 문서</code>]

d:\Work\Perl\advent_abstract>t06.pl 1
링크 [http://advent.perl.kr/2011/2011-12-01.html]
제목 [첫째 날: 네모 반듯한 표 그리고 한글]
저자 [keedi]
목차 [<code>저자</code> - <code>시작하며</code> - <code>준비물</code> - <code>네모 반듯한 표</code>
- <code>어이쿠! 표가 깨져요!</code> - <code>어라? 줄바꿈이 어색해요</code> - <code>정리하며</code>]

6. 모듈 목록 추출

펄 크리스마스 달력의 각 기사에는 유용한 모듈들이 여럿 소개됩니다. 그 모듈들의 이름을 추출해서 목록을 출력해 보도록 합시다.

여기에서 문제는, 기사의 텍스트 중에 어떤 게 모듈의 이름인지 콕 집어서 찾아내기가 쉽지 않다는 겁니다.

그러나! 이번에도 역시 캘린더 편집자께서 꼬박꼬박 모듈 이름은 해당 모듈의 perldoc 문서와 링크를 해 두었습니다. :-)

Web::Query 모듈을 예로 든다면, 캘린더 기사 몇 개를 살펴보니 대충 다음과 같은 형태로 링크가 되어 있더군요.

(각 링크를 클릭해보면 알겠지만 처음 세 가지는 결국 동일하게 Web::Query 최신 버전의 perldoc 문서로 연결됩니다)

따라서 저렇게 생긴 링크를 찾아서 끝부분의 검색어 부분만 뽑아내면 되겠습니다.

지난 글의 코드에서 추가된 부분만 살펴보겠습니다:

 1  # 생략
 2  use List::MoreUtils qw/uniq/;
 3  # 생략
 4
 5  # 정보5. 언급된 모듈
 6  my $modules = "";
 7  my @modules;
 8  $wq->find('a')
 9     ->each( sub {
10                  my $m = $_->attr('href');
11                  if ( $m =~ m'https?://search.cpan.org/perldoc\?([^#]+)' ) {
12                      push @modules, $1;
13                  }
14                  elsif ( $m =~ m'https?://metacpan.org/module/([^#]+)' ) {
15                      push @modules, $1;
16                  }
17                  elsif ( $m =~ m'https?://p3rl.org/([^#]+)' ) {
18                      push @modules, $1;
19                  }
20                  elsif ( $m =~ m'https?://search.cpan.org/~.+/([^#]+)' ) {
21                      my $t = $1;
22                      $t =~ s/-/::/g;
23                      push @modules, $t;
24                  }
25              } );
26  $modules = join " , ", map { "<index($_)>" } uniq @modules;
27
28  # 생략
29  print "모듈 [$modules]\n";

실행 결과:

d:\Work\Perl\advent_abstract>t07.pl 1
링크 [http://advent.perl.kr/2011/2011-12-01.html]
제목 [첫째 날: 네모 반듯한 표 그리고 한글]
저자 [keedi]
목차 [<code>저자</code> - <code>시작하며</code> - <code>준비물</code> - <code>네모 반듯한 표</code>
- <code>어이쿠! 표가 깨져요!</code> - <code>어라? 줄바꿈이 어색해요</code> - <code>정리하며</code>]
모듈 [<index(Text::ASCIITable)> , <index(Text::CharWidth)> , <index(Text::WrapI18N)> , <index(Text::Wrap)>]

d:\Work\Perl\advent_abstract>t07.pl 10
링크 [http://advent.perl.kr/2011/2011-12-10.html]
제목 [열번째 날: GuiTest를 활용한 네이트온 원격제어 자동 수락기]
저자 [perlstudy]
목차 [<code>저자</code> - <code>시작하며</code> - <code>준비물</code> - <code>GuiTest 예제로 배우기</code>
- <code>네이트온 원격제어 자동 수락기</code> - <code>만들어 봅시다</code> - <code>정리하며</code>
- <code>후기</code>]
모듈 [<index(WWW::Mechanize)> , <index(Win32::GuiTest)> , <index(Win32::GuiTest::Examples)>]

7. 기사 첫 절의 내용

캘린더의 기사는 항상 "저자" 소개가 먼저 나오고, 그 다음 "시작하며" 절에서 해당 기사를 작성하게 된 동기라거나 소개할 기술에 대해 소개를 합니다. 2011년의 경우는 항상 "시작하며"라는 제목으로 되어 있고, 2010년의 경우는 기사들마다 좀 다릅니다. 가장 이상적인 거야 전체 기사의 요약문을 기사 작성자가 처음에 제공을 하거나 편집자가 따로 작성을 해서 제공하는 것이겠지만, 이는 쉽지 않은 일이고, 그게 없으니 위에 말한 "시작하며" 절의 내용을 일종의 소개글로 사용하도록 합시다.

그러면 우리가 뽑아내야 할 것은, 전체 html 파일의 내용 중에,

<h2>저자</h2>
저자소개 내용...
<h2>시작하며</h2>
시작하며 절의 내용...      <!-- 이 부분이 되겠습니다. -->
<h2>그 다음 절</h2>
그 다음 절의 내용

"저자소개 내용"을 뽑아낼 때와 거의 비슷하게 할 수 있을 것 같습니다. 그런데 그때와 크게 다른 점이 있는데, 저자소개 내용을 뽑아낼 때에는 h2 태그 안에 "저자"라고 적힌 부분을 찾으면 되는데, 지금 같은 경우는 항상 "시작하며"라고 적혀 있다는 보장이 없다는 것입니다. (대부분은 "시작하며"이지만)

그러면 앞에서부터 h2 태그를 만날 때마다 숫자를 세는 방법도 있고, 전체를 정규표현식으로 적어줄 수도 있겠습니다만... 사실 우리는 두번째 절의 제목이 무엇인지 정확히 알고 있습니다. 왜냐하면 목차를 추출할 때 각 h2 태그의 텍스트를 뽑아서 @toc 배열에 저장해 두었기 때문입니다. 이걸 사용하면 되겠군요!

그럼 일단, 저자 정보를 빼낼 때와 유사하게, 일단 저 영역의 html만 남기고 앞뒤를 제거해보도록 합시다.

# 정보6. "저자" 절 바로 다음 절의 내용
my $abstract = "";
my $abs_html = $wq->html();
$abs_html =~ s{
                ^                   # 스트링의 제일 처음
                .*                  # 임의의 문자열
                <h2[^>]*>           # <h2> 태그
                \Q$toc[1]\E         # 두번째 절의 제목 - @toc 의 두번째 원소
                </h2>               # </h2> 태그 닫고
                (.+?)               # 이 부분이 우리가 추출할 부분
                <h2[^>]*>           # 세번째 절이 시작되는 <h2> 태그
                .*                  # 임의의 문자열
                $                   # 스트링의 끝
              }
              {$1}isx;

저자 정보가 있는 영역을 추출할 때와 동일합니다, 다만 저번에는 그냥 "저자"라는 텍스트를 매치했는데, 이번에는 $toc[1]에 들어있는 문자열과 매치를 시키고 있습니다.

이 때 $toc[1]의 내용 중에 정규표현식에서 사용되는 특수 문자가 들어있을 수가 있습니다. 예를 들어 두번째 절의 제목이 "어떻게?"라고 하면, 여기 들어있는 물음표 ?가 정규식 내에서 해석되면서 이것은 "어떻" 또는 "어떻게" 두 가지 문자열에 매치되는 정규식이 되어 버립니다. 이러면 제대로 매치가 되지 않고 치환에 실패합니다.

따라서, $toc[1] 안에 들어있는 문자열은 무조건 "문자 그대로" 사용하도록 해야 합니다. 그러기 위해서 앞에 \Q 지시자를 붙여서 escape시켜주고, $toc[1] 뒤에는 \E를 붙여서 그 이후부터는 다시 평범한 정규식으로 처리하도록 합니다.

이 시점에서 $abs_html 에는 해당 영역의 html만 남게 되었습니다. 이후 처리를 위해서, $abs_html 의 내용을 출력시켜보면 다음과 같습니다.

<p>오래 전 있었던 일입니다. 우연히 다음 그림과 같은 사진 파일을 보게 되었습니다.<p>
<img alt="여러 사진이 포함된 하나의 이미지" id="" src="2011-12-15-1.png" /><br />
<em>그림 1.</em> 여러 사진이 포함된 하나의 이미지<p>하나의 JPEG 사진 파일 안에 사진
세 장이 합쳐져 있는데 이게 모니터에 다 들어오지 않아 보기에 불편하더랍니다. 그래서 저
안에 있는 사진 세 장을 각각 별도의 파일로 저장하고 싶어졌습니다.<p>위와 같이 사진이 세
장 뿐이면 그림판에서 영역 지정해 저장하면 됩니다. 하지만 파일 하나에 사진이 수십 장
들어있다면, 그리고 그런 파일이 또 수십 개나 있다면 정말 골치아픈 일이 될 것입니다.
이것을 자동으로 할 수 없을까 궁리해 보았습니다.

이제 여기서 다시 html 태그를 떼어내고 텍스트만 남기도록 합시다.

html 태그를 떼어내는 데에도 Web::Query 모듈을 사용하도록 하겠습니다. 정규표현식 치환을 쓸 수도 있지만, 이쪽이 더 수월합니다.

1차 시도:

$abstract = wq($abs_html)->text();

$abs_html 의 내용만 가지고 새로운 Web::Query 오브젝트를 만든 후에, 그 오브젝트에 text() 메쏘드를 호출합니다.

그러나 이것은 실패합니다. $abs_html 안에 들어있는 내용이 제대로 구성된 html이 아니라 일부만 떼어놓은 것이라서 그런지, "Unknown source type:" 이라는 에러가 나며 스크립트가 죽습니다.

2차 시도:

$abstract = wq("<html>$abs_html</html>")->text();

약간 꼼수를 부려보았습니다. $abs_html 내용의 앞뒤에 html, /html 태그를 붙여서, 제대로 된 html 문서 형식인 것처럼 꾸며보았습니다. 이랬더니 제대로 실행은 되었습니다만, 막상 출력 결과를 보니 다음과 같이:

오래 전 있었던 일입니다. 우연히 다음 그림과 같은 사진 파일을 보게 되었습니다.그림 1. 여
러 사진이 포함된 하나의 이미지하나의 JPEG 사진 파일 안에 사진 세 장이 합쳐져 있는데 이
게 모니터에 다 들어오지 않아 보기에 불편하더랍니다. 그래서 저 안에 있는 사진 세 장을 각각
별도의 파일로 저장하고 싶어졌습니다.위와 같이 사진이 세 장 뿐이면 그림판에서 영역 지정해
저장하면 됩니다. 하지만 파일 하나에 사진이 수십 장 들어있다면, 그리고 그런 파일이 또 수십
개나 있다면 정말 골치아픈 일이 될 것입니다. 이것을 자동으로 할 수 없을까 궁리해 보았습니다.
전체가 하나의 단락이 되어 버렸습니다. 그래서 보기에 불편합니다.

$abs_html 안에는 여러 단락이 <p>태그로 구분되어서 나옵니다. 그러면 <p>태그마다 텍스트를 따로따로 추출해봅시다.

3차 시도:

my @abstract = wq("<html >$abs_html</html>")
                 ->find('p')
                 ->text();
$abstract = join "\n\n", @abstract

@abtract 라는 배열을 추가로 선언하고, wq()로 생성한 오브젝트에 find('p')를 호출하여 p태그들을 찾고, 각각에 대해 text()를 얻어오게 하고 있습니다.

(이 때 text()를 호출하는 과정이 리스트 문맥에서 이뤄져야 각각의 텍스트가 전부 담깁니다. 따라서 배열에 할당을 하고 있습니다. 만일 스칼라 변수에 할당을 하면, 즉 스칼라 문맥에서 호출하면 첫번째 <p>에 해당하는 텍스트만 추출됩니다)

그 후 @abstract 에 들어 있는 각 원소들을 하나로 합치되, 원소 사이에 줄바꿈 문자 두 개를 삽입하도록 하고 있습니다.

실행 결과는:

오래 전 있었던 일입니다. 우연히 다음 그림과 같은 사진 파일을 보게 되었습니다.

그림 1. 여러 사진이 포함된 하나의 이미지

하나의 JPEG 사진 파일 안에 사진 세 장이 합쳐져 있는데 이게 모니터에 다 들어오지 않아 보기에 불편하더랍니다. 그래서 저 안에 있는 사진 세 장을 각각 별도의 파일로 저장하고 싶어졌습니다.

위와 같이 사진이 세 장 뿐이면 그림판에서 영역 지정해 저장하면 됩니다. 하지만 파일 하나에 사진이 수십 장 들어있다면, 그리고 그런 파일이 또 수십 개나 있다면 정말 골치아픈 일이 될 것입니다. 이것을 자동으로 할 수 없을까 궁리해 보았습니다.

만세! 우리가 원하는 대로 단락이 이쁘게 구분되었습니다.

여기까지 작성된 전체 코드입니다:

  1  #!perl
  2  # t08.pl
  3  # - URL에 해당하는 글 링크와 제목 표시
  4  # - 가져올 기사의 날짜 부분을 명령행 인자로 받음
  5  # - 저자 표시
  6  # - 목차 표시
  7  # - 언급된 모듈 표시
  8  # - 두번째 절의 내용 표시
  9  use 5.010;
 10  use strict;
 11  use warnings;
 12  use utf8;
 13
 14  use Web::Query;     # 웹 긁어오는 라이브러리
 15  use List::MoreUtils qw/uniq/;
 16
 17  binmode STDOUT, ":encoding(cp949)";
 18
 19  # 정보1. URL
 20  my $day = $ARGV[0] // 15;
 21  my $url = sprintf "http://advent.perl.kr/2011/2011-12-%02d.html", $day;
 22
 23  my $wq;
 24  eval {
 25      $wq = wq($url);
 26  };
 27  die "$@" if $@;
 28
 29  # 정보2. 제목
 30  my $title = $wq->find('h1')->text();
 31
 32  # 정보3. 저자
 33  my $author;
 34  my $author_html = $wq->html();
 35  $author_html =~ s{
 36                        ^             # 스트링의 제일 처음
 37                        .*            # 임의의 문자열
 38                        <h2[^>]*>     # <h2> 태그. <h2 id="author">등과 같은 형태일 수도 있음
 39                        저자          # '저자'
 40                        </h2>         # </h2> 태그 닫고
 41                        (.+?)         # 저자 정보가 들어 있는 텍스트를 괄호로 묶음
 42                        <h2[^>]*>     # 그 다음 절의 제목이 들어가는 <h2> 태그
 43                        .*            # 나머지 내용이 담긴 임의의 문자열
 44                        $             # 스트링의 끝
 45                   }
 46                   {$1}isx;
 47  if ( $author_html =~ m'
 48                          @           # "@"로 시작
 49                          (.+?)       # 이 부분이 아이디
 50                          (\s|</a>)   # </a>로 끝나는 경우도 있고, 그냥 공백으로 끝나는 경우도 있음
 51                        'x ) {
 52      $author = $1;
 53  }
 54  else {
 55      warn "can't extract author";
 56      $author = "";
 57  }
 58
 59  # 정보4. 목차
 60  my $toc = "";
 61  my @toc;
 62  $wq->find('h2')
 63     ->each(
 64             sub {
 65                     push @toc, $_->text;
 66                 }
 67             );
 68  $toc = join " - ", map { "<code>$_</code>" } @toc;
 69
 70  # 정보5. 언급된 모듈
 71  my $modules = "";
 72  my @modules;
 73  $wq->find('a')
 74     ->each( sub {
 75                  my $m = $_->attr('href');
 76                  if ( $m =~ m'https?://search.cpan.org/perldoc\?([^#]+)' ) {
 77                      push @modules, $1;
 78                  }
 79                  elsif ( $m =~ m'https?://metacpan.org/module/([^#]+)' ) {
 80                      push @modules, $1;
 81                  }
 82                  elsif ( $m =~ m'https?://p3rl.org/([^#]+)' ) {
 83                      push @modules, $1;
 84                  }
 85                  elsif ( $m =~ m'https?://search.cpan.org/~.+/([^#]+)' ) {
 86                      my $t = $1;
 87                      $t =~ s/-/::/g;
 88                      push @modules, $t;
 89                  }
 90              } );
 91  $modules = join " , ", map { "<index($_)>" } uniq @modules;
 92
 93  # 정보6. "저자" 절 바로 다음 절의 내용
 94  my $abstract = "";
 95  my $abs_html = $wq->html();
 96  $abs_html =~ s{
 97                  ^                   # 스트링의 제일 처음
 98                  .*                  # 임의의 문자열
 99                  <h2[^>]*>           # <h2> 태그
100                  \Q$toc[1]\E         # 두번째 절의 제목 - @toc 의 두번째 원소
101                  </h2>               # </h2> 태그 닫고
102                  (.+?)               # 이 부분이 우리가 추출할 부분
103                  <h2[^>]*>           # 세번째 절이 시작되는 <h2> 태그
104                  .*                  # 임의의 문자열
105                  $                   # 스트링의 끝
106                }
107                {$1}isx;
108
109  my @abstract = wq("<html >$abs_html</html>")
110                   ->find('p')
111                   ->text();
112  $abstract = join "\n\n", @abstract;
113
114  # 출력
115      print "링크 [$url]\n";
116      print "제목 [$title]\n";
117      print "저자 [$author]\n";
118      print "목차 [$toc]\n";
119      print "모듈 [$modules]\n";
120      print "요약\n-----\n$abstract\n-----\n";

실행 결과는:

d:\Work\Perl\advent_abstract>t08.pl
링크 [http://advent.perl.kr/2011/2011-12-15.html]
제목 [열다섯번째 날: 한 이미지 안에 들어있는 사진들 추출하기 - Prima 모듈]
저자 [gypark]
목차 [<code>저자</code> - <code>시작하며</code> - <code>아이디어</code> -
<code>Prima 모듈 설치</code> - <code>Prima 코드 예제</code> - <code>완성</code>
- <code>실행 결과</code> - <code>정리하며</code> - <code>참고 문서</code>]
모듈 [<index(Prima)> , <index(Prima::Image)>]
요약
-----
오래 전 있었던 일입니다. 우연히 다음 그림과 같은 사진 파일을 보게 되었습니다.

그림 1. 여러 사진이 포함된 하나의 이미지

하나의 JPEG 사진 파일 안에 사진 세 장이 합쳐져 있는데 이게 모니터에 다 들어오지 않아
보기에 불편하더랍니다. 그래서 저 안에 있는 사진 세 장을 각각 별도의 파일로 저장하고
싶어졌습니다.

위와 같이 사진이 세 장 뿐이면 그림판에서 영역 지정해 저장하면 됩니다. 하지만 파일 하나에
사진이 수십 장 들어있다면, 그리고 그런 파일이 또 수십 개나 있다면 정말 골치아픈 일이 될
것입니다. 이것을 자동으로 할 수 없을까 궁리해 보았습니다.
-----

d:\Work\Perl\advent_abstract>t08.pl 3
링크 [http://advent.perl.kr/2011/2011-12-03.html]
제목 [셋째 날: 소스코드 감쪽같이 숨기기]
저자 [aanoaa]
목차 [<code>저자</code> - <code>시작하며</code> - <code>어떻게?</code> - <code>만들어
봅시다!</code> - <code>정리하며</code> - <code>참고문서</code>]
모듈 [<index(Acme::Bleach)>]
요약
-----
Perl 코드를 실행할 수 있다는 것은 소스 코드도 읽을 수 있다는 것을 뜻합니다. 하지만 단 몇 줄의
Perl 코드로 여러분의 코드를 감쪽같이 숨길 수도 있습니다.
-----

우리가 원하는 모든 정보가 제대로 추출되고 있습니다.

다음 글에서는 최종 완성본의 형태를 언급하고, 마치도록 하겠습니다.

8. 마무리

지금까지 과정을 통해, 우리는 우리가 원하는 모든 정보를 추출할 수 있게 되었습니다. 이 글에서 마무리를 짓겠습니다.

출력 모양 꾸미기.

이 프로그램의 출력을 제 홈페이지에 입력하기 위한 형태로 고치는 것이야, print 를 할 때 어떻게 모양을 잡느냐에 따라 달라지는 것이므로 생략하도록 하겠습니다.

여러 날의 기사들을 한번에 받아오기.

끝으로, 각 날짜에 해당하는 걸 따로따로 받아오기 불편하니, 1일부터 24일까지, 또는 내가 지정한 범위의 기사들을 한번에 가져오도록 고쳐 봅시다.

# (생략)
my $year  = $ARGV[0] // "2011";
my $begin = $ARGV[1] // 1;
my $end   = $ARGV[2] // $ARGV[1] // 24;

foreach my $day ( $begin .. $end ) {
    # 정보1. URL
    my $url = sprintf "http://advent.perl.kr/$year/$year-12-%02d.html", $day;

    my $wq;
    eval {
        $wq = wq($url);
    };
    warn "$@" if $@;
    next if $@;

    # (생략)
}

t08.pl의 코드를 거의 그대로 쓰면서, 기존 코드에서 $url을 선언하는 부분부터 출력하는 부분까지 전체를 foreach 루프 안에 넣었습니다.

그리고 명령행 인자를 세 개까지 받도록 하였습니다. 첫째는 년도, 두번째는 시작일자, 세번째는 종료일자를 의미합니다.

만일 명령행 인자가 하나만 있으면 시작일자는 1, 끝일자는 24로 정해집니다. $end의 경우 defined-or 연산자가 두 번 쓰였습니다.

즉 실행하는 예는:

perl t09.pl            # 기본적으로 2011년, 12월 1일부터 24일까지
perl t09.pl 2010       # 2010년, 1일부터 24일까지
perl t09.pl 2011 5     # 2011년, 5일부터 5일까지, 즉 5일만
perl t09.pl 2011 3 10  # 2011년, 3일부터 10일까지
등이 됩니다. (뭔가 묘하게 옵션이 직관적이지 못하다는 느낌이 듭니다. 제가 저 혼자 쓰기 편할대로 만들어서 그렇습니다 ^_^)

그리고 wq()를 호출할 때, 지난 강좌까지는 웹 페이지를 가져올 때 404등의 에러가 나면 그 시점에서 die를 하도록 했지만, 이제는 현재 날짜 페이지를 가져오는 데 실패해도 그 다음 날짜로 계속 진행을 해야 하기 때문에, die 대신 warn으로 경고만 하고, 루프를 다시 반복하도록 next를 호출하고 있습니다.

마치며.

지난 "가계도 분석 프로젝트"에서는 정해진 형태의 텍스트를 입력으로 받아서, 자료를 구성하고 출력했다면, 이번 "크리스마스 달력 요약집 만들기"에서는 웹 페이지를 입력으로 받아서 원하는 정보를 추출하는 일을 해 보았습니다. 유사한 작업을 해야 하는 분들께 도움이 되었으면 좋겠습니다.

(제일 큰 소득은 Web::Query라는 편한 모듈이 있는 걸 알았다는 것 같기도 하군요 ^^ 저도 이번에야 알았는데 아주 좋네요)

끝으로, 펄 크리스마스 달력에 유익하고 재밌는 기사들을 기고해주시는 쟁쟁한 Perl 프로그래머 분들께 감사드립니다.
http://advent.perl.kr/2011/

9. 기타 & Comments

이름:  
Homepage:
내용:
 


컴퓨터분류
각주:
1. Perl/한글

마지막 편집일: 2012-2-11 12:25 am (변경사항 [d])
2130 hits | Permalink | 변경내역 보기 [h] | 페이지 소스 보기