HTML parses ทำงานอย่างไรหากไม่ได้ใช้ regexp


96

ฉันเห็นคำถามทุกวันเพื่อถามวิธีแยกวิเคราะห์หรือดึงข้อมูลบางอย่างจากสตริง HTML และคำตอบ / ความคิดเห็นแรกคือ "อย่าใช้ RegEx เพื่อแยกวิเคราะห์ HTML เกรงว่าคุณจะรู้สึกโกรธ!" (บางครั้งส่วนสุดท้ายจะถูกละไว้)

สิ่งนี้ค่อนข้างสับสนสำหรับฉันฉันคิดเสมอว่าโดยทั่วไปวิธีที่ดีที่สุดในการแยกวิเคราะห์สตริงที่ซับซ้อนคือการใช้นิพจน์ทั่วไป โปรแกรมแยกวิเคราะห์ HTML ทำงานอย่างไร? มันไม่ใช้นิพจน์ทั่วไปในการแยกวิเคราะห์

อาร์กิวเมนต์เฉพาะสำหรับการใช้นิพจน์ทั่วไปคือไม่มีทางเลือกในการแยกวิเคราะห์เสมอไป (เช่น JavaScript โดยที่ DOMDocument ไม่ใช่ตัวเลือกที่ใช้ได้ทั่วไป) ตัวอย่างเช่น jQuery ดูเหมือนจะจัดการได้ดีโดยใช้ regex เพื่อแปลงสตริง HTML เป็นโหนด DOM

ไม่แน่ใจว่า CW หรือไม่นี่เป็นคำถามที่แท้จริงที่ฉันต้องการคำตอบและไม่ได้ตั้งใจให้เป็นกระทู้สนทนา


ติดแท็กใหม่เพื่อเพิ่มการแยกวิเคราะห์และการแยกวิเคราะห์ html - @Andy E ฉันหวังว่าคุณจะพอใจ - ฉันคิดว่ามันจะเป็นประโยชน์
JXG

@JXG: สบายดีกับฉันขอบคุณ :-)
Andy E

คำตอบ:


65

โดยปกติจะใช้โทเค็นเซอร์ ข้อกำหนด HTML5ฉบับร่างมีอัลกอริทึมที่ครอบคลุมสำหรับจัดการ "HTML ในโลกแห่งความเป็นจริง"


1
พบ ... เพื่ออ้างถึง "ในการจัดการกรณีเหล่านี้ตัวแยกวิเคราะห์จะมีระดับการซ้อนสคริปต์ซึ่งในตอนแรกจะต้องตั้งค่าเป็นศูนย์และแฟล็กหยุดตัวแยกวิเคราะห์ซึ่งต้องตั้งค่าเป็นเท็จในตอนแรก" - กล่าวอีกนัยหนึ่งคุณต้องทำซ้ำด้วยตัวเองและมีตรรกะที่กำหนดเองมากมาย: 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 คือเมื่อพบข้อผิดพลาดคุณไม่โอเคที่จะพ่น "แยกวิเคราะห์ข้อผิดพลาด" และปล่อยไว้อย่างนั้น คุณเข้าสู่โหมดนิสัยใจคอและพยายามทำให้ดีที่สุดเท่าที่จะทำได้จากความยุ่งเหยิงที่คุณพบรวมถึงแท็กที่ไม่ตรงกันการสอดประสานสไตล์ [{]} และความแปลกประหลาดทุกประเภทโดยพยายามทำให้ผลลัพธ์ออกมาดีที่สุดเท่าที่จะทำได้และหลีกเลี่ยงไม่ได้ ล้มเหลวเจ็บปวดน้อยที่สุด ... นี่ไม่ใช่สิ่งที่คุณสามารถทำได้กับ regexes
SF.

7
@Timothy K: 'หมายเหตุ: เนื่องจากวิธีที่อัลกอริทึมนี้ทำให้องค์ประกอบเปลี่ยนผู้ปกครองจึงได้รับการขนานนามว่า "อัลกอริทึมหน่วยงานการรับเลี้ยงบุตรบุญธรรม" (ตรงกันข้ามกับอัลกอริทึมอื่น ๆ ที่เป็นไปได้ในการจัดการกับเนื้อหาที่ตรวจสอบผิดซึ่งรวมถึง "อัลกอริทึมการร่วมประเวณีระหว่างพี่น้อง" "อัลกอริทึมเรื่องความลับ" และ "อัลกอริทึมไฮเซนเบิร์ก")
JXG

133

โปรแกรมแยกวิเคราะห์ HTML ทำงานอย่างไร? มันไม่ใช้นิพจน์ทั่วไปในการแยกวิเคราะห์?

ดีไม่

หากคุณย้อนกลับไปในสมองของคุณถึงทฤษฎีของหลักสูตรการคำนวณถ้าคุณเรียนหลักสูตรหนึ่งหรือหลักสูตรคอมไพเลอร์หรืออะไรที่คล้ายกันคุณอาจจำได้ว่ามีภาษาและรูปแบบการคำนวณหลายประเภท ฉันไม่มีคุณสมบัติที่จะลงรายละเอียดทั้งหมด แต่ฉันสามารถตรวจสอบประเด็นสำคัญบางประการกับคุณได้

ประเภทของภาษาและการคำนวณที่ง่ายที่สุด (สำหรับวัตถุประสงค์เหล่านี้) คือภาษาปกติ สิ่งเหล่านี้สามารถสร้างขึ้นด้วยนิพจน์ทั่วไปและรับรู้ด้วยออโตมาตา จำกัด โดยทั่วไปนั่นหมายความว่าสตริง "การแยกวิเคราะห์" ในภาษาเหล่านี้ใช้สถานะ แต่ไม่ใช่หน่วยความจำเสริม HTML ไม่ใช่ภาษาปกติอย่างแน่นอน หากคุณคิดเช่นนั้นรายการแท็กสามารถซ้อนกันได้ตามอำเภอใจ ตัวอย่างเช่นตารางสามารถมีตารางและแต่ละตารางสามารถมีแท็กที่ซ้อนกันจำนวนมาก ด้วยนิพจน์ทั่วไปคุณอาจสามารถเลือกคู่ของแท็กได้ แต่จะไม่มีอะไรซ้อนกันโดยพลการ

ภาษาคลาสสิกธรรมดาที่ไม่ปกติจะจับคู่วงเล็บอย่างถูกต้อง ลองทำตามที่คุณทำได้คุณจะไม่สามารถสร้างนิพจน์ทั่วไป (หรือ จำกัด ออโตเมตัน) ที่จะใช้งานได้ตลอดไป คุณต้องมีหน่วยความจำเพื่อติดตามความลึกของการซ้อน

เครื่องสเตทที่มีสแต็กสำหรับหน่วยความจำเป็นจุดแข็งถัดไปของโมเดลการคำนวณ สิ่งนี้เรียกว่าระบบอัตโนมัติแบบกดลงและจดจำภาษาที่สร้างขึ้นโดยไวยากรณ์ที่ไม่มีบริบท ที่นี่เราสามารถจดจำวงเล็บที่ตรงกันได้อย่างถูกต้อง - สแต็กเป็นโมเดลหน่วยความจำที่สมบูรณ์แบบสำหรับมัน

นี่ดีพอสำหรับ HTML หรือไม่? น่าเศร้าที่ไม่ อาจเป็นไปได้สำหรับ XML ที่ตรวจสอบความถูกต้องอย่างรอบคอบซึ่งแท็กทั้งหมดจะเรียงกันอย่างสมบูรณ์แบบเสมอ ใน HTML <b><i>wow!</b></i>โลกแห่งความจริงที่คุณสามารถหาได้ง่ายตัวอย่างเช่น เห็นได้ชัดว่าสิ่งนี้ไม่ซ้อนกันดังนั้นเพื่อที่จะแยกวิเคราะห์ได้อย่างถูกต้องสแต็กจึงไม่ทรงพลังเพียงพอ

ระดับถัดไปของการคำนวณคือภาษาที่สร้างขึ้นโดยไวยากรณ์ทั่วไปและได้รับการยอมรับจากเครื่องทัวริง สิ่งนี้เป็นที่ยอมรับกันโดยทั่วไปว่าเป็นรูปแบบการคำนวณที่แข็งแกร่งที่สุดที่มี - เครื่องสถานะพร้อมหน่วยความจำเสริมซึ่งหน่วยความจำสามารถแก้ไขได้ทุกที่ นี่คือสิ่งที่ภาษาโปรแกรมสามารถทำได้ นี่คือระดับความซับซ้อนที่ HTML อาศัยอยู่

เพื่อสรุปทุกอย่างในประโยคเดียว: ในการแยกวิเคราะห์ HTML ทั่วไปคุณต้องมีภาษาโปรแกรมจริงไม่ใช่นิพจน์ทั่วไป

HTML จะแยกวิเคราะห์ในลักษณะเดียวกับที่ภาษาอื่น ๆ ในการแยกวิเคราะห์ ได้แก่ การแยกวิเคราะห์และการแยกวิเคราะห์ ขั้นตอนการอ่านจะแบ่งสตรีมของอักขระแต่ละตัวออกเป็นโทเค็นที่มีความหมาย ขั้นตอนการแยกวิเคราะห์จะประกอบโทเค็นโดยใช้สถานะและหน่วยความจำเป็นเอกสารที่สอดคล้องกันอย่างมีเหตุผลซึ่งสามารถดำเนินการได้


22

นิพจน์ทั่วไปเป็นเพียงรูปแบบหนึ่งของตัวแยกวิเคราะห์ โปรแกรมแยกวิเคราะห์ HTML ที่ซื่อสัตย์ต่อความดีจะมีความซับซ้อนมากกว่าที่จะแสดงใน regexes อย่างมีนัยสำคัญโดยใช้การสืบเชื้อสายซ้ำการคาดการณ์และเทคนิคอื่น ๆ อีกมากมายเพื่อตีความข้อความอย่างถูกต้อง หากคุณต้องการเข้าใช้งานจริงๆคุณอาจตรวจสอบlex & yaccและเครื่องมือที่คล้ายกัน

ข้อห้ามในการใช้ regexes สำหรับการแยกวิเคราะห์ HTML ควรจะเขียนให้ถูกต้องมากกว่านี้เช่น "อย่าใช้นิพจน์ทั่วไปที่ไร้เดียงสาในการแยกวิเคราะห์ HTML ... " (เกรงว่าคุณจะรู้สึกโกรธ) "... และปฏิบัติต่อผลลัพธ์ด้วยความระมัดระวัง" สำหรับเป้าหมายเฉพาะบางประการ regex อาจจะเพียงพออย่างสมบูรณ์ แต่คุณต้องระมัดระวังให้มากเพื่อตระหนักถึงข้อ จำกัด ของ regex ของคุณและระมัดระวังตามความเหมาะสมกับแหล่งที่มาของข้อความที่คุณกำลังแยกวิเคราะห์ (เช่นหากเป็น การป้อนข้อมูลของผู้ใช้ระวังให้มากจริงๆ)


+1 เป็นคำตอบที่ดี ฉันต้องยอมรับว่าฉันเคยใช้ regexes มาก่อนแม้ว่าฉันจะไม่ได้ควบคุม 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% เอกสาร HTML ที่มีรูปแบบ XML มีจำนวนน้อย
Quentin

4
ใช่พวกเขาทำ ... อย่าใช้ "อักขระทีละอักขระ" อย่างแท้จริงเพราะคุณสามารถพยายามสตรีมสิ่งต่างๆ แต่ประเด็นของฉันคือคุณต้องเขียนโปรแกรมแยกวิเคราะห์ของคุณเอง โปรแกรมเมอร์ยุคใหม่ไม่คุ้นเคยกับการเขียนโค้ดแบบนั้น ... เราคุ้นเคยกับ "HtmlDocumentUtility.Load" และอะไรทำนองนั้น :)
Timothy Khouri

4
@Andy E: Regexes ไม่ใช่เวทมนตร์ แต่ยังทำงานทีละตัวอักษรเช่นเดียวกับการแยกวิเคราะห์ประเภทอื่น ๆ หรือฟังก์ชั่นสตริงอื่น ๆ
Bart van Heukelom

1
BTW: ตัวอย่างแรกของคุณไม่ใช่แค่ "HTML กึ่งใช้ได้" เป็น HTML 4.01 Strict ที่ถูกต้องจริงๆ คุณสามารถใช้เช่นตัวตรวจสอบ W3C เพื่อตรวจสอบสิ่งนี้ แท็กปิดเป็นทางเลือกอย่างเป็นทางการสำหรับ <li> (ดูข้อกำหนด HTML 4)
sleske

2
@Bart: จุดดีบางครั้งสมองของฉันลืมตรรกะทั้งหมดและคิดว่าสิ่งต่าง ๆ ทำงานโดยใช้เวทมนตร์
Andy E
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.