¿Cómo seleccionar y extraer textos entre dos elementos?

Estoy tratando de raspar este sitio web utilizando scrapy. La estructura de la página se ve así:

Quiero seleccionar y extraer los textos entre:

Follows

y

Followed by

luego textos entre

Followed by

y

Spin-off


He intentado este código:

 def parse(self, response): for sel in response.css("div.list"): item = ImdbcoItem() item['Follows'] = sel.css("a#follows+h4.li_group ~ div a::text").extract(), item['Followed_by'] = sel.css("a#vfollowed_by+h4.li_group ~ div a::text").extract(), item['Spin_off'] = sel.css("a#spin_off+h4.li_group ~ div a::text").extract(), return item 

Pero este el primer elemento extrae todos los divs, no solo los divs entre

Follows

y

Followed by


Cualquier ayuda sería realmente útil!

Un patrón de extracción que me gusta usar para estos casos es:

  • bucle sobre los “límites” (aquí, elementos h4 )
  • Mientras los enumeramos a partir de 1
  • usando el eje de following-sibling XPath, como en la respuesta de @ Andersson, para obtener elementos antes del siguiente límite,
  • y filtrandolas contando el número de elementos de “límite” anteriores, ya que sabemos de nuestra enumeración dónde estamos

Este sería el bucle:

 $ scrapy shell 'http://www.imdb.com/title/tt0092455/trivia?tab=mc&ref_=tt_trv_cnn' (...) >>> for cnt, h4 in enumerate(response.css('div.list > h4.li_group'), start=1): ... print(cnt, h4.xpath('normalize-space()').get()) ... 1 Follows 2 Followed by 3 Edited into 4 Spun-off from 5 Spin-off 6 Referenced in 7 Featured in 8 Spoofed in 

Y este es un ejemplo del uso de la enumeración para obtener elementos entre límites (tenga en cuenta que esto usa variables XPath con $cnt en la expresión y que pasa cnt=cnt en .xpath() ):

 >>> for cnt, h4 in enumerate(response.css('div.list > h4.li_group'), start=1): ... print(cnt, h4.xpath('normalize-space()').get()) ... print(h4.xpath('following-sibling::div[count(preceding-sibling::h4)=$cnt]', cnt=cnt).xpath( 'string(.//a)').getall()) ... 1 Follows ['Star Trek', 'Star Trek: The Animated Series', 'Star Trek: The Motion Picture', 'Star Trek II: The Wrath of Khan', 'Star Trek III: The Search for Spock', 'Star Trek IV: The Voyage Home'] 2 Followed by ['Star Trek V: The Final Frontier', 'Star Trek VI: The Undiscovered Country', 'Star Trek: Deep Space Nine', 'Star Trek: Generations', 'Star Trek: Voyager', 'First Contact', 'Star Trek: Insurrection', 'Star Trek: Enterprise', 'Star Trek: Nemesis', 'Star Trek', 'Star Trek Into Darkness', 'Star Trek Beyond', 'Star Trek: Discovery', 'Untitled Star Trek Sequel'] 3 Edited into ['Reading Rainbow: The Bionic Bunny Show', 'The Unauthorized Hagiography of Vincent Price'] 4 Spun-off from ['Star Trek'] 5 Spin-off ['Star Trek: The Next Generation - The Transinium Challenge', 'A Night with Troi', 'Star Trek: Deep Space Nine', "Star Trek: The Next Generation - Future's Past", 'Star Trek: The Next Generation - A Final Unity', 'Star Trek: The Next Generation: Interactive VCR Board Game - A Klingon Challenge', 'Star Trek: Borg', 'Star Trek: Klingon', 'Star Trek: The Experience - The Klingon Encounter'] 6 Referenced in (...) 

A continuación, le indicamos cómo puede usar eso para completar un elemento (aquí, estoy usando un dict simple para ilustrar):

 >>> item = {} >>> for cnt, h4 in enumerate(response.css('div.list > h4.li_group'), start=1): ... key = h4.xpath('normalize-space()').get().strip() # there are some non-breaking spaces ... if key in ['Follows', 'Followed by', 'Spin-off']: ... values = h4.xpath('following-sibling::div[count(preceding-sibling::h4)=$cnt]', ... cnt=cnt).xpath( ... 'string(.//a)').getall() ... item[key] = values ... >>> from pprint import pprint >>> pprint(item) {'Followed by': ['Star Trek V: The Final Frontier', 'Star Trek VI: The Undiscovered Country', 'Star Trek: Deep Space Nine', 'Star Trek: Generations', 'Star Trek: Voyager', 'First Contact', 'Star Trek: Insurrection', 'Star Trek: Enterprise', 'Star Trek: Nemesis', 'Star Trek', 'Star Trek Into Darkness', 'Star Trek Beyond', 'Star Trek: Discovery', 'Untitled Star Trek Sequel'], 'Follows': ['Star Trek', 'Star Trek: The Animated Series', 'Star Trek: The Motion Picture', 'Star Trek II: The Wrath of Khan', 'Star Trek III: The Search for Spock', 'Star Trek IV: The Voyage Home'], 'Spin-off': ['Star Trek: The Next Generation - The Transinium Challenge', 'A Night with Troi', 'Star Trek: Deep Space Nine', "Star Trek: The Next Generation - Future's Past", 'Star Trek: The Next Generation - A Final Unity', 'Star Trek: The Next Generation: Interactive VCR Board Game - A ' 'Klingon Challenge', 'Star Trek: Borg', 'Star Trek: Klingon', 'Star Trek: The Experience - The Klingon Encounter']} >>> 

Puedes intentar usar las siguientes expresiones XPath para obtener

  • Todos los nodos de texto para el bloque “Follows”:

     //div[./preceding-sibling::h4[1]="Follows"]//text() 
  • Todos los nodos de texto para el bloque “Seguido por”:

     //div[./preceding-sibling::h4[1]="Followed by"]//text() 
  • Todos los nodos de texto para el bloque “Spin off”:

     //div[./preceding-sibling::h4[1]="Spin-off"]//text()