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

플러그인에 있는 파싱 쓰고있는데 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;

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

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

회원로그인

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