정규 표현식을 사용하지 않는 경우 HTML 구문 분석은 어떻게 작동합니까?


96

나는 매일 HTML 문자열에서 무언가를 구문 분석하거나 추출하는 방법을 묻는 질문을 봅니다. 첫 번째 답변 / 주석은 항상 "분노를 느끼지 않도록 HTML 구문 분석에 RegEx를 사용하지 마십시오!"입니다. (마지막 부분은 때때로 생략됩니다).

이것은 나에게 다소 혼란 스럽습니다. 일반적으로 복잡한 문자열을 구문 분석하는 가장 좋은 방법은 정규식을 사용하는 것이라고 항상 생각했습니다. 그렇다면 HTML 파서는 어떻게 작동합니까? 정규식을 사용하여 구문 분석하지 않습니다.

정규식 사용에 대한 한 가지 특정 인수는 항상 구문 분석 대안이있는 것은 아니라는 것입니다 (예 : DOMDocument가 보편적으로 사용 가능한 옵션이 아닌 JavaScript). 예를 들어 jQuery는 정규식을 사용하여 HTML 문자열을 DOM 노드로 변환하여 잘 관리하는 것처럼 보입니다.

이것이 CW인지 아닌지 확실하지 않은 것은 내가 답변을 받고 싶은 진정한 질문이며 실제로 토론 스레드가 될 의도가 아닙니다.


파싱 ​​및 html 파싱을 추가하기 위해 태그를 다시 지정했습니다. @Andy E, 괜찮기를 바랍니다. 도움이 될 것이라고 생각했습니다.
JXG

@JXG : 괜찮습니다. 감사합니다 :-)
Andy E

답변:


65

일반적으로 토크 나이저를 사용합니다. 초안 HTML5 사양에는 "실제 HTML"처리를 위한 광범위한 알고리즘 이 있습니다.


1
"이러한 경우를 처리하기 위해 파서에는 처음에 0으로 설정해야하는 스크립트 중첩 수준과 처음에 false로 설정해야하는 파서 일시 중지 플래그가 있습니다." -즉, 직접 반복해야하며 많은 사용자 정의 논리가 있어야합니다. P
Timothy Khouri

1
찬성. 일부 기술 대신 알고리즘 복잡성을 강조하는 것이 좋습니다.
Arnis Lapsa

1
많은 사용자 정의 논리로 직접 반복하는 것은 좋은 생각이 아닙니다. 가능하면 표준 알고리즘을 지원하는 라이브러리를 사용하십시오. 예 : search.cpan.org/~tobyink/HTML-HTML5-Parser-0.03/lib/HTML/HTML5/… / code.google.com/p/html5lib
Quentin

8
HTML 파서의 주된 문제는 오류가 발생했을 때 "Parse error"를 뱉어 내고 그대로 두는 것이 좋지 않다는 것입니다. 쿼크 모드에 들어가서 일치하지 않는 태그, [{]} 스타일 인터레이스 및 모든 종류의 기이함을 포함하여 엉망진창에서 가능한 한 최선을 다해 최선을 다해 결과를 최대한 멋지게 보이게 만들고 피할 수없는 결과를 얻으려고합니다. 실패는 가장 고통스럽지 않습니다. 이것은 정규식으로 할 수있는 일이 아닙니다.
SF.

7
@Timothy K : '참고 :이 알고리즘으로 인해 요소가 부모를 변경하는 방식 때문에 "입양 대행 알고리즘"이라고 불립니다 ( "근친상간 알고리즘"을 포함하는 잘못 중첩 된 콘텐츠를 처리하기위한 다른 가능한 알고리즘과는 대조적으로, "비밀 사건 알고리즘"및 "하이젠 버그 알고리즘"). '
JXG

133

그렇다면 HTML 파서는 어떻게 작동합니까? 정규식을 사용하여 구문 분석하지 않습니까?

음 ... 아니.

당신의 두뇌에서 계산 이론으로 되돌아 가면, 하나를 수강했거나, 컴파일러 코스 또는 이와 유사한 것을 수강했다면, 다른 종류의 언어와 계산 모델이 있다는 것을 기억할 것입니다. 모든 세부 사항을 다룰 자격은 없지만 몇 가지 주요 사항을 함께 검토 할 수 있습니다.

(이러한 목적을위한) 가장 간단한 유형의 언어 및 계산은 일반 언어입니다. 정규 표현식으로 생성 할 수 있으며 유한 오토마타로 인식 할 수 있습니다. 기본적으로 이는 이러한 언어의 "파싱"문자열이 상태를 사용하지만 보조 메모리는 사용하지 않음을 의미합니다. HTML은 확실히 일반 언어가 아닙니다. 생각해 보면 태그 목록이 임의로 깊이 중첩 될 수 있습니다. 예를 들어 테이블에는 테이블이 포함될 수 있고 각 테이블에는 많은 중첩 태그가 포함될 수 있습니다. 정규식을 사용하면 한 쌍의 태그를 선택할 수 있지만 임의로 중첩 된 것은 없습니다.

규칙적이지 않은 고전적인 단순 언어는 괄호와 올바르게 일치합니다. 시도해보십시오. 항상 작동하는 정규식 (또는 유한 오토 마톤)을 만들 수 없습니다. 중첩 깊이를 추적하려면 메모리가 필요합니다.

메모리 용 스택이있는 상태 머신은 컴퓨팅 모델의 다음 강점입니다. 이를 푸시 다운 자동화라고하며 문맥없는 문법에 의해 생성 된 언어를 인식합니다. 여기에서 정확하게 일치하는 괄호를 인식 할 수 있습니다. 실제로 스택은이를위한 완벽한 메모리 모델입니다.

이게 HTML에 충분할까요? 안타깝게도 아닙니다. 실제로 모든 태그가 항상 완벽하게 정렬되는 엄청나게 신중하게 검증 된 XML의 경우 일 수 있습니다. 실제 HTML에서는 <b><i>wow!</b></i>. 이것은 분명히 중첩되지 않으므로 올바르게 구문 분석하기 위해 스택은 충분히 강력하지 않습니다.

다음 수준의 계산은 일반 문법에 의해 생성되고 튜링 머신에서 인식되는 언어입니다. 이것은 일반적으로 가장 강력한 계산 모델로 받아 들여집니다. 보조 메모리가있는 상태 머신은 어디에서나 메모리를 수정할 수 있습니다. 이것이 프로그래밍 언어가 할 수있는 일입니다. 이것은 HTML이 존재하는 복잡성의 수준입니다.

여기에 모든 것을 한 문장으로 요약하려면 일반 HTML을 구문 분석하려면 정규식이 아닌 실제 프로그래밍 언어가 필요합니다.

HTML은 다른 언어가 파싱되는 것과 같은 방식으로 파싱됩니다 : 렉싱과 파싱. 렉싱 단계는 개별 문자의 흐름을 의미있는 토큰으로 나눕니다. 구문 분석 단계는 상태와 메모리를 사용하여 토큰을 논리적으로 일관된 문서로 조합하여 처리 할 수 ​​있습니다.


22

정규식은 파서의 한 형태 일뿐입니다. 정직한 HTML 파서는 재귀 하강 , 예측 및 텍스트를 적절하게 해석하기 위해 여러 다른 기술을 사용하여 정규식으로 표현할 수있는 것보다 훨씬 더 복잡 합니다. 정말로 들어가고 싶다면 lex & yacc 및 유사한 도구를 확인할 수 있습니다 .

HTML 구문 분석에 정규 표현식을 사용하는 것에 대한 금지는 아마도 " HTML 구문 분석을 위해 순진한 정규 표현식을 사용하지 마십시오 ..." (분노를 느끼지 않도록) "... 그리고 결과를주의해서 처리하십시오."와 같이 더 정확하게 작성되어야합니다 . 특정 목표의 경우 정규식이 완벽하게 적절할 수 있지만 정규식의 제한 사항을 인식하고 구문 분석하는 텍스트의 소스에 적합한만큼 신중해야합니다 (예 : 사용자 입력, 실제로 매우 조심하십시오).


+1, 좋은 답변입니다. 나는 내가 HTML을 제어하지 않았을 때에도 정규식을 사용했지만 공개적으로 출시 된 애플리케이션에서는 사용하지 않았다는 것을 인정해야합니다. 순진했기 때문에 나도 "분노를 느껴라"를했다. 하지만 그건 오래 전 일입니다 :-)
Andy E

6

HTML 구문 분석은 선형 텍스트를 트리 구조로 변환하는 것입니다. 정규식은 일반적으로 트리 구조를 처리 할 수 ​​없습니다. 다음 토큰을 얻기 위해 각 지점에서 필요한 정규식은 항상 변경됩니다. 파서에서 정규식을 사용할 수 있지만 가능한 각 구문 분석 상태에 대해 전체 정규식 배열이 필요합니다.


2

100 % 솔루션을 원하는 경우 : HTML을 문자별로 반복하는 사용자 정의 코드를 작성해야하며 현재 노드를 중지하고 시작해야하는지 여부를 결정하기위한 엄청난 양의 논리가 필요합니다. 다음.

그 이유는 이것이 유효한 HTML이기 때문입니다.

<ul>
<li>One
<li>Two
<li>Three
</ul>

그러나 이것은 다음과 같습니다.

<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>

"90 % 솔루션"에 동의하는 경우 : 그러면 XML 파서를 사용하여 문서를로드하는 것이 좋습니다. 또는 Regex를 사용합니다 (하지만 콘텐츠의 마스터라면 xml이 더 쉽습니다).


4
XML 파서는 1 % 솔루션과 비슷합니다. 잘 구성된 XML 인 HTML 문서의 수는 적습니다.
Quentin

4
예, 그렇습니다. 스트리밍을 시도 할 수 있으므로 문자 그대로 "문자 별"을 사용하지 마십시오. 하지만 내 요점은 자신의 파서를 작성해야한다는 것입니다. 초보 프로그래머는 그런 종류의 코드를 작성하는 데 익숙하지 않습니다 ... 우리는 "HtmlDocumentUtility.Load"와 같은 것에 익숙합니다. :)
Timothy Khouri

4
@Andy E : 정규식은 마법이 아닙니다. 또한 다른 종류의 구문 분석 또는 도대체 다른 문자열 함수처럼 문자 단위로 작동합니다.
Bart van Heukelom 2010 년

1
BTW : 첫 번째 예는 "반 유효 HTML"이 아닙니다. 실제로 유효한 HTML 4.01 Strict입니다. 예를 들어 W3C 유효성 검사기를 사용하여이를 확인할 수 있습니다. 닫는 태그는 <li>에 대해 공식적으로 선택 사항입니다 (HTML 4 사양 참조).
sleske 2010 년

2
@Bart : 좋은 지적, 때때로 내 두뇌는 모든 논리를 잊고 모든 것이 마법으로 작동한다고 생각합니다.
Andy E
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.