Perbedaan antara penyeleksi Scrapy a::text dan ::text

Saya telah membuat scraper untuk mengambil beberapa nama produk dari halaman web. Ini bekerja dengan lancar. Saya telah menggunakan pemilih CSS untuk melakukan pekerjaan itu. Namun, satu-satunya hal yang saya tidak mengerti adalah perbedaan antara penyeleksi a::text dan a ::text (jangan mengabaikan spasi antara a dan ::text pada penyeleksi terakhir). Saat saya menjalankan skrip, saya mendapatkan hasil yang sama persis, apa pun pemilih yang saya pilih.

import requests
from scrapy import Selector

res = requests.get("https://www.kipling.com/uk-en/sale/type/all-sale/?limit=all#")
sel = Selector(res)
for item in sel.css(".product-list-product-wrapper"):
    title = item.css(".product-name a::text").extract_first().strip()
    title_ano = item.css(".product-name a ::text").extract_first().strip()
    print("Name: {}\nName_ano: {}\n".format(title,title_ano))

Seperti yang Anda lihat, title dan title_ano berisi pemilih yang sama, kecuali spasi di yang terakhir. Meski begitu, hasilnya selalu sama.

Pertanyaan saya: apakah ada perbedaan mendasar antara keduanya, dan kapan saya harus menggunakan yang pertama dan kapan yang terakhir?


person SIM    schedule 01.02.2018    source sumber
comment
Bagaimana kasus penggunaan ini? Apakah Anda hanya bertanya tentang sintaks CSS?   -  person tripleee    schedule 01.02.2018
comment
Apakah ini jawabannya @ tripleee?   -  person SIM    schedule 01.02.2018
comment
Tidak, jawaban adalah apa yang kami posting di kotak besar di bawah dengan tombol Posting Jawaban Anda. Apa yang saya posting adalah komentar. Ini tidak berisi upaya apa pun untuk menjawab, ini meminta Anda untuk mengklarifikasi pertanyaan Anda -- idealnya, edit agar memiliki jawaban judul yang lebih baik, deskripsi masalah yang lebih baik, dan tag yang sesuai.   -  person tripleee    schedule 01.02.2018
comment
Bagian mana dari uraian saya yang tidak jelas? Tag mana yang saya pilih yang tidak saya gunakan di scraper saya? Tapi aku akan mengedit judulnya.   -  person SIM    schedule 01.02.2018
comment
@ novice-coder , dapatkah Anda membagikan referensi ke ::text elemen semu? Saya tidak dapat menemukan apa pun tentang keberadaannya...   -  person Andersson    schedule 01.02.2018
comment
Terima kasih Pak Andersson, atas komentar Anda. Pada dasarnya, ketika saya mengurai teks apa pun dari beberapa elemen menggunakan pemilih css, saya tidak memerlukan penggunaan ::text, baik itu perpustakaan BeautifulSoup atau lxml. Namun, jika ingin mengurainya menggunakan scrapy maka ini wajib ::text untuk mendapatkan teksnya. Anda tahu betul hal itu. Intinya adalah: Saya tidak menemukan banyak perbedaan menggunakan spasi di antaranya untuk mengurai teks apa pun dari beberapa elemen tetapi harus ada do's and don'ts tentang penggunaannya. Itulah yang ingin saya ketahui.   -  person SIM    schedule 01.02.2018
comment
@ novice-coder, saya tidak menggunakan Scrapy, jadi sebenarnya saya tidak tahu cara mendapatkan teks dari node dengan ::text :) Saya hanya belum pernah mendengar tentang elemen semu ini. IMHO Saya tidak berpikir bahwa ruang dapat membuat perbedaan apa pun dalam kasus Anda... Saya juga tidak berpikir bahwa pertanyaan ini pantas mendapat suara negatif :)   -  person Andersson    schedule 01.02.2018
comment
@tripleee: Selain judul, dalam hal apa deskripsi masalahnya tidak jelas (apakah ::text dan a::text secara fungsional setara, jika tidak, apa perbedaannya dan apa, ahem, kasus penggunaan untuk masing-masing) atau tag tidak cocok (pertanyaannya adalah tentang penyeleksi yang digunakan oleh pustaka web scraping Python yang disebut Scrapy)?   -  person BoltClock    schedule 01.02.2018
comment
Dengan judul yang diperbaiki dan klarifikasi di sini di komentar, saya rasa ini tidak lagi menjadi tidak jelas, dan saya telah mencabut suara saya untuk menutupnya karena tidak jelas. Terima kasih atas pingnya.   -  person tripleee    schedule 01.02.2018
comment
@tripleee: Jangan khawatir. Saya akhirnya meluangkan banyak waktu dan penelitian untuk menjawab pertanyaan ini, jadi saya pikir saya akan menyempurnakan pertanyaan itu lagi.   -  person BoltClock    schedule 01.02.2018


Jawaban (1)


Pengamatan yang menarik! Saya menghabiskan beberapa jam terakhir untuk menyelidiki hal ini dan ternyata, ada lebih dari yang terlihat.

Jika Anda berasal dari CSS, Anda mungkin berharap untuk menulis a::text dengan cara yang sama seperti Anda menulis a::first-line, a::first-letter, a::before atau a::after. Tidak ada kejutan di sana.

Di sisi lain, sintaksis pemilih standar akan menyarankan bahwa a ::text cocok dengan elemen semu ::text dari keturunan elemen a, sehingga setara dengan a *::text. Namun, .product-list-product-wrapper .product-name a tidak memiliki elemen turunan apa pun, jadi, a ::text seharusnya tidak cocok dengan apa pun. Fakta bahwa itu cocok menunjukkan bahwa Scrapy tidak mengikuti tata bahasanya.

Scrapy menggunakan Parsel (berbasis cssselect) untuk menerjemahkan penyeleksi ke XPath, dari situlah ::text berasal. Dengan mengingat hal tersebut, mari kita periksa bagaimana Parsel mengimplementasikan ::text:

>>> from parsel import css2xpath
>>> css2xpath('a::text')
'descendant-or-self::a/text()'
>>> css2xpath('a ::text')
'descendant-or-self::a/descendant-or-self::text()'

Jadi, seperti cssselect, apa pun yang mengikuti kombinator turunan diterjemahkan ke dalam sumbu descendant-or-self, tetapi karena node teks adalah turunan yang tepat dari node elemen di DOM, ::text diperlakukan sebagai node mandiri dan dikonversi langsung ke text(), yang mana, dengan descendant-or-self sumbu, cocok dengan simpul teks mana pun yang merupakan turunan dari elemen a, sama seperti a/text() cocok dengan simpul teks mana pun anak dari elemen a (anak juga merupakan turunan).

Yang mengerikan, ini terjadi bahkan ketika Anda menambahkan * eksplisit ke pemilih:

>>> css2xpath('a *::text')
'descendant-or-self::a/descendant-or-self::text()'

Namun, penggunaan sumbu descendant-or-self berarti a ::text dapat mencocokkan semua node teks dalam elemen a, termasuk yang ada di elemen lain yang bersarang di dalam a. Dalam contoh berikut, a ::text akan mencocokkan dua node teks: 'Link ' diikuti oleh 'text':

<a href="https://example.com">Link <span>text</span></a>

Jadi, meskipun penerapan ::text oleh Scrapy merupakan pelanggaran berat terhadap tata bahasa Selectors, tampaknya hal ini dilakukan dengan sengaja.

Faktanya, elemen semu Scrapy lainnya ::attr()1 berperilaku serupa. Selector berikut semuanya cocok dengan node atribut id milik elemen div ketika tidak memiliki elemen turunan apa pun:

>>> 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'

... tetapi div ::attr(id) dan div *::attr(id) akan cocok dengan semua node atribut id dalam turunan div bersama dengan atribut id miliknya sendiri, seperti pada contoh berikut:

<div id="parent"><p id="child"></p></div>

Tentu saja, ini adalah kasus penggunaan yang kurang masuk akal, jadi kita harus bertanya-tanya apakah ini merupakan efek samping yang tidak disengaja dari penerapan ::text.

Bandingkan penyeleksi elemen semu dengan penyeleksi elemen semu yang menggantikan selektor sederhana apa pun:

>>> css2xpath('a [href]')
'descendant-or-self::a/descendant-or-self::*/*[@href]'

Ini dengan benar menerjemahkan kombinator turunan ke descendant-or-self::*/* dengan sumbu child implisit tambahan, memastikan bahwa predikat [@href] tidak pernah diuji pada elemen a.

Jika Anda baru mengenal XPath, Selectors, atau bahkan Scrapy, ini semua mungkin tampak membingungkan dan membebani. Jadi, inilah ringkasan kapan harus menggunakan satu pemilih dibandingkan pemilih lainnya:

  • Gunakan a::text jika elemen a Anda hanya berisi teks, atau jika Anda hanya tertarik pada node teks tingkat atas dari elemen a ini dan bukan elemen bertumpuknya.

  • Gunakan a ::text jika elemen a Anda berisi elemen bersarang dan Anda ingin mengekstrak semua node teks dalam elemen a ini.

    Meskipun Anda dapat menggunakan a ::text jika elemen a Anda hanya berisi teks, sintaksisnya membingungkan, jadi demi konsistensi, gunakan a::text sebagai gantinya.


1 Yang menarik, ::attr() muncul di (ditinggalkan mulai tahun 2021) Spesifikasi Selectors Non-elemen, yang seperti yang Anda harapkan berperilaku konsisten dengan tata bahasa Selectors, sehingga perilakunya di Scrapy tidak konsisten dengan spesifikasi. ::text di sisi lain jelas-jelas hilang dari spesifikasi; berdasarkan jawaban ini, saya rasa Anda dapat menebak alasannya secara masuk akal.

person BoltClock    schedule 01.02.2018