링크
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가 그 디렉토리 안의 찾으려는 데이타 파일을 찾는데 시간이 너무 걸리는 경우가 발생합니다.
이개념이 인덱스의 개념으로도 활용될수도 있고, 처리 속도 개선에도 포인트 라고 생각합니다.
게시글 목록
| 번호 | 제목 |
|---|---|
| 29416 | |
| 13230 | |
| 13227 | |
| 13226 |
JavaScript
PHP 이미지 지원 함수
|
| 13225 | |
| 13224 |
MySQL
심플쪽지2.0n
|
| 25107 | |
| 13219 |
JavaScript
iframe 외부의 부모 값에 접근.
4
|
| 13218 |
jQuery
select plugin
|
| 13216 | |
| 13211 | |
| 13210 | |
| 29415 |
HTML
prototype API
|
| 13204 | |
| 13202 |
jQuery
이미지 리사이즈 jQuery 로 변경 방법
1
|
| 29411 | |
| 13199 | |
| 13196 |
기타
tar 복사
2
|
| 13192 |
jQuery
jQuery 셀렉트 박스 입니다.
3
|
| 13191 | |
| 13189 | |
| 13186 | |
| 29408 | |
| 13185 |
jQuery
jQuery plugin Editors
|
| 13183 |
jQuery
jQuery 그래프 애니메이션
1
|
| 13178 | |
| 13173 |
기타
이번달 일수 구하기
4
|
| 13166 |
jQuery
SIR 메인에 사용된 jQuery widget
6
|
| 13165 |
기타
property 구현
|
| 13163 |
JavaScript
한글만 입력되게
1
|
| 13162 |
기타
휴대폰번호 검사
|
| 13157 | |
| 13153 |
jQuery
이미지 없으면 자동으로 No Image 출력
3
|
| 13149 |
JavaScript
a 링크 점선 없애기, input+label 묶기
3
|
| 13144 |
jQuery
ifrmae 자동 리사이즈
4
|
| 13142 | |
| 13141 | |
| 13140 |
jQuery
jquery 예
|
| 13138 | |
| 13137 |
MySQL
insert에서 where 사용하기
|
| 13136 |
MySQL
MySQL Database 언어코드 수정하기
|
| 25102 | |
| 13135 |
jQuery
회원가입 아이디 체크부분 jQuery 버젼
|
| 13132 |
jQuery
jQuery History & Bookmark
2
|
| 13131 |
jQuery
스포일러 기능~
|
| 13130 |
jQuery
이미지를 순서대로 로딩하기
|
| 13128 |
jQuery
jQuery 페이징
1
|
| 13125 |
jQuery
폼 입력값 검사
2
|
| 13117 |
JavaScript
간단한 서브메뉴 onmouseover 이벤트 팁
7
|
| 13116 | |
| 13114 |
jQuery
jQuery Cheat sheet
1
|
| 13113 | |
| 13109 |
jQuery
간단한 풍선도움말
3
|
| 13106 |
jQuery
자동등록방지 코드
2
|
| 13105 | |
| 13103 | |
| 13102 |
JavaScript
클릭으로 수량 조절
|
| 13099 |
jQuery
AJAX Libraries API -- 구글
2
|
| 13096 | |
| 13091 |
jQuery
jQuery 동적테이블
4
|
| 29404 | |
| 13090 | |
| 13087 |
JavaScript
검색엔진에 노출을 못하게 하는 방법
2
|
| 29400 | |
| 13085 | |
| 13082 | |
| 29398 | |
| 13081 |
JavaScript
페이지 인쇄할때 자동으로 다음장 인쇄 기능
|
| 13080 |
기타
특정문자 제거하기
|
| 13077 |
JavaScript
숫자만 입력되게 하기
2
|
| 25097 | |
| 13076 | |
| 13075 |
기타
팝업뛰우기
|
| 13072 |
JavaScript
접속시 우측 하단에 메시지 나타나게 하기
2
|
| 13071 |
MySQL
phpMyAdmin 3.0 설치 방법
|
| 29397 |
HTML
글자수 제한시켜 짜르기
|
| 13069 | |
| 13068 | |
| 13063 | |
| 13060 |
JavaScript
ezPrice 금액 입력 도우미
2
|
| 25092 | |
| 25091 | |
| 25090 | |
| 13056 |
MySQL
FOUND_ROWS()
3
|
| 13055 | |
| 13054 | |
| 29396 |
HTML
레이어 스부적스부적 이동 스크립트
|
| 13045 | |
| 13042 | |
| 13026 | |
| 13022 | |
| 13017 | |
| 13013 |
PHP
정규식 질문드립니다.
3
|
| 13008 | |
| 13002 |
PHP
태그변환 정규식
5
|
| 12998 | |
| 25083 | |
| 12997 | |
| 29394 | |
| 29392 |
댓글 작성
댓글을 작성하시려면 로그인이 필요합니다.
로그인하기