php

guzzle, phpSpreadsheet 로 스크래핑

hojomu 2023. 9. 12. 12:16

1. Guzzle 활용하기

<?php

set_time_limit(0);

require 'vendor/autoload.php';

use GuzzleHttp\Client;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use GuzzleHttp\Exception\RequestException;

// Guzzle 클라이언트를 생성합니다.
$client = new Client();
$spreadsheet = new Spreadsheet();

// 완료목록
// '동탄 동물병원' => '%EB%8F%99%ED%83%84+%EB%8F%99%EB%AC%BC%EB%B3%91%EC%9B%90, 126.8946875%3B37.578325',
//     '동탄 고양이 호텔' => '%EB%8F%99%ED%83%84+%EA%B3%A0%EC%96%91%EC%9D%B4+%ED%98%B8%ED%85%94, 127.10504700000229%3B37.18412720000249',
//     '화성 동물병원' => '%ED%99%94%EC%84%B1+%EB%8F%99%EB%AC%BC%EB%B3%91%EC%9B%90, 127.10729150000145%3B37.16654380000182',

$targets = [
    '화성 고양이 호텔' => '%ED%99%94%EC%84%B1+%EA%B3%A0%EC%96%91%EC%9D%B4+%ED%98%B8%ED%85%94, 126.83140500000144%3B37.199565000000675',
    '용인 동물병원' => '%EC%9A%A9%EC%9D%B8+%EB%8F%99%EB%AC%BC%EB%B3%91%EC%9B%90, 127.10729150000094%3B37.16654380000074',
    '용인 고양이 호텔' => '%EC%9A%A9%EC%9D%B8+%EA%B3%A0%EC%96%91%EC%9D%B4+%ED%98%B8%ED%85%94, 127.12444809999863%3B37.331904300000474',
    '오산 동물병원' => '%EC%98%A4%EC%82%B0+%EB%8F%99%EB%AC%BC%EB%B3%91%EC%9B%90, 127.15263529999737%3B37.271100399999256',
    '오산 고양이 호텔' => '%EC%98%A4%EC%82%B0+%EA%B3%A0%EC%96%91%EC%9D%B4+%ED%98%B8%ED%85%94, 127.07746199999809%3B37.14988700000025',
    '군포 동물병원' => '%EA%B5%B0%ED%8F%AC+%EB%8F%99%EB%AC%BC%EB%B3%91%EC%9B%90, 127.06732260000092%3B37.169057900000354',
    '군포 고양이 호텔' => '%EA%B5%B0%ED%8F%AC+%EA%B3%A0%EC%96%91%EC%9D%B4+%ED%98%B8%ED%85%94, 126.93533800000279%3B37.36152299999945',
    '세종 동물병원' => '%EC%84%B8%EC%A2%85+%EB%8F%99%EB%AC%BC%EB%B3%91%EC%9B%90, 126.93498789999842%3B37.383211499998836',
    '세종 고양이 호텔' => '%EC%84%B8%EC%A2%85+%EA%B3%A0%EC%96%91%EC%9D%B4+%ED%98%B8%ED%85%94, 127.28943249999855%3B36.4803512',
    '아산 동물병원' => '%EC%95%84%EC%82%B0+%EB%8F%99%EB%AC%BC%EB%B3%91%EC%9B%90, 127.22461590000336%3B36.503293900001395',
    '아산 고양이 호텔' => '%EC%95%84%EC%82%B0+%EA%B3%A0%EC%96%91%EC%9D%B4+%ED%98%B8%ED%85%94, 127.06977800000124%3B36.77321300000108',
];

$cartegoryNames = ['name', 'roadAddress', 'tel', 'telDisplay', 'homePage'];

foreach ($targets as $key => $value) {
    $resultLists = [];
    $pageNumber = 1;
    $totalCount = 0;

    $target = explode(', ', $value);

    // 새로운 시트 생성
    $sheet = $spreadsheet->createSheet();
    $sheet->setTitle($key); // 시트의 이름을 키 값으로 설정

    while (true) {
        try {
            // 요청을 보낼 URL을 정의합니다.
            $url = "https://";

            echo "현재 request url = {$url}" . PHP_EOL;

            // HTTP GET 요청을 보냅니다.
            $response = $client->request('GET', $url, [
                'headers' => [
                    'Accept' => 'application/json, text/plain, */*',
                    'Accept-Encoding' => 'gzip, deflate, br',
                    'Accept-Language' => 'ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4',
                    'Cache-Control' => 'no-cache',
                    'Expires' => 'Sat, 01 Jan 2000 00:00:00 GMT',
                    'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
                    'Cookie' => 'NNB=; nx_ssl=2; page_uid=',
                    'Pragma' => 'no-cache',
                    'Referer' => "https://",
                    'Sec-Ch-Ua' => '"Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"',
                    'Sec-Ch-Ua-Mobile' => '?0',
                    'Sec-Ch-Ua-Platform' => '"Windows"',
                    'Sec-Fetch-Dest' => 'empty',
                    'Sec-Fetch-Mode' => 'cors',
                    'Sec-Fetch-Site' => 'same-origin',
                ],
            ]);

            // 응답 본문을 문자열로 가져옵니다.
            $responseBody = (string) $response->getBody();
            $jsonData = json_decode($responseBody, true);

            // 결과를 출력합니다.
            dump($jsonData);

            $lists = $jsonData['result']['place']['list'];
        } catch (RequestException $e) {
            echo "{$key} HTTP 요청 중 오류 발생: " . $e->getMessage() . PHP_EOL;
            break;
        }
        $totalCount = $jsonData['result']['place']['totalCount'];
        foreach ($lists as $list) {
            $resultList = [];
            foreach ($cartegoryNames as $name) {
                $resultList[$name] = $list[$name];
            }
            $resultLists[] = $resultList;
        }
        if (count($lists) < 20) {
            break;
        }
        $pageNumber++;
        sleep(60);
    }

    // 엑셀 데이터 추가
    $row = 2; // 데이터 행 시작
    foreach ($resultLists as $resultList) {
        $col = 'A'; // 열 초기화 (첫 번째 열)
        $sheet->setCellValue($col . $row, $row - 1); // No. 추가

        // 나머지 데이터 추가
        foreach ($resultList as $value) {
            $col++;
            $sheet->setCellValue($col . $row, $value);
        }

        $row++; // 다음 행
    }

    echo "{$key} 완료 / 리스트 총 개수 : {$totalCount}" . PHP_EOL;
    dump($resultLists);

    // Excel 파일로 저장
    $writer = new Xlsx($spreadsheet);
    $writer->save("exceldata/{$key}.xlsx");

    sleep(15);
}

Guzzle의 Client() 기능을 생성하고

$response = $client->request('GET', $url, [
                'headers' => [
                
                ],
            ]);

request를 할 수 있다. 이 때 headers에 넣을 데이터는

 request headers 를 복사해서

PostMan의 Bulk Edit을 활용해서 필요한 헤더 데이터를 찾도록 하자

 

$responseBody = (string) $response->getBody();
$jsonData = json_decode($responseBody, true);

받아온 $response는 처음에 객체 형태이기 때문에 getBody() 메서드로 내용을 가져오고, (string) 형태로 선언해준 다음 json으로 변경해서 사용할 수 있다.

 

 

2. phpSpreadsheet 

json형태의 데이터로 잘 만들었다면, 눈으로 확인하기 쉽게 엑셀에 담아서 보관할 수 있다

php8.1 버전에서 phpSpreadsheet  를 활용해서 excel파일을 읽고, 쓰는 방법을 알아보자

(버전에 따라 지원하는 excel 편집 라이브러리가 다르다)

<?php
// PhpSpreadsheet 라이브러리를 로드합니다.
require 'vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;

// Excel 파일을 로드합니다.
$spreadsheet = IOFactory::load('exceldata/unique.xlsx');

// 모든 워크시트를 가져옵니다.
$worksheets = $spreadsheet->getAllSheets();

foreach ($worksheets as $worksheet) {
    // 워크시트를 선택합니다.
    $worksheetTitle = $worksheet->getTitle();
    
    // G 열 데이터를 기준으로 행을 그룹화합니다.
    $groupedRows = [];
    foreach ($worksheet->getRowIterator() as $row) {
        $cellG = $worksheet->getCell('F' . $row->getRowIndex());
        $groupKey = $cellG->getValue();

        if (!isset($groupedRows[$groupKey])) {
            $groupedRows[$groupKey] = [];
        }

        $rowData = $worksheet->rangeToArray("B{$row->getRowIndex()}:F{$row->getRowIndex()}")[0];
        $groupedRows[$groupKey][] = $rowData;
    }

    dump($groupedRows);

    // 각 그룹마다 새로운 Excel 파일을 생성하고 데이터를 추가합니다.
    foreach ($groupedRows as $groupKey => $groupedRowData) {
        $newSpreadsheet = new Spreadsheet();
        $newWorksheet = $newSpreadsheet->getActiveSheet();

        $headerRow = ['Name', 'Address', 'Phone', 'Homepage', 'SearchKeyWord'];
        $newWorksheet->fromArray([$headerRow], null, 'A1');
        $newWorksheet->fromArray($groupedRowData, null, 'A2'); // 데이터를 워크시트에 추가

        // 새로운 Excel 파일로 저장합니다.
        $outputFileName = 'exceldata/'. $groupKey . '.xlsx';
        $writer = IOFactory::createWriter($newSpreadsheet, 'Xlsx');
        $writer->save($outputFileName);
    }
}

위의 코드는 중복 데이터가 삭제 된 excel 파일을 불러와서 특정 열의 값을 비교해서 중복이 발생하는 행을 삭제하고, 특정 열의 값이 같은 요소들 끼리 excel 파일로 나누는 중복 제거 기능을 수행한다.

 

IOFactory, SpreadSheet를 활용한다.

 

getRowIterator() 로 워크시트를 행으로 나누고

getRowIndex()로 각 행의 인덱스를 설정한 후, getCell()을 이용해서 워크시트에 존재하는 모든 F열 값을 cellG에 저장한다.

 

$groupedRows 배열에 groupKey값과 배열을 선언해주고

원하는 범위의 엑셀 데이터를 배열에 넣어주고

Spreadsheet의 기능들로 스프레드 시트를 제작한 뒤

IOFactory로 엑셀 파일을 생성하자.