ข้อสังเกตที่น่าสนใจ! ฉันใช้เวลาสองสามชั่วโมงที่ผ่านมาเพื่อสืบสวนเรื่องนี้ และปรากฎว่า มีอะไรมากกว่าที่ตาเห็น
หากคุณมาจาก CSS คุณอาจคาดหวังที่จะเขียน a::text
ในลักษณะเดียวกับที่คุณเขียน a::first-line
, a::first-letter
, a::before
หรือ a::after
ไม่มีความประหลาดใจที่นั่น
ในทางกลับกัน ไวยากรณ์ตัวเลือกมาตรฐานจะแนะนำว่า a ::text
ตรงกับองค์ประกอบหลอก ::text
ของ ลูกหลาน ขององค์ประกอบ a
ทำให้เทียบเท่ากับ a *::text
อย่างไรก็ตาม .product-list-product-wrapper .product-name a
ไม่มีองค์ประกอบลูก ดังนั้น a ::text
ไม่ควรตรงกับสิ่งใดเลยตามสิทธิ์ ความจริงที่ว่ามันตรงกันแสดงว่า Scrapy ไม่ได้เป็นไปตามไวยากรณ์
Scrapy ใช้ Parsel (ขึ้นอยู่กับ cssselect) เพื่อแปลตัวเลือกเป็น XPath ซึ่งเป็นที่มาของ ::text
ด้วยเหตุนี้ เรามาดูกันว่า Parsel ใช้งาน ::text
อย่างไร:
>>> from parsel import css2xpath
>>> css2xpath('a::text')
'descendant-or-self::a/text()'
>>> css2xpath('a ::text')
'descendant-or-self::a/descendant-or-self::text()'
ดังนั้น เช่นเดียวกับ cssselect สิ่งใดก็ตามที่ตามหลังตัวรวมลูกหลานจะถูกแปลเป็นแกน descendant-or-self
แต่เนื่องจากโหนดข้อความเป็นลูกที่เหมาะสมของโหนดองค์ประกอบใน DOM ::text
จึงถือเป็นโหนดแบบสแตนด์อโลนและแปลงโดยตรงเป็น text()
ซึ่งด้วย descendant-or-self
axis จับคู่โหนดข้อความใดๆ ที่สืบทอดมาจากองค์ประกอบ a
เช่นเดียวกับที่ a/text()
จับคู่โหนดข้อความ ลูก ขององค์ประกอบ a
(ลูกก็เป็นผู้สืบทอดเช่นกัน)
จริงๆ แล้วสิ่งนี้จะเกิดขึ้นแม้ว่าคุณจะเพิ่ม *
ที่ชัดเจนให้กับตัวเลือก:
>>> css2xpath('a *::text')
'descendant-or-self::a/descendant-or-self::text()'
อย่างไรก็ตาม การใช้แกน descendant-or-self
หมายความว่า a ::text
สามารถจับคู่โหนดข้อความทั้งหมดในองค์ประกอบ a
ได้ รวมถึงโหนดในองค์ประกอบอื่นๆ ที่ซ้อนอยู่ภายใน a
ในตัวอย่างต่อไปนี้ a ::text
จะจับคู่โหนดข้อความสองโหนด: 'Link '
ตามด้วย 'text'
:
<a href="https://example.com">Link <span>text</span></a>
ดังนั้นแม้ว่าการใช้ ::text
ของ Scrapy จะเป็นการละเมิดไวยากรณ์ Selectors อย่างร้ายแรง แต่ดูเหมือนว่าจะทำในลักษณะนี้โดยตั้งใจอย่างมาก
อันที่จริงแล้ว องค์ประกอบหลอกอื่น ๆ ของ Scrapy ::attr()
1 มีพฤติกรรมคล้ายกัน ตัวเลือกต่อไปนี้ทั้งหมดตรงกับโหนดแอตทริบิวต์ id
ที่เป็นขององค์ประกอบ div
เมื่อไม่มีองค์ประกอบสืบทอด:
>>> css2xpath('div::attr(id)')
'descendant-or-self::div/@id'
>>> css2xpath('div ::attr(id)')
'descendant-or-self::div/descendant-or-self::*/@id'
>>> css2xpath('div *::attr(id)')
'descendant-or-self::div/descendant-or-self::*/@id'
... แต่ div ::attr(id)
และ div *::attr(id)
จะจับคู่โหนดแอตทริบิวต์ id
ทั้งหมดภายในลูกหลานของ div
พร้อมกับแอตทริบิวต์ id
ของตัวเอง เช่นในตัวอย่างต่อไปนี้:
<div id="parent"><p id="child"></p></div>
แน่นอนว่า นี่เป็นกรณีการใช้งานที่มีความเป็นไปได้น้อยกว่ามาก ดังนั้นจึงต้องสงสัยว่านี่เป็นผลข้างเคียงโดยไม่ได้ตั้งใจจากการใช้งาน ::text
หรือไม่
เปรียบเทียบตัวเลือกองค์ประกอบหลอกกับตัวเลือกที่ใช้แทนตัวเลือกง่ายๆ สำหรับองค์ประกอบหลอก:
>>> css2xpath('a [href]')
'descendant-or-self::a/descendant-or-self::*/*[@href]'
วิธีนี้จะแปลตัวรวมลูกหลานเป็น descendant-or-self::*/*
อย่างถูกต้องด้วยแกน child
โดยนัยเพิ่มเติม เพื่อให้แน่ใจว่าไม่มีการทดสอบเพรดิเคต [@href]
บนองค์ประกอบ a
หากคุณยังใหม่กับ XPath, Selectors หรือแม้แต่ Scrapy ทั้งหมดนี้อาจดูสับสนและล้นหลามมาก ต่อไปนี้เป็นบทสรุปว่าเมื่อใดควรใช้ตัวเลือกหนึ่งเหนืออีกตัวเลือกหนึ่ง:
ใช้ a::text
หากองค์ประกอบ a
ของคุณมีเพียงข้อความ หรือหากคุณสนใจเฉพาะโหนดข้อความระดับบนสุดขององค์ประกอบ a
นี้ ไม่ใช่องค์ประกอบที่ซ้อนกัน
ใช้ a ::text
หากองค์ประกอบ a
ของคุณมีองค์ประกอบที่ซ้อนกัน และคุณต้องการแยกโหนดข้อความทั้งหมดภายในองค์ประกอบ a
นี้
แม้ว่าคุณจะสามารถใช้ a ::text
ได้หากองค์ประกอบ a
ของคุณมีเพียงข้อความ แต่ไวยากรณ์ขององค์ประกอบนั้นทำให้เกิดความสับสน ดังนั้นเพื่อความสอดคล้องกัน ให้ใช้ a::text
แทน
1 ในบันทึกที่น่าสนใจ ::attr()
ปรากฏใน (ละทิ้ง ณ ปี 2021) ข้อมูลจำเพาะของตัวเลือกที่ไม่ใช่องค์ประกอบ โดยที่คุณคาดว่าตัวเลือกจะทำงานสอดคล้องกับไวยากรณ์ของตัวเลือก ซึ่งทำให้พฤติกรรมของตัวเลือกใน Scrapy ไม่สอดคล้องกับข้อมูลจำเพาะ ::text
ในทางกลับกันหายไปจากข้อมูลจำเพาะอย่างเห็นได้ชัด จากคำตอบนี้ ฉันคิดว่าคุณสามารถเดาได้อย่างสมเหตุสมผลว่าทำไม
person
BoltClock
schedule
01.02.2018
::text
pseudo-element ได้หรือไม่? ฉันไม่พบสิ่งใดเกี่ยวกับการมีอยู่ของมันเลย... - person Andersson   schedule 01.02.2018::text
ไม่ว่าจะเป็นไลบรารีBeautifulSoup
หรือlxml
อย่างไรก็ตาม เมื่อพูดถึงการแยกวิเคราะห์แบบเดียวกันโดยใช้ scrapy นี่ถือเป็นข้อบังคับ::text
เพื่อรับข้อความ คุณรู้เรื่องนี้เป็นอย่างดี ประเด็นคือ: ฉันไม่พบความแตกต่างมากนักในการใช้ช่องว่างระหว่างนั้นเพื่อแยกวิเคราะห์ข้อความจากองค์ประกอบบางอย่าง แต่ควรมีdo's and don'ts
เกี่ยวกับการใช้งาน นั่นคือสิ่งที่ผมอยากจะรู้ - person SIM   schedule 01.02.2018::text
:) ฉันไม่เคยได้ยินเกี่ยวกับองค์ประกอบหลอกนี้เลย IMHO ฉันไม่คิดว่าพื้นที่จะสร้างความแตกต่างในกรณีของคุณได้เลย ... นอกจากนี้ฉันไม่คิดว่าคำถามนี้สมควรได้รับการลงคะแนน :) - person Andersson   schedule 01.02.2018