Skip to content

PHP에서 네이티브 DOM, Simple HTML DOM Parser, Symfony의 DomCrawler를 사용하여 HTML을 파싱하는 방법과 각 방법의 강점 및 사용 사례 비교.

Notifications You must be signed in to change notification settings

bright-kr/php-html-parsing

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 

Repository files navigation

PHP로 HTML 파싱하기

Promo

이 가이드는 PHP에서 HTML을 파싱하는 3가지 기법을 살펴보고, 각각의 강점과 차이점을 비교합니다:

Why Parse HTML in PHP?

PHP에서의 HTML 파싱은 HTML 콘텐츠를 DOM(Document Object Model) 구조로 변환하는 작업을 의미합니다. DOM 형식으로 변환되면 HTML 콘텐츠를 쉽게 탐색하고 조작할 수 있습니다.

특히, PHP에서 HTML을 파싱해야 하는 주요 이유는 다음과 같습니다:

  • 데이터 추출: 웹 페이지에서 특정 콘텐츠를 가져오며, HTML 요소의 텍스트 또는 속성도 포함합니다.
  • 자동화: 콘텐츠 스크レイピング, 리포팅, HTML 기반 데이터 집계 등의 작업을 간소화합니다.
  • 서버 측 HTML 처리: 애플리케이션에서 렌더링하기 전에 HTML을 파싱 및 조작하여 웹 콘텐츠를 정리, 형식화 또는 수정합니다.

Prerequisites

코딩을 시작하기 전에, 머신에 PHP 8.4+가 설치되어 있는지 확인하십시오. 다음 명령을 실행하여 확인할 수 있습니다:

php -v

출력은 아래와 비슷하게 표시되어야 합니다:

PHP 8.4.3 (cli) (built: Jan 19 2025 14:20:58) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.4.3, Copyright (c) Zend Technologies
    with Zend OPcache v8.4.3, Copyright (c), by Zend Technologies

다음으로, 의존성 관리를 더 쉽게 하기 위해 Composer 프로젝트를 초기화하십시오. 시스템에 Composer가 설치되어 있지 않다면, 다운로드한 뒤 설치 지침을 따르십시오.

먼저, PHP HTML 프로젝트를 위한 새 폴더를 생성합니다:

mkdir php-html-parser

터미널에서 해당 폴더로 이동한 뒤 composer init 명령으로 그 안에서 Composer 프로젝트를 초기화합니다:

composer init

이 과정에서 몇 가지 질문이 표시됩니다. 기본 답변으로도 충분하지만, 필요하다면 PHP HTML 파싱 프로젝트에 맞게 더 구체적인 정보를 입력하여 설정을 조정할 수 있습니다.

다음으로, 선호하는 IDE에서 프로젝트 폴더를 여십시오. PHP 개발에는 Visual Studio Code with the PHP extension 또는 IntelliJ WebStorm이 좋은 선택입니다.

이제 프로젝트 폴더에 비어 있는 index.php 파일을 추가하십시오. 프로젝트 구조는 다음과 같아야 합니다:

php-html-parser/
  ├── vendor/
  ├── composer.json
  └── index.php

index.php를 열고 다음 코드를 추가하여 프로젝트를 초기화하십시오:

<?php

require_once __DIR__ . "/vendor/autoload.php";

// scraping logic...

다음 명령으로 스크립트를 실행합니다:

php index.php

HTML Retrieval in PHP

PHP에서 HTML을 파싱하기 전에, 파싱할 HTML이 필요합니다. 이 섹션에서는 PHP에서 HTML 콘텐츠에 접근하는 두 가지 다른 접근 방식을 살펴봅니다. 또한 PHP로 web scraping 하는 방법 가이드도 읽어보시는 것을 권장합니다.

With CURL

PHP는 HTTP 리クエスト를 수행하는 데 널리 사용되는 HTTP 클라이언트인 cURL을 기본적으로 지원합니다. cURL extension을 활성화하거나, Ubuntu Linux에서는 다음으로 설치할 수 있습니다:

sudo apt-get install php8.4-curl

cURL을 사용하면 온라인 서버로 HTTP GET 리クエスト를 보내고 서버가 반환한 HTML 문서를 가져올 수 있습니다. 아래 예제 스크립트는 간단한 GET 리クエスト를 수행하고 HTML 콘텐츠를 가져옵니다:

// initialize cURL session
$ch = curl_init();

// set the URL you want to make a GET request to
curl_setopt($ch, CURLOPT_URL, "https://www.scrapethissite.com/pages/forms/?per_page=100");

// return the response instead of outputting it
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// execute the cURL request and store the result in $response
$html = curl_exec($ch);

// close the cURL session
curl_close($ch);

// output the HTML response
echo $html;

위 코드 스니펫을 index.php에 추가하고 실행하십시오. 그러면 다음과 같은 HTML 코드가 출력됩니다:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hockey Teams: Forms, Searching and Pagination | Scrape This Site | A public sandbox for learning web scraping</title>
    <link rel="icon" type="image/png" href="/static/images/scraper-icon.png" />
    <!-- Omitted for brevity... -->
</html>

From a File

cURL로 이전에 가져온 Scrape This Site의 “Hockey Teams” 페이지 HTML을 담고 있는 index.html 파일이 있다고 가정하겠습니다:

The index.html file in the project folder

HTML Parsing in PHP: 3 Approaches

이 섹션에서는 PHP에서 HTML을 파싱하기 위해 세 가지 다른 라이브러리를 사용하는 방법을 설명합니다:

  1. 바닐라 PHP를 위한 Dom\HTMLDocument 사용
  2. Simple HTML DOM Parser 라이브러리 사용
  3. Symfony의 DomCrawler 컴포넌트 사용

세 가지 경우 모두, 로컬 index.html 파일의 HTML을 파싱하여 페이지의 모든 하키 팀 엔트리를 선택하고 그로부터 데이터를 추출합니다:

The table on the target page

최종 결과는 다음 세부 정보를 포함하는 스크レイピング된 하키 팀 엔트리 목록이 됩니다:

  • Team Name
  • Year
  • Wins
  • Losses
  • Win %
  • Goals For (GF)
  • Goals Against (GA)
  • Goal Difference

다음 구조를 가진 HTML 테이블에서 이를 추출할 수 있습니다:

The HTML DOM structure of the table's rows

테이블 행의 각 열은 특정 class를 가지므로, class를 CSS selector로 사용하여 요소를 선택하고 text를 통해 콘텐츠를 가져와 데이터를 추출할 수 있습니다.

Approach #1: With Dom\HTMLDocument

PHP 8.4+에는 내장 Dom\HTMLDocument 클래스가 포함되어 있습니다. 이는 HTML 문서를 나타내며, HTML 콘텐츠를 파싱하고 DOM 트리를 탐색할 수 있게 해줍니다.

Step #1: Installation and Set Up

Dom\HTMLDocumentStandard PHP Library의 일부입니다. 다만 이를 사용하려면 DOM extension을 활성화하거나, 다음 Linux 명령으로 설치해야 합니다:

sudo apt-get install php-dom

Step #2: HTML Parsing

아래와 같이 HTML string을 파싱할 수 있습니다:

$dom = \DOM\HTMLDocument::createFromString($html);

index.html 파일은 다음과 같이 파싱할 수 있습니다:

$dom = \DOM\HTMLDocument::createFromFile("./index.html");

$dom은 데이터 파싱에 필요한 메서드를 제공하는 Dom\HTMLDocument 객체입니다.

Step #3: Data Parsing

다음 접근 방식으로 \DOM\HTMLDocument를 사용해 모든 하키 팀 엔트리를 선택할 수 있습니다:

// select each row on the page
$table = $dom->getElementsByTagName("table")->item(0);
$rows = $table->getElementsByTagName("tr");

// iterate through each row and extract data
foreach ($rows as $row) {
  $cells = $row->getElementsByTagName("td");

  // extracting the data from each column
  $team = trim($cells->item(0)->textContent);
  $year = trim($cells->item(1)->textContent);
  $wins = trim($cells->item(2)->textContent);
  $losses = trim($cells->item(3)->textContent);
  $win_pct = trim($cells->item(5)->textContent);
  $goals_for = trim($cells->item(6)->textContent);
  $goals_against = trim($cells->item(7)->textContent);
  $goal_diff = trim($cells->item(8)->textContent);

  // create an array for the scraped team data
  $team_data = [
    "team" => $team,
    "year" => $year,
    "wins" => $wins,
    "losses" => $losses,
    "win_pct" => $win_pct,
    "goals_for" => $goals_for,
    "goals_against" => $goals_against,
    "goal_diff" => $goal_diff
  ];

  // print the scraped team data
  print_r($team_data);
  print ("\n");
}

\DOM\HTMLDocument는 고급 쿼리 메서드를 제공하지 않습니다. 따라서 getElementsByTagName() 같은 메서드와 수동 반복에 의존해야 합니다.

사용된 메서드에 대한 설명은 다음과 같습니다:

  • getElementsByTagName(): 문서 내에서 지정된 tag(예: <table>, <tr>, <td>)의 모든 요소를 가져옵니다.
  • item(): getElementsByTagName()가 반환한 요소 목록에서 개별 요소를 반환합니다.
  • textContent: 요소의 원시 텍스트 콘텐츠를 제공하는 속성으로, 표시되는 데이터(팀 이름, 연도 등)를 추출할 수 있습니다.

또한 더 깔끔한 데이터를 위해 텍스트 콘텐츠의 앞뒤 공백을 제거하고자 trim()도 사용했습니다.

위 스니펫을 index.php에 추가하면 다음과 같은 결과가 출력됩니다:

Array
(
    [team] => Boston Bruins
    [year] => 1990
    [wins] => 44
    [losses] => 24
    [win_pct] => 0.55
    [goals_for] => 299
    [goals_against] => 264
    [goal_diff] => 35
)

// omitted for brevity...

Array
(
    [team] => Detroit Red Wings
    [year] => 1994
    [wins] => 33
    [losses] => 11
    [win_pct] => 0.688
    [goals_for] => 180
    [goals_against] => 117
    [goal_diff] => 63
) 

Approach #2: Using Simple HTML DOM Parser

Simple HTML DOM Parser는 HTML 콘텐츠를 쉽게 파싱하고 조작할 수 있게 해주는 경량 PHP 라이브러리입니다.

Step #1: Installation and Set Up

다음 명령으로 Composer를 통해 Simple HTML Dom Parser를 설치할 수 있습니다:

composer require voku/simple_html_dom

또는 simple_html_dom.php 파일을 수동으로 다운로드하여 프로젝트에 포함할 수도 있습니다.

그런 다음 index.php에서 다음 코드로 import 하십시오:

use voku\helper\HtmlDomParser;

Step #2: HTML Parsing

HTML string을 파싱하려면 file_get_html() 메서드를 사용하십시오:

$dom = HtmlDomParser::str_get_html($html);

index.html을 파싱하려면 대신 file_get_html()을 작성하십시오:

$dom = HtmlDomParser::file_get_html($str);

이렇게 하면 HTML 콘텐츠가 $dom 객체로 로드되며, DOM을 쉽게 탐색할 수 있습니다.

Step #3: Data Parsing

Simple HTML DOM Parser를 사용하여 HTML에서 하키 팀 데이터를 추출합니다:

// find all rows in the table
$rows = $dom->findMulti("table tr.team");

// loop through each row to extract the data
foreach ($rows as $row) {
  // extract data using CSS selectors
  $team_element = $row->findOne(".name");
  $team = trim($team_element->plaintext);

  $year_element = $row->findOne(".year");
  $year = trim($year_element->plaintext);

  $wins_element = $row->findOne(".wins");
  $wins = trim($wins_element->plaintext);

  $losses_element = $row->findOne(".losses");
  $losses = trim($losses_element->plaintext);

  $win_pct_element = $row->findOne(".pct");
  $win_pct = trim($win_pct_element->plaintext);

  $goals_for_element = $row->findOne(".gf");
  $goals_for = trim($goals_for_element->plaintext);

  $goals_against_element = $row->findOne(".ga");
  $goals_against = trim(string: $goals_against_element->plaintext);

  $goal_diff_element = $row->findOne(".diff");
  $goal_diff = trim(string: $goal_diff_element->plaintext);

  // create an array with the extracted team data
  $team_data = [
    "team" => $team,
    "year" => $year,
    "wins" => $wins,
    "losses" => $losses,
    "win_pct" => $win_pct,
    "goals_for" => $goals_for,
    "goals_against" => $goals_against,
    "goal_diff" => $goal_diff
  ];

  // print the scraped team data
  print_r($team_data);
  print("\n");
}

위에서 사용한 Simple HTML DOM Parser 기능은 다음과 같습니다:

  • findMulti(): 주어진 CSS selector로 식별되는 모든 요소를 선택합니다.
  • findOne(): 주어진 CSS selector와 매칭되는 첫 번째 요소를 찾습니다.
  • plaintext: HTML 요소 내부의 원시 텍스트 콘텐츠를 가져오는 속성입니다.

이번에는 더 포괄적이고 견고한 로직으로 CSS selector를 적용했습니다. 그러나 결과는 초기 PHP HTML 파싱 접근 방식과 동일합니다.

Approach #3: Using Symfony’s DomCrawler Component

Symfony의 DomCrawler 컴포넌트는 HTML 문서를 파싱하고 그로부터 데이터를 추출하는 쉬운 방법을 제공합니다.

Note: 이 컴포넌트는 Symfony framework의 일부이지만, 이 섹션에서처럼 단독으로도 사용할 수 있습니다.

Step #1: Installation and Set Up

다음 Composer 명령으로 Symfony의 DomCrawler 컴포넌트를 설치하십시오:

composer require symfony/dom-crawler

그런 다음 index.php 파일에서 import 하십시오:

use Symfony\Component\DomCrawler\Crawler;

Step #2: HTML Parsing

HTML string을 파싱하려면 html() 메서드로 Crawler 인스턴스를 생성하십시오:

$crawler = new Crawler($html);

파일을 파싱하려면 file_get_contents()를 사용하고 Crawler 인스턴스를 생성하십시오:

$crawler = new Crawler(file_get_contents("./index.html"));

위 라인은 HTML 콘텐츠를 $crawler 객체에 로드하며, 이는 데이터 탐색 및 추출을 위한 쉬운 메서드를 제공합니다.

Step #3: Data Parsing

DomCrawler 컴포넌트를 사용해 하키 팀 데이터를 추출합니다:

// select all rows within the table
$rows = $crawler->filter("table tr.team");

// loop through each row to extract the data
$rows->each(function ($row, $i) {
  // extract data using CSS selectors
  $team_element = $row->filter(".name");
  $team = trim($team_element->text());

  $year_element = $row->filter(".year");
  $year = trim($year_element->text());

  $wins_element = $row->filter(".wins");
  $wins = trim($wins_element->text());

  $losses_element = $row->filter(".losses");
  $losses = trim($losses_element->text());

  $win_pct_element = $row->filter(".pct");
  $win_pct = trim($win_pct_element->text());

  $goals_for_element = $row->filter(".gf");
  $goals_for = trim($goals_for_element->text());

  $goals_against_element = $row->filter(".ga");
  $goals_against = trim($goals_against_element->text());

  $goal_diff_element = $row->filter(".diff");
  $goal_diff = trim($goal_diff_element->text());

  // create an array with the extracted team data
  $team_data = [
    "team" => $team,
    "year" => $year,
    "wins" => $wins,
    "losses" => $losses,
    "win_pct" => $win_pct,
    "goals_for" => $goals_for,
    "goals_against" => $goals_against,
    "goal_diff" => $goal_diff
  ];

  // print the scraped team data
  print_r($team_data);
  print ("\n");
});

사용된 DomCrawler 메서드는 다음과 같습니다:

  • each(): 선택된 요소 목록을 반복하는 데 사용합니다.
  • filter(): CSS selector를 기반으로 요소를 선택합니다.
  • text(): 선택된 요소의 텍스트 콘텐츠를 추출합니다.

Parsing HTML in PHP: Comparison Table

아래 요약 표에서 여기서 살펴본 PHP HTML 파싱 3가지 접근 방식을 비교할 수 있습니다:

\DOM\HTMLDocument Simple HTML DOM Parser Symfony’s DomCrawler
Type 네이티브 PHP 컴포넌트 외부 라이브러리 Symfony 컴포넌트
GitHub Stars 880+ 4,000+
XPath Support ✔️ ✔️
CSS Selector Support ✔️ ✔️
Learning Curve 낮음 낮음~중간 중간
Simplicity of Use 중간 높음 높음
API 기본 풍부함 풍부함

Conclusion

이러한 솔루션은 동작하지만, 대상 웹 페이지가 렌더링을 위해 JavaScript에 의존하는 경우에는 효과적이지 않습니다. 이런 경우 위와 같은 단순 HTML 파싱 접근 방식만으로는 충분하지 않습니다. 대신, 고급 HTML 파싱 기능을 갖춘 완전한 기능의 scraping browser가 필요하며, 예를 들어 Scraping Browser와 같은 솔루션이 필요합니다.

HTML 파싱을 우회하고 즉시 구조화된 데이터에 접근하고 싶다면, 수백 개 웹사이트를 커버하는 바로 사용 가능한 datasets를 확인해 보십시오!

지금 Bright Data 계정을 생성하고 무료 체험으로 데이터 및 스크レイピング 솔루션을 테스트해 보십시오!

About

PHP에서 네이티브 DOM, Simple HTML DOM Parser, Symfony의 DomCrawler를 사용하여 HTML을 파싱하는 방법과 각 방법의 강점 및 사용 사례 비교.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published