2016-02-26 43 views
5

uczę scrapy z samouczka: http://doc.scrapy.org/en/1.0/intro/tutorial.htmlDlaczego XPath wewnątrz pętli selektora jeszcze wrócić listę w tutorialu

Kiedy uruchomić następujący przykład skrypt w samouczku. Zauważyłem, że chociaż już przeglądałem listę selektorów, kafelek, który dostałem z sel.xpath('a/text()').extract(), wciąż był listą zawierającą jeden ciąg. Podobnie jak [u'Python 3 Object Oriented Programming'] zamiast u'Python 3 Object Oriented Programming'. W późniejszym przykładzie lista jest przypisywana do pozycji jako item['title'] = sel.xpath('a/text()').extract(), co moim zdaniem nie jest logicznie poprawne.

import scrapy 

class DmozSpider(scrapy.Spider): 
    name = "dmoz" 
    allowed_domains = ["dmoz.org"] 
    start_urls = [ 
     "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/", 
     "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/" 
    ] 

    def parse(self, response): 
     for sel in response.xpath('//ul/li'): 
      title = sel.xpath('a/text()').extract() 
      link = sel.xpath('a/@href').extract() 
      desc = sel.xpath('text()').extract() 
      print title, link, desc 

Jednak jeśli mogę użyć następującego kodu:

import scrapy 

class DmozSpider(scrapy.Spider): 
    name = "dmoz" 
    allowed_domains = ["dmoz.org"] 
    start_urls = [ 
     "http://www.dmoz.org/Computers/Programming/Languages/Python/", 
    ] 

    def parse(self, response): 
     for href in response.css("ul.directory.dir-col > li > a::attr('href')"): 
      link = href.extract() 
      print(link) 

link jest ciągiem zamiast listy.

Czy jest to błąd, czy zamierzony?

Odpowiedz

8

.xpath().extract() i .css().extract() zwróci listę ponieważ .xpath() i .css() powrót SelectorList obiektów.

Zobacz https://parsel.readthedocs.org/en/v1.0.1/usage.html#parsel.selector.SelectorList.extract

(SelectorList) .extract():

Połącz się z .extract() metoda dla każdego elementu jest ta lista i powrót ich wyniki spłaszczone, jako lista ciągów Unicode.

.extract_first() jest to, czego szukasz (który jest słabo udokumentowany)

Zrobione z http://doc.scrapy.org/en/latest/topics/selectors.html:

Jeśli chcesz, aby wyodrębnić tylko pierwszy dopasowanego elementu można nazwać selektor .extract_first()

>>> response.xpath('//div[@id="images"]/a/text()').extract_first() 
u'Name: My image 1 ' 

w drugim przykładzie:

def parse(self, response): 
    for href in response.css("ul.directory.dir-col > li > a::attr('href')"): 
     link = href.extract() 
     print(link) 

każdy href w pętli będzie Selector przedmiot.Wywołanie .extract() na nim będzie Ci jeden ciąg znaków Unicode powrotem:

$ scrapy shell "http://www.dmoz.org/Computers/Programming/Languages/Python/" 
2016-02-26 12:11:36 [scrapy] INFO: Scrapy 1.0.5 started (bot: scrapybot) 
(...) 
In [1]: response.css("ul.directory.dir-col > li > a::attr('href')") 
Out[1]: 
[<Selector xpath=u"descendant-or-self::ul[@class and contains(concat(' ', normalize-space(@class), ' '), ' directory ') and (@class and contains(concat(' ', normalize-space(@class), ' '), ' dir-col '))]/li/a/@href" data=u'/Computers/Programming/Languages/Python/'>, 
<Selector xpath=u"descendant-or-self::ul[@class and contains(concat(' ', normalize-space(@class), ' '), ' directory ') and (@class and contains(concat(' ', normalize-space(@class), ' '), ' dir-col '))]/li/a/@href" data=u'/Computers/Programming/Languages/Python/'>, 
... 
<Selector xpath=u"descendant-or-self::ul[@class and contains(concat(' ', normalize-space(@class), ' '), ' directory ') and (@class and contains(concat(' ', normalize-space(@class), ' '), ' dir-col '))]/li/a/@href" data=u'/Computers/Programming/Languages/Python/'>] 

tak .css() na response zwraca SelectorList:

In [2]: type(response.css("ul.directory.dir-col > li > a::attr('href')")) 
Out[2]: scrapy.selector.unified.SelectorList 

Pętla na tym obiekcie daje Selector instancje:

In [5]: for href in response.css("ul.directory.dir-col > li > a::attr('href')"): 
    ...:  print href 
    ...:  
<Selector xpath=u"descendant-or-self::ul[@class and contains(concat(' ', normalize-space(@class), ' '), ' directory ') and (@class and contains(concat(' ', normalize-space(@class), ' '), ' dir-col '))]/li/a/@href" data=u'/Computers/Programming/Languages/Python/'> 
<Selector xpath=u"descendant-or-self::ul[@class and contains(concat(' ', normalize-space(@class), ' '), ' directory ') and (@class and contains(concat(' ', normalize-space(@class), ' '), ' dir-col '))]/li/a/@href" data=u'/Computers/Programming/Languages/Python/'> 
(...) 
<Selector xpath=u"descendant-or-self::ul[@class and contains(concat(' ', normalize-space(@class), ' '), ' directory ') and (@class and contains(concat(' ', normalize-space(@class), ' '), ' dir-col '))]/li/a/@href" data=u'/Computers/Programming/Languages/Python/'> 

Wywołanie .extract() daje jeden kod Unicode ing:

In [6]: for href in response.css("ul.directory.dir-col > li > a::attr('href')"): 
    print type(href.extract()) 
    ...:  
<type 'unicode'> 
<type 'unicode'> 
<type 'unicode'> 
<type 'unicode'> 
<type 'unicode'> 
<type 'unicode'> 
<type 'unicode'> 
<type 'unicode'> 
<type 'unicode'> 
<type 'unicode'> 
<type 'unicode'> 
<type 'unicode'> 
<type 'unicode'> 

Uwaga: .extract() na Selector jest wrongly documented jako powrót listę ciągów. Otworzę numer na parsel (który jest taki sam jak selektory Scrapy i jest używany pod maską w scrapie 1.1+)

+0

Dzięki za szybką odpowiedź! Właśnie edytowałem post i dodałem przykład z samouczka, w którym 'extract()' podaje ciąg znaków. Czy to dlatego, że używam css? – entron

+0

Dobrze, to, co napisałem, nie jest poprawne (zbyt szybka odpowiedź). W rzeczywistości '.xpath(). Extract()' i '.css(). Extract()' zwraca listy, ponieważ '.xpath()' i '.css()' zwraca obiekty 'SelectorList'. Ale zapętlanie '.xpath()' daje ci 'Selector', z którego możesz wywołać' .extract() 'i uzyskać pojedynczy element. Zmienię moją odpowiedź –

+0

Ta część jest naprawdę myląca, ale teraz rozumiem! Dziękuję Ci bardzo! – entron