플러그인에 있는 파싱 쓰고있는데 php7.2로 업데이트 이후

매출이 오르면 내리는 수수료! 지금 수수료센터에서 전자결제(PG)수수료 비교견적 신청해 보세요!
플러그인에 있는 파싱 쓰고있는데 php7.2로 업데이트 이후

QA

플러그인에 있는 파싱 쓰고있는데 php7.2로 업데이트 이후

본문

GnuboardScrap.php 코드


<?php
/**
 * @author:  명랑폐인 *** 개인정보보호를 위한 이메일주소 노출방지 ***
 * License: 상업적 이용 금지. 소스 수정후 배포금지. 라이센스 문구 삭제금지.
 *          상업적 이용은 sir.co.kr 컨텐츠몰에서 구매 바랍니다.
 */
require 'library/vendor/autoload.php';
use Goutte\Client;
use Symfony\Component\DomCrawler\Crawler;
class GnuboardScrap
{
    private $protocol = "https";
    private $doamin = "";
    protected $is_debug = false;
    protected $debug_info = array();
    protected $client = null;
    protected $ssl_options = null;
    public function __construct($domain, $protocol="http", $is_debug=false) {
        $this->domain = $domain;
        $this->protocol = strtolower($protocol);

        $client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.github.com/gists/c027285c8058e707fa95', array());
        //https 접속시 ssl 오류가 발생하면, ['verify' => false] 옵션을 추가한다.
        if($this->protocol == "https") {
            $this->ssl_options = ['verify' => false];
        }
        $this->client = new \Goutte\Client();
        // Create and use a guzzle client instance that will time out after 90 seconds
        $guzzleClient = new \GuzzleHttp\Client(array(
            'timeout' => 90,
            'verify' => false,
        ));
        $this->client->setClient($guzzleClient);
    }
    public function __destruct() {
    }
    public function setDebug($is_debug) {
        $this->is_debug = $is_debug ? true : false;
    }
    /**
     * 게시판 목록을 파싱한다.
     * 파싱대상에는 반드시 content_url 정보가 필요하다.
     *  $uri 파싱대상 uri
     *  array 게시판 리스트 목록.
     */
    public function getList($uri) {
        $url = $this->protocol."://".$this->domain.$uri;
        $crawler = $this->client->request('GET', $url);
        $items = $crawler->filter('div.sir_ul01 ul')->children();
        $result = array();
        foreach($items as $item) {
            $row = array();
            $obj = new Crawler($item);
            try { //제목영역은 정상 파싱이 안되기 때문에 예외처리로 제외한다.
                $wr_id = $obj->filter("div.li_num")->text();
                $title =  $obj->filter('a.title_link')->text();
                $href = $obj->filter('a.title_link')->attr("href");
                if(strpos($href, "//") ===0) $href = $this->protocol.":".$href;
                $row['wr_id'] = trim($wr_id);
                $row['title'] = trim($title);
                $row['content_url'] = trim($href);
                $result[] = $row;
            } catch(Exception $e) {
                $this->addDebugInfo("[" . __METHOD__ . "() line ".__LINE__."] ".$e->getMessage(). "");
            }
        }
        return $result;
    }
    /**
     * 게시판 본문 내용을 크롤링후  파싱한다.
     * 파싱대상은 제목, 본문내용, 첨부파일, 등록자명, 등록자 아이디, 등록일자 등
     *
     *  $uri
     *  array
     */
    public function getContent($wr_id, $url) {
        $result = array();
        $crawler = $this->client->request('GET', $url);
        $result['wr_id'] = $wr_id;
        $result['wr_subject'] = $crawler->filter("h2.head_h2")->text();
        $result['wr_name'] = $crawler->filter("#info_name span.sv_wrap a .member")->text();
        $writer_link =  $crawler->filter("#info_name span.sv_wrap a")->attr("href");
        $parts = parse_url($writer_link);
        parse_str($parts['query'], $query);
        $result['mb_id'] = $send_mb_id = $query['mb_id'];
        $result['wr_datetime'] =  $crawler->filter("#info_date")->text();
        $result['wr_hit'] =  $crawler->filter("#info_hit span")->text();
        $result['wr_comment_cnt'] =  $crawler->filter("#info_cmt a span")->text();
        $result['wr_content'] =  $crawler->filter("#vbo_con .con_inner")->html();
        $result['content_images'] = $this->getContentImages($crawler->filter("#vbo_con .con_inner"));
        $result['comments'] = $this->getCommentList($crawler->filter("section.vbo_vcmt"));
        return $result;
    }
    /**
     * 본문 컨텐츠에서 코멘트를 추출한다.
     * 
     *  $node
     *  array 컨텐츠 목록
     * 
     */
    public function getCommentList($node) {
        $comments = $node->filter('article.vcmt')->each(function($node, $i) {
        });
        //todo 코멘트 리스트 파싱 처리
        //todo 댓글이 폐이징되어 표시되는 경우, ajax로 호출하는 경우 처리
    }
    /**
     * 본문 컨텐츠에서 첨부 이미지를 추출한다.
     * 추출 대상 이미지는 editor영역 에서 추출한 이미지와, 가변파일로 첨부한 이미지
     *  $node
     *  array, editor 영역 이미지와 가변파일 이미지를 따로 추출한다.
     */
    private function getContentImages($node) {
        //가변파일 업로드로 업로드한 이미지들.
        $upload_images = $node->filter("img")->each(function($node) {
            //todo 보안을 위해서는 이미지 확장자를 체크하는게 좋다.
            $img_src = $node->attr("src");
            if(stripos($img_src, $this->doamin."/data/editor") !== FALSE) {
                return $img_src;
            }
        });
        //에디터로 업로드한 이미지들.
        $embed_images = $node->filter("img")->each(function($node) {
            $img_src = $node->attr("src");
            //todo 보안을 위해서는 이미지 확장자를 체크하는게 좋다.
            if(stripos($img_src, $this->doamin."/data/file/") !== FALSE) {
                return $img_src;
            }
        });
        //return ["upload_images" => array_filter($upload_images)
        //    , "embed_images" => array_filter($embed_images)
        //];
        return array_filter(array_merge($upload_images, $embed_images));
    }
    /**
     * url 에 있는 파일을 다운받아 $upload_temp_dir 에 저장한다.
     * $filepath 는 cheditor 경로
     *  $url 다운받을 파일 위치
     *  $upload_temp_dir 임시 업로드 위치
     *  $filepath
     *  다운로드후 저장된 파일의 경로, 실패시 null
     */
    public function downloadUrlImage($url, $upload_dir, $filename) {
        try {
            $filepath = $upload_dir . "/" . $filename;
            $file_handle = fopen($filepath, 'w');
            $client = new \GuzzleHttp\Client();
            $response = $client->get($url, ['save_to' => $file_handle]);
            return $filepath;
        } catch(Exception $e) {
            $this->addDebugInfo("[" . __METHOD__ . "() line ".__LINE__."] ".$e->getMessage(). "");
            return null;
        }
    }
    private function addDebugInfo($comment) {
        $this->debug_info[] = "DEBUG ".date("Y-m-d H:i:s")." ".$comment;
    }
    public function getDebugInfo() {
        return $this->debug_info;
    }

}

 

board_scrap_functions.php 플러그인 코드

 


<?php
/**
 * @author:  명랑폐인 *** 개인정보보호를 위한 이메일주소 노출방지 ***
 * License : 제작자 삭제 금지. 출처 표기.
 */
if(!function_exists('board_write')) {
    /**
     * 그누보드 게시물 등록 함수.
     * 그누보드의 게시판 등록 파일(write_update.php) 로직을 참고하였습니다.
     *  $bo_table 게시판명 ex) freeboard
     *  $subject  게시물 제목
     *  $content 게시물 내용
     *  array $files 업로드할 가변파일 목록, 가변파일이 필요한 경우만 사용
     *        ex /home/account/www/data/uploads/images1.jpg,,,,
     */
    function board_write($bo_table, $contents, $files=array()) {
        global $g5, $config;
        $write_table = "g5_write_{$bo_table}";
        $wr_num = get_next_num($write_table);
        $wr_reply = '';
        $ca_name = "";
        $wr_option = "html1";
        $secret = "";
        $mail = "";
        $wr_subject = $contents['wr_subject'];
        $wr_content = $contents['wr_content'];
        $wr_link1 = "";
        $wr_link2 = "";
        //파싱원본글에 대한 wr_id
        //todo 중복등록을 피하기 위해서 $ori_wr_id값을 확장필드에 저장후, 중복 체크하는게 좋다.
        $ori_wr_id = $contents['wr_id'];
        //관리자가 등록한것으로 처리한다.
        //$mb_id = $contents['mb_id'];
        //$wr_name = $contents['wr_name'];
        $mb_id = "admin";
        $wr_name = "관리자";
        $wr_email = "";
        $sql = " insert into $write_table
                    set wr_num = '$wr_num',
                         wr_reply = '$wr_reply',
                         wr_comment = 0,
                         ca_name = '$ca_name',
                         wr_option = '$wr_option',
                         wr_subject = '$wr_subject',
                         wr_content = '$wr_content',
                         wr_link1 = '$wr_link1',
                         wr_link2 = '$wr_link2',
                         wr_link1_hit = 0,
                         wr_link2_hit = 0,
                         wr_hit = 0,
                         wr_good = 0,
                         wr_nogood = 0,
                         mb_id = '$mb_id',
                         wr_password = '',
                         wr_name = '$wr_name',
                         wr_email = '$wr_email',
                         wr_homepage = '',
                         wr_datetime = '" . G5_TIME_YMDHIS . "',
                         wr_last = '" . G5_TIME_YMDHIS . "',
                         wr_ip = '0.0.0.0',
                         wr_1 = '',
                         wr_2 = '',
                         wr_3 = '',
                         wr_4 = '',
                         wr_5 = '',
                         wr_6 = '',
                         wr_7 = '',
                         wr_8 = '',
                         wr_9 = '',
                         wr_10 = '' ";
        sql_query($sql);
        $wr_id = sql_insert_id();
        // 부모 아이디에 UPDATE
        sql_query(" update $write_table set wr_parent = '$wr_id' where wr_id = '$wr_id' ");
        // 새글 INSERT
        sql_query(" insert into {$g5['board_new_table']} ( bo_table, wr_id, wr_parent, bn_datetime, mb_id ) values ( '{$bo_table}', '{$wr_id}', '{$wr_id}', '" . G5_TIME_YMDHIS . "', '$mb_id' ) ");
        // 게시글 1 증가
        sql_query("update {$g5['board_table']} set bo_count_write = bo_count_write + 1 where bo_table = '{$bo_table}'");
        // 파일개수 체크
        $file_count = 0;
        $upload_count = count($files);
        for ($i = 0; $i < $upload_count; $i++) {
            if ($files[$i] && file_exists($files[$i]))
                $file_count++;
        }
        // 디렉토리가 없다면 생성합니다. (퍼미션도 변경하구요.)
        @mkdir(G5_DATA_PATH . '/file/' . $bo_table, G5_DIR_PERMISSION);
        @chmod(G5_DATA_PATH . '/file/' . $bo_table, G5_DIR_PERMISSION);
        $chars_array = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z'));
        // 가변 파일 업로드
        $file_upload_msg = '';
        $upload = array();
        for ($i = 0; $i < count($files); $i++) {
            $upload[$i]['file'] = '';
            $upload[$i]['source'] = '';
            $upload[$i]['filesize'] = 0;
            $upload[$i]['image'] = array();
            $upload[$i]['image'][0] = '';
            $upload[$i]['image'][1] = '';
            $upload[$i]['image'][2] = '';
            $upload[$i]['del_check'] = false;
            $tmp_file = $files[$i];
            $filesize = filesize($files[$i]);
            $filename = basename($files[$i]);
            $filename = get_safe_filename($filename);
            if (file_exists($tmp_file)) {
                //=================================================================\
                // 090714
                // 이미지나 플래시 파일에 악성코드를 심어 업로드 하는 경우를 방지
                // 에러메세지는 출력하지 않는다.
                //-----------------------------------------------------------------
                $timg = @getimagesize($tmp_file);
                // image type
                if (preg_match("/\.({$config['cf_image_extension']})$/i", $filename) ||
                    preg_match("/\.({$config['cf_flash_extension']})$/i", $filename)) {
                    if ($timg['2'] < 1 || $timg['2'] > 16)
                        continue;
                }
                //=================================================================
                $upload[$i]['image'] = $timg;
                // 프로그램 원래 파일명
                $upload[$i]['source'] = $filename;
                $upload[$i]['filesize'] = $filesize;
                // 아래의 문자열이 들어간 파일은 -x 를 붙여서 웹경로를 알더라도 실행을 하지 못하도록 함
                $filename = preg_replace("/\.(php|phtm|htm|cgi|pl|exe|jsp|asp|inc)/i", "$0-x", $filename);
                shuffle($chars_array);
                $shuffle = implode('', $chars_array);
                // 첨부파일 첨부시 첨부파일명에 공백이 포함되어 있으면 일부 PC에서 보이지 않거나 다운로드 되지 않는 현상이 있습니다. (길상여의 님 090925)
                $upload[$i]['file'] = abs(ip2long($_SERVER['REMOTE_ADDR'])) . '_' . substr($shuffle, 0, 8) . '_' . replace_filename($filename);
                $dest_file = G5_DATA_PATH . '/file/' . $bo_table . '/' . $upload[$i]['file'];
                // 업로드가 안된다면 에러메세지 출력하고 죽어버립니다.
                $error_code = copy($tmp_file, $dest_file) or die("upload error");
                // 올라간 파일의 퍼미션을 변경합니다.
                chmod($dest_file, G5_FILE_PERMISSION);
            }
        }
        // 나중에 테이블에 저장하는 이유는 $wr_id 값을 저장해야 하기 때문입니다.
        for ($i = 0; $i < count($upload); $i++) {
            if (!get_magic_quotes_gpc()) {
                $upload[$i]['source'] = addslashes($upload[$i]['source']);
            }
            $sql = " insert into {$g5['board_file_table']}
                    set bo_table = '{$bo_table}',
                         wr_id = '{$wr_id}',
                         bf_no = '{$i}',
                         bf_source = '{$upload[$i]['source']}',
                         bf_file = '{$upload[$i]['file']}',
                         bf_content = '',
                         bf_download = 0,
                         bf_filesize = '{$upload[$i]['filesize']}',
                         bf_width = '{$upload[$i]['image']['0']}',
                         bf_height = '{$upload[$i]['image']['1']}',
                         bf_type = '{$upload[$i]['image']['2']}',
                         bf_datetime = '" . G5_TIME_YMDHIS . "' ";
            sql_query($sql);
        }
        // 업로드된 파일 내용에서 가장 큰 번호를 얻어 거꾸로 확인해 가면서
        // 파일 정보가 없다면 테이블의 내용을 삭제합니다.
        $row = sql_fetch(" select max(bf_no) as max_bf_no from {$g5['board_file_table']} where bo_table = '{$bo_table}' and wr_id = '{$wr_id}' ");
        for ($i = (int)$row['max_bf_no']; $i >= 0; $i--) {
            $row2 = sql_fetch(" select bf_file from {$g5['board_file_table']} where bo_table = '{$bo_table}' and wr_id = '{$wr_id}' and bf_no = '{$i}' ");
            // 정보가 있다면 빠집니다.
            if ($row2['bf_file']) break;
            // 그렇지 않다면 정보를 삭제합니다.
            sql_query(" delete from {$g5['board_file_table']} where bo_table = '{$bo_table}' and wr_id = '{$wr_id}' and bf_no = '{$i}' ");
        }
        // 파일의 개수를 게시물에 업데이트 한다.
        $row = sql_fetch(" select count(*) as cnt from {$g5['board_file_table']} where bo_table = '{$bo_table}' and wr_id = '{$wr_id}' ");
        sql_query(" update {$write_table} set wr_file = '{$row['cnt']}' where wr_id = '{$wr_id}' ");
        return $wr_id;
    }
}
if(!function_exists('board_content_image_replace')) {
    /**
     * 게시판 본문에 첨부된 이미지 url경로를 업로드한 url경로로 치환한다.
     * 원본 이미지 url 경로에서 domain과, path, filename 을 변경한다.
     *  $content 게시판 본문
     *  $images array original 이미지 경로와
     */
    function board_content_image_replace($content, $images) {
        $new_content = $content;
        $domain = G5_DOMAIN ? G5_DOMAIN : $_SERVER['HTTP_HOST'];
        //사이트 호스트정보가 IP로 나오는경우 삭제처리
        if(filter_var($domain, FILTER_VALIDATE_IP)) {
            $domain = '';
        }
        for($i =0; $i<count($images); $i++) {
            $original_url = $images[$i]['original_url'];
            $upload_filepath = $images[$i]['upload_filepath'];
            $my_domain_url = $domain ? "//".$domain.$upload_filepath : $upload_filepath;
            $new_content = str_replace($original_url, $my_domain_url, $new_content);
        }
        /* 테스트 코드
        echo "<xmp>";
        echo "============ old wr_content =========\n";
        echo $content;
        echo "============ new wr_content =========\n";
        echo $new_content;
        echo "</xmp>";
        */
        return $new_content;
    }
}

if(!function_exists('generate_new_filename')) {
    /**
     * 랜덤한 파일명을 생성한다. 초기 파일 확장자는 유지한다.
     * 그누보드의 파일명 생성 로직을 참고하였습니다.
     *  $filname 파일명
     *  String 신규 파일명
     */
    function generate_new_filename($filename) {
        $chars_array = array_merge(range(0,9), range('a','z'), range('A','Z'));
        shuffle($chars_array);
        $shuffle = implode('', $chars_array);
        $prefix =  abs(ip2long($_SERVER['REMOTE_ADDR'])).'_'.substr($shuffle,0,8);
        $usec = get_microtime();
        $info = pathinfo($filename);
        $ext = $info['extension'];
        $new_filename =  sha1($_SERVER['REMOTE_ADDR'].$usec).($ext ? '.'.$ext : "");
        return $prefix."_".$new_filename;
    }
}
if(!function_exists('gnuboard_scrap')) {
    /**
     * 대상 url 게시물을 파싱후 게시판에 등록한다.
     *  $url 파싱 대상 url
     *  $target_bo_table 파싱후 등록할 대상 테이블
     *  array  게시물들이 등록되면, 등록한 wr_id
     */
    function gnuboard_scrap($url, $target_bo_table, $print_debug = false) {
        global $g5, $member;
        include_once dirname(__DIR__)."/lib/GnuboardScrap.php";
        $ym = date('ym', G5_SERVER_TIME);
        $upload_temp_dir = G5_DATA_PATH."/scrap"; //임시 업로드 디렉토리
        @mkdir($upload_temp_dir, G5_DIR_PERMISSION);
        @chmod($upload_temp_dir, G5_DIR_PERMISSION);
        $upload_editor_dir = G5_DATA_PATH."/editor/".$ym; //에디터 업로드 디렉토리
        @mkdir($upload_editor_dir, G5_DIR_PERMISSION);
        @chmod($upload_editor_dir, G5_DIR_PERMISSION);
        $url_info = parse_url($url);
        $domain = $url_info['host'];
        $scheme = $url_info['scheme'];
        $uri = $url_info['path'].($url_info['query'] ? "?".$url_info['query'] : "");

        $result = array();
        $scrap = new GnuboardScrap($domain, $scheme);
        if($print_debug) {
            $scrap->setDebug(true);
        }
        $list = $scrap->getList($uri);
        foreach($list as $row) {
            //todo 이미 파싱한 게시물인지 체크, $row['wr_id'] 값을 게시물에 저장후 중복된 데이타인지 체크
            $contents = $scrap->getContent($row['wr_id'], $row['content_url']);
            $upload_data_list = array();
            //todo 가변파일로 업로드된 이미지와, 에디터 embed 이미지는 분리하여 처리하여야 하나
            //      불필요하게 코드를 복잡하게 만들기 때문에, 에디터 embed 이미지로 처리한다.
            for($i = 0;$i<count($contents['content_images']); $i++) {
                $download_url = $contents['content_images'][$i];
                $url_info  = parse_url($download_url);
                $path_info = pathinfo($url_info['path']);
                $filename = $path_info['basename'];
                $filename = generate_new_filename($filename); //원본 이미지 url 경로와 다른 파일명으로 저장한다.
                $upload_filepath = $scrap->downloadUrlImage($download_url, $upload_editor_dir, $filename);
                //다운로드 url 경로와 다운로드 받은 파일경로를 같이 저장한다.
                $upload_data_list[] = array(
                      "original_url" => $download_url
                    , "upload_filepath" => "/".G5_DATA_DIR."/editor/".$ym."/".$filename
                );
            }
            //게시판 본문 이미지 태그의 정보를 다운로드한 이미지 경로로 변경한다.
            $contents['wr_content'] = board_content_image_replace($contents['wr_content'], $upload_data_list);
            $wr_id  = board_write($target_bo_table, $contents);
            $result[] = array("wr_subject" => $contents['wr_subject'], "wr_id" => $wr_id);
            //가변파일을 처리하는 경우, 임시 업로드된 이미지를 삭제한다.
            //foreach (glob($upload_temp_dir."/*") as $temp_filename) {
            //    unlink($temp_filename);
            //}

            sleep(1);
            //FIXME 예제코드가 sir.co.kr 유머게시판을 파싱합니다.
            //과도한 트래픽과 request를 방지하기 위해 게시물 1개만 파싱되도록 하였습니다.
            //페이지별로 전체를 파싱하기 위해선 아래 break 문을 삭제하시면 됩니다.
            //과도한 request가 발생하면 sir.co.kr 사이트에서 접속이 차단될수 있습니다.(주의바람)
            //sir.co.kr 에 대한 파싱은 테스트로만 사용하길 바랍니다.
            break;
        }
        if($print_debug) {
            print_r2($scrap->getDebugInfo());
        }
        return $result;
    }
}
?>

제목은 잘가져오고 여러분들이 답변 주셔서 오류는 안뜨는데

내용을 못가져오고 있는데 왜그런건가요?

이 질문에 댓글 쓰기 :

답변 3

크롤링 대상 서버에서 코드구성을 바꾼 경우 기존에 지정된 filter 는 동작안할 가능성도 있으며

그런 경우 filter 에 지정된 selector 를 수정해야 할수 있습니다.

 

e.g.

 

크롤링 대상 서버에서 응답 텍스트가 다음과 같이 떨어질때는


<div id="info_name"><span class="sv_wrap"><a href="#" class="member">text</a></span></div>

다음 셀렉터가 유효하지만


$crawler->filter("#info_name span.sv_wrap a .member");

 

 

크롤링 대상 서버에서 응답 텍스트가 이전과 다르게 다음과 같이 수정되었다면


<div id="info_nam"><b class="sv_wra"><a href="#" class="membe">text</a></b></div>

다음 셀렉터는 유효하지 않게 됩니다.


$crawler->filter("#info_name span.sv_wrap a .member");

if(strpos($href, "//") ===0) $href = $this->protocol.":".$href;

if (strpos($href, "//") == 0) $href = $this->protocol.":".$href;

 

Or

 

if(strpos($href, "//") ===0) $href = $this->protocol.":".$href;

if (substr($href, 0, 2) == "//") $href = $this->protocol.":".$href;

이거 플러그인내용이 뭔데요? 깃헙 관련 된거 같아보인데 정확히 알고 싶어요

답변을 작성하시기 전에 로그인 해주세요.
전체 465
QA 내용 검색

회원로그인

(주)에스아이알소프트 / 대표:홍석명 / (06211) 서울특별시 강남구 역삼동 707-34 한신인터밸리24 서관 1404호 / E-Mail: admin@sir.kr
사업자등록번호: 217-81-36347 / 통신판매업신고번호:2014-서울강남-02098호 / 개인정보보호책임자:김민섭(minsup@sir.kr)
© SIRSOFT