링크
http://www.chancein.com/svc/sample/page_parsing2.php (274) http://sir.co.kr/bbs/board.php?bo_table=tip_php&wr_id=1021 (168)며칠전에 올린 http://sir.co.kr/bbs/board.php?bo_table=tip_php&wr_id=1021
에 이어지는 내용입니다.
페이지파싱의 여러가지 문제점 중의 하나는
로컬데이타를 보여주는 것보다 많이 느리다는 것입니다.
느려지는 요인에는 여러가지가 있겟지만
인터넷속도, 타겟 서버의 상태, 현재페이지의 사용정도? 등등의
가변될수 있는 상황에 따라 결과물을 받아오거나 처리하는데 시간차가 많이 날수 있습니다.
아무튼 일정치 않는 페이지파싱 처리 속도를 어느정도 일정케 하고 안정화 시키기 위해서는
다른 기능이 필요합니다.
일종의 캐시 기능이라고 볼수 있는데요.
요약하면
페이지파싱한 데이타를 지정된곳에 파일형태나 디비에 저장
현재 페이지가 열릴때마다 저장된 데이타의 저장된 시간을 비교하여
캐싱시간보다 짧으면 저장된 데이타를 보여주고
캐싱시간보다 길으면 새로 데이타를 파싱하고 가져온 데이타를 새로 저장하는 것입니다.
이번 내용에서는 파일을 사용하여 간단하게 하는 방법만 간단하게 설명합니다.
step1.
먼저 테스트하고 있는 파일과 같은 경로에 temp 라는 디렉토리를 생성합니다.
퍼미션은 707 이나 777 로 줍니다.
아래와 같은 샘플코드를 작성하여
웹에 업로드 시킨후 확인해 봅니다.
<?php
touch('temp/test');
$temp = filemtime('temp/test');
echo date("Y-m-d H:i:s", $temp);
?>
확인해보면
2008-09-29 09:35:38
라고 출력됩니다.
위 테스트 파일의 내용은
현재 실행되는 파일의 경로와 동일한 경로에 있는 temp 디렉토리에 빈파일 test 를 생성시키고
그 test 파일의 최종 수정시간을 보여주는 내용입니다.
확인을 위해서 새로고침을 여러번 해보면,
새로고침을 할때마다 해당 페이지의 시간이 바뀜을 알수있습니다.
즉, 최종수정시간을 반환하는 것입니다.
filemtime (http://kr.php.net/manual/kr/function.filemtime.php) 은 타겟파일의 최종 수정 시간을 unix timestamp 값으로 반환합니다.
step2.
간단한 캐시 시스템 만들기.
아래와 같은 샘플코드를 작성하여
웹에 업로드 시킨후 확인해 봅니다.
<?php
$save_file = 'temp/test_data';//캐싱될 데이타가 저장될 파일 경로
$check_time = 60 * 1; //1분, 한시간이면 60초가 60번이 한번 즉 60 * 60 *1, 하루면 60 * 60 * 24 *1
$current_time = time();//현재 시간의 unix timestamp
//기존 저장된 파일의 최종 수정 시간을 뽑아옴
$save_time = @filemtime($save_file);//@붙이는 이유는 파일이 없을 경우나 최종수정시간을 읽지못할 경우의 예상치 못한 에러를 방지하기 위함
//현재 시간에서 최종 수정시간을 빼서 그것이 $check_time 보다 큰지 작은지를 비교
if ($current_time - $save_time > $check_time){//$check_time 보다 클경우, 새로 데이타를 받고 저장함
//샘플데이타 배열, 나중에 데이타를 가졍오는 부분으로 바꿀 것임
$test_data = Array();
$test_data[0]['name'] = 'test 0';
$test_data[0]['title'] = 'test title 0';
$test_data[1]['name'] = 'test 1';
$test_data[1]['title'] = 'test title 1';
//여기까지
$save_data = serialize($test_data);//객체를 일렬화된 값으로 변환 시킨다. http://kr.php.net/manual/kr/function.serialize.php
$fo = fopen($save_file, 'w');//쓰기 전용으로 .....
fwrite($fo, $save_data);
fclose($fo);
echo '새로운 데이타로 저장 <br />';
}
else {//기존 데이타로 처리함
$fo = fopen($save_file, "r");//읽기 전용으로 .....
$save_data = fread($fo, filesize($save_file));
fclose($fo);
$test_data = unserialize($save_data);//일렬화된 값을 객체로 변환 시킨다. http://kr.php.net/manual/kr/function.unserialize.php
echo '기존 데이타 사용 <br />';
}
print_r($test_data);
?>
실행을 시키면 제일 처음에는
새로운 데이타로 저장
Array ( [0] => Array ( [name] => test 0 [title] => test title 0 ) [1] => Array ( [name] => test 1 [title] => test title 1 ) )
로 보이고 새로고침을 하면
기존 데이타 사용
Array ( [0] => Array ( [name] => test 0 [title] => test title 0 ) [1] => Array ( [name] => test 1 [title] => test title 1 ) )
로 보입니다.
1분이 지난후 다시 새로고침을 하면
새로운 데이타로 저장
Array ( [0] => Array ( [name] => test 0 [title] => test title 0 ) [1] => Array ( [name] => test 1 [title] => test title 1 ) )
과 같은 내용을 확인할수 있습니다.
즉, 데이타파일의 최종 수정시간이 위 소스에서 지정해놓은 $check_time (1분) 미만이면 기존 데이타를 보여주고
이상이면 새로운 데이타를 보여주는 것입니다.
배열을 데이타로 파일에 저장하기 위해서는 여러가지 방법이 있지만
저는 serialize를 사용하여 일렬화 하여 파일에 저장하였습니다.
(저장된 내용을 배열로 반환할 때는 unserialize를 사용했습니다.)
http://kr.php.net/manual/kr/function.serialize.php
http://kr.php.net/manual/kr/function.unserialize.php
step3.
페이지 파싱 부분과 캐싱 기능의 결합.
먼저, temp 안의 test_data 파일을 삭제합니다.
위 소스에서도 보이듯이
//샘플데이타 배열, 나중에 데이타를 가졍오는 부분으로 바꿀 것임
$test_data = Array();
$test_data[0]['name'] = 'test 0';
$test_data[0]['title'] = 'test title 0';
$test_data[1]['name'] = 'test 1';
$test_data[1]['title'] = 'test title 1';
//여기까지
이렇게 된 부분을 이전 것의 소스를 변형하여 바꿉니다.
아래와 같은 샘플코드를 작성하여
웹에 업로드 시킨후 확인해 봅니다.
<?php
$save_file = 'temp/test_data';//캐싱될 데이타가 저장될 파일 경로
$check_time = 60 * 1; //1분, 한시간이면 60초가 60번이 한번 즉 60 * 60 *1, 하루면 60 * 60 * 24 *1
$current_time = time();//현재 시간의 unix timestamp
//기존 저장된 파일의 최종 수정 시간을 뽑아옴
$save_time = @filemtime($save_file);//@붙이는 이유는 파일이 없을 경우나 최종수정시간을 읽지못할 경우의 예상치 못한 에러를 방지하기 위함
//현재 시간에서 최종 수정시간을 빼서 그것이 $check_time 보다 큰지 작은지를 비교
if ($current_time - $save_time > $check_time){//$check_time 보다 클경우, 새로 데이타를 받고 저장함
//페이지 파싱하여 배열에 담는 부분
$test_data = Array();
$url = 'http://sir.co.kr/bbs/new.php';
$text = file_get_contents($url);
$temp = @explode('<colgroup width="60">', $text);
$temp = @explode('</form>', $temp[1]);
$text = $temp[0];
preg_match_all("`<tr align='center' height='30'><td align='left'>.*<a href='new\.php\?gr_id=.+'>(.+)</a>.*</td><td align='left'>.*<a href='new\.php\?bo_table=.+'>(.+)</a>.*</td><td.*<a href='board\.php\?.+&wr_id=.+'>(.+)</a>.*</td><td>.*<a href=\"javascript:;\" onClick=\"showSideView\(.+\);\".+>(.+)</a>.*</td>.*</tr>`iU", $text, $match);
if (is_array($match[1])){//데이타가 있다.
$i = 0;
foreach($match[1] as $k => $v){
$test_data[$i]['g_name'] = trim($v);
$test_data[$i]['b_name'] = trim($match[2][$k]);
$test_data[$i]['title'] = trim($match[3][$k]);
$test_data[$i]['writer'] = trim(str_replace("../", '/svc/img_viewer_dummy.php?url=http://sir.co.kr/', $match[4][$k]));
$i++;
}
if (count($test_data) > 0) {//수집된 데이타가 하나 이상일 경우에만 저장.
$save_data = serialize($test_data);//객체를 일렬화된 값으로 변환 시킨다. http://kr.php.net/manual/kr/function.serialize.php
$fo = fopen($save_file, 'w');//쓰기 전용으로 .....
fwrite($fo, $save_data);
fclose($fo);
echo '새로운 데이타로 저장 <br />';
}
}
}
//새로 수집된 데이타가 없는 경우 기존 데이타로 처리한다.
//이유는 네트워크 정체등의 예상치 못한 이유로 데이타를 가져오지 못할 경우가 있기 때문이다.
if (empty($test_data) || count($test_data) == 0) {
if (is_file($save_file) && filesize($save_file) > 0){//파일이 존재하고 파일사이즈가 0보다 클때만 읽어옴
$fo = fopen($save_file, "r");//읽기 전용으로 .....
$save_data = fread($fo, filesize($save_file));
fclose($fo);
$test_data = unserialize($save_data);//일렬화된 값을 객체로 변환 시킨다. http://kr.php.net/manual/kr/function.unserialize.php
echo '기존 데이타 사용 <br />';
}
else {
$test_data = Array();
echo '수집된 데이타가 없음 <br />';
}
}
print_r($test_data);
?>
확인을 웹에서 하면
최초 새로 수집될때보다 기존 테이타 사용시가 훨씬 빠름을 알수 있습니다.
step4.
데이타의 가공 및 출력
기존 것의 내용과 동일하지만 그래도 적어봅니다.
아래와 같은 샘플코드를 작성하여
웹에 업로드 시킨후 확인해 봅니다.
<?php
$save_file = 'temp/test_data';//캐싱될 데이타가 저장될 파일 경로
$check_time = 60 * 1; //1분, 한시간이면 60초가 60번이 한번 즉 60 * 60 *1, 하루면 60 * 60 * 24 *1
$current_time = time();//현재 시간의 unix timestamp
//기존 저장된 파일의 최종 수정 시간을 뽑아옴
$save_time = @filemtime($save_file);//@붙이는 이유는 파일이 없을 경우나 최종수정시간을 읽지못할 경우의 예상치 못한 에러를 방지하기 위함
//현재 시간에서 최종 수정시간을 빼서 그것이 $check_time 보다 큰지 작은지를 비교
if ($current_time - $save_time > $check_time){//$check_time 보다 클경우, 새로 데이타를 받고 저장함
//페이지 파싱하여 배열에 담는 부분
$test_data = Array();
$url = 'http://sir.co.kr/bbs/new.php';
$text = file_get_contents($url);
$temp = @explode('<colgroup width="60">', $text);
$temp = @explode('</form>', $temp[1]);
$text = $temp[0];
preg_match_all("`<tr align='center' height='30'><td align='left'>.*<a href='new\.php\?gr_id=.+'>(.+)</a>.*</td><td align='left'>.*<a href='new\.php\?bo_table=.+'>(.+)</a>.*</td><td.*<a href='board\.php\?.+&wr_id=.+'>(.+)</a>.*</td><td>.*<a href=\"javascript:;\" onClick=\"showSideView\(.+\);\".+>(.+)</a>.*</td>.*</tr>`iU", $text, $match);
if (is_array($match[1])){//데이타가 있다.
$i = 0;
foreach($match[1] as $k => $v){
$test_data[$i]['g_name'] = trim($v);
$test_data[$i]['b_name'] = trim($match[2][$k]);
$test_data[$i]['title'] = trim($match[3][$k]);
$test_data[$i]['writer'] = trim(str_replace("../", '/svc/img_viewer_dummy.php?url=http://sir.co.kr/', $match[4][$k]));
$i++;
}
if (count($test_data) > 0) {//수집된 데이타가 하나 이상일 경우에만 저장.
$save_data = serialize($test_data);//객체를 일렬화된 값으로 변환 시킨다. http://kr.php.net/manual/kr/function.serialize.php
$fo = fopen($save_file, 'w');//쓰기 전용으로 .....
fwrite($fo, $save_data);
fclose($fo);
echo '새로운 데이타로 저장 <br />';
}
}
}
//새로 수집된 데이타가 없는 경우 기존 데이타로 처리한다.
//이유는 네트워크 정체등의 예상치 못한 이유로 데이타를 가져오지 못할 경우가 있기 때문이다.
if (empty($test_data) || count($test_data) == 0) {
if (is_file($save_file) && filesize($save_file) > 0){//파일이 존재하고 파일사이즈가 0보다 클때만 읽어옴
$fo = fopen($save_file, "r");//읽기 전용으로 .....
$save_data = fread($fo, filesize($save_file));
fclose($fo);
$test_data = unserialize($save_data);//일렬화된 값을 객체로 변환 시킨다. http://kr.php.net/manual/kr/function.unserialize.php
echo '기존 데이타 사용 <br />';
}
else {
$test_data = Array();
echo '수집된 데이타가 없음 <br />';
}
}
//데이타의 가공및 출력
if (!is_array($test_data) || count($test_data) == 0){//데이타가 배열이 아니거나 갯수가 0일때
$text = '<table><tr><td>그룹</td><td>게시판</td><td>제목</td><td>작성자</td></tr>';
$text .= '<tr height=50><td colspan=4>수집된 데이타가 없습니다.</td></tr>';
$text .= '</table>';
}
else {
$text = '<table><tr><td>그룹</td><td>게시판</td><td>제목</td><td>작성자</td></tr>';
foreach($test_data as $k => $v){
$text .= '<tr><td>' . $v['g_name'] . '</td><td>' . $v['b_name'] . '</td><td>' . $v['title'] . '</td><td>' . $v['writer'] . '</td></tr>';
}
$text .= '</table>';
}
echo $text;
?>
초보분들을 위한 간단한 강좌? 를 마치며 제 개인적인 생각을 몇가지 적는다면
발전하기 위한 삼요소는
이해, 활용, 아이디어 인것 같습니다.
이해가 가장 중요하고, 그 다음에 그것을 활용할수 있어야 하고, 그 다음에 자신이 아이디어를 내어 새로운 것을
만들어 낼수 있어야 한다고 생각합니다.
그 발전을 이루어내기 위해서는 개인적인 노력과 포기하지 않는 마음, 그리고 남의 것을 받아들일수 있는 마음 같습니다.
별로 좋은 내용은 아닐지라도 초보분들에게 도움 되었으면 좋겠습니다.
감사합니다.
댓글 8개
파일로 데이타를 저장하여 처리할경우
동접이 많은 경우의 파일 lock 문제,
계속해서 쌓여져 가는 데이타 파일의 처리 문제 그리고
하나의 저장디렉토리에서 너무 많은 파일이 쌓이는 것
등이 문제가 많이 됩니다.
즉, 데이타 파일을 저장할때 file lock 을 잘 처리해야 합니다.
안그러면, 동시에 접속하고 있는 클라이언트들은 '멍때리고' 있을 겁니다.
캐싱으로 사용하기 위해 저장된 데이타를 일정시간 간격으로 주기적으로 지워주는 과정이 필요합니다.
지금 예제와 같이 단순 하나의 데이파 파일만 있는 경우는 관계없지만
검색에 응용될 경우에는 검색어와 타겟사이트, 페이징에 딸라 따로
데이타 파일을 만들 경우가 많은데 이런경우는 사이트 활용이 높을수록
쌓이는 파일이 엄청나게 많죠.
아무튼 이부분은 php로 만든 쉘 프로그램을 크론에 등록시키는 정도로 충분히 해결가능합니다.
마지막으로, 제 개인적으로는 이게 제일 중요한데
데이타 디렉토리를 조건에 맞게 잘 적절히 잘 분리해야 합니다.
안그러면 하나의 디렉토리에 단기간에 너무 많은 데이타파일 생성되는데,
php가 그 디렉토리 안의 찾으려는 데이타 파일을 찾는데 시간이 너무 걸리는 경우가 발생합니다.
이개념이 인덱스의 개념으로도 활용될수도 있고, 처리 속도 개선에도 포인트 라고 생각합니다.
게시글 목록
| 번호 | 제목 |
|---|---|
| 13686 | |
| 13683 | |
| 21003 | |
| 13680 |
jQuery
jQuery로 만든 라이트 박스...
2
|
| 25150 | |
| 25143 | |
| 20981 | |
| 13679 | |
| 13678 |
기타
프로세스 빨리 종료하기
|
| 13677 | |
| 13675 |
JavaScript
리눅스 서버의 BIOS 및 시스템 정보 확인하기
1
|
| 13674 | |
| 29483 |
HTML
[TIP] 퀵메뉴 스크립트
1
|
| 13672 | |
| 13671 |
jQuery
성능향상을 위한 팁-9
|
| 13670 |
기타
성능향상을 위한 팁-8
|
| 13669 |
기타
성능향상을 위한 팁-7
|
| 29482 |
HTML
성능향상을 위한 팁-6
|
| 13668 |
JavaScript
성능향상을 위한 팁-5
|
| 13666 | |
| 29481 |
HTML
성능향상을 위한 팁-3
|
| 29480 |
HTML
성능향상을 위한 팁-2
|
| 13665 |
JavaScript
성능향상을 위한 팁-1
|
| 13664 |
정규표현식
자바스크립트 팁 (고급 팁)
|
| 13663 |
기타
이미지 바꾸기 이벤트
|
| 13662 | |
| 13661 |
JavaScript
Javascript 정의 팁입니다.
|
| 25124 | |
| 25116 | |
| 20970 | |
| 13660 |
JavaScript
adsc
|
| 13658 |
기타
pause 함수
1
|
| 29479 |
HTML
URL Encode & Decide
|
| 13655 | |
| 13650 |
jQuery
jQuery.each break continue
4
|
| 13648 | |
| 13647 | |
| 13646 |
Flash
insert에서 where 사용하기
|
| 29478 |
HTML
테이블 복사
|
| 13645 |
PHP
3.0
|
| 13643 |
정규표현식
정규식 온라인 테스트 사이트
1
|
| 13642 | |
| 13640 |
MySQL
박씨빌더 에러문에 대해서 질문요
1
|
| 13637 |
jQuery
이미지 마우스 온 버블 이펙트 2
2
|
| 13636 |
jQuery
cheat sheet 1.4 and so on
|
| 13635 | |
| 13634 | |
| 29476 | |
| 13633 |
JavaScript
수행시간 체크
|
| 13632 |
JavaScript
거래명세표나 견적서에 합계금액 한글로 표시.
|
| 13630 | |
| 29473 | |
| 13624 |
jQuery
플리커 이미지 랜덤 출력하기
5
|
| 13620 |
jQuery
jquery 오른쪽 마우스금지
3
|
| 13582 | |
| 13556 | |
| 29472 | |
| 29471 | |
| 13555 |
JavaScript
php > 내가 만들어 쓰는 함수 > 이미지에 워터마크 처리하기
|
| 13554 | |
| 13553 |
jQuery
jQuery + 정규표현식( 이메일검증법)
|
| 13552 |
MySQL
CentOS 5.4 에서 APM 설치
|
| 13551 | |
| 13550 | |
| 13546 | |
| 13545 | |
| 13544 | |
| 29470 | |
| 13541 | |
| 13540 | |
| 13538 | |
| 13535 | |
| 29466 | |
| 13533 | |
| 29463 | |
| 13532 |
JavaScript
입력값이 한글이 포함되어있는지 체크
|
| 13531 |
JavaScript
영문과 숫자로만 이루어졌는지 체크
|
| 13529 | |
| 29462 |
HTML
로컬 텍스트 엑셀 파일 읽기/쓰기
|
| 13528 | |
| 29459 | |
| 29458 |
HTML
숫자만 입력할수있게...
|
| 13527 | |
| 13526 | |
| 13525 | |
| 13523 | |
| 13522 |
Flash
플래시를 이용한 비쥬얼한 정규식 프로그램
|
| 13512 |
JavaScript
PHP입문부터 기초까지?;;;;;;; - 4편
9
|
| 13510 | |
| 13507 | |
| 13498 |
MySQL
[보안] MD5 해독 불능 팁
8
|
| 29452 | |
| 13492 | |
| 13487 | |
| 29450 | |
| 13480 |
JavaScript
PHP입문부터 기초까지?;;;;;;; - 2편
6
|
| 13474 |
jQuery
jquery로 label값 정렬하기...
5
|
| 13460 |
JavaScript
PHP입문부터 기초까지?;;;;;;; - 1편
13
|
| 13457 | |
| 29447 |
댓글 작성
댓글을 작성하시려면 로그인이 필요합니다.
로그인하기