2011-07-04 61 views
13

所以,我的問題相對比較簡單。我有一個蜘蛛抓取多個網站,我需要它按照我在代碼中寫入的順序返回數據。它張貼在下面。訂單中的Scrapy抓取網址

from scrapy.spider import BaseSpider 
from scrapy.selector import HtmlXPathSelector 
from mlbodds.items import MlboddsItem 

class MLBoddsSpider(BaseSpider): 
    name = "sbrforum.com" 
    allowed_domains = ["sbrforum.com"] 
    start_urls = [ 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110328/", 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110329/", 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110330/" 
    ] 

    def parse(self, response): 
     hxs = HtmlXPathSelector(response) 
     sites = hxs.select('//div[@id="col_3"]//div[@id="module3_1"]//div[@id="moduleData4952"]') 
     items = [] 
     for site in sites: 
      item = MlboddsItem() 
      item['header'] = site.select('//div[@class="scoreboard-bar"]//h2//span[position()>1]//text()').extract()# | /*//table[position()<2]//tr//th[@colspan="2"]//text()').extract() 
      item['game1'] = site.select('/*//table[position()=1]//tr//td[@class="tbl-odds-c2"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c4"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c6"]//text()').extract() 
      items.append(item) 
     return items 

結果以隨機順序返回,例如返回第29個,然後是第28個,然後是第30個。我已經嘗試將DFO的調度程序順序改爲BFO,以防萬一這是問題所在,但這並沒有改變任何事情。

在此先感謝。

+0

卡安你告訴我們,你是如何調用你的蜘蛛? –

+0

>我有一個蜘蛛爬行多個網站, 您的意思是多個啓動網址? – warvariuc

回答

15

start_urls定義在start_requests方法中使用的url。您的parse方法將在下載頁面時爲每個啓動url調用一個響應。但是您無法控制加載時間 - 第一個啓動網址可能會在最後到達parse

解決方案 - 覆蓋start_requests方法並添加到生成的請求metapriority密鑰。在parse中提取此priority值並將其添加到item。在管道中做一些基於這個值的東西。(我不知道爲什麼和在哪裏需要按照這個順序處理這些網址)。

或者讓它有同步 - 將這些啓動URL存儲在某個地方。第一個放入start_urls。在parse中處理第一個響應併產生項目,然後從存儲中取出下一個URL並請求parse

+0

所有好的反饋,感謝大家的幫助。這一個最接近我想要做的。 – Jeff

+0

我有一個相關的問題。假設我想指定一個網址列表,其中第一個是網站的主頁,下一個是網頁列表。我該如何解決它? –

+0

@PrakharMohanSrivastava,把它們放在['start_urls'](http://doc.scrapy.org/en/latest/topics/spiders.html#scrapy.spider.Spider.start_urls)? – warvariuc

0

免責聲明:未與scrapy工作專門

刮板可排隊和重新排隊基於超時和HTTP錯誤的請求,這將是一個容易得多,如果你可以從響應頁面日期得到什麼?

I.e.添加另一個獲取日期的hxs.select語句(只需查看一下,它肯定在響應數據中),然後將其添加到項目字典中,根據該項目對項目進行排序。

這可能是一個更可靠的方法,而不是依賴於擦傷的順序...

0

相信

hxs.select('...') 

你做出將從順序出現它的網站刮數據。無論是scrapy還是以任意順序穿過你的start_urls。迫使它要經過他們在一個預定義的順序,你得注意,如果你需要抓取更多的網站,這將無法正常工作,那麼你可以試試這個:

start_urls = ["url1.html"] 

def parse1(self, response): 
    hxs = HtmlXPathSelector(response) 
    sites = hxs.select('blah') 
    items = [] 
    for site in sites: 
     item = MlboddsItem() 
     item['header'] = site.select('blah') 
     item['game1'] = site.select('blah') 
     items.append(item) 
    return items.append(Request('url2.html', callback=self.parse2)) 

然後寫一個parse2,做同樣的但是用callback = self.parse3附加了url3.html的請求。這是可怕的編碼風格,但我只是拋出它,以防您需要快速入侵。

2

我懷疑是否有可能實現你想要的,除非你玩scrapy內部。有關scrapy谷歌組的一些類似討論,例如

http://groups.google.com/group/scrapy-users/browse_thread/thread/25da0a888ac19a9/1f72594b6db059f4?lnk=gst

一件事還可幫助是 設置CONCURRENT_REQUESTS_PER_SPIDER 1但不會完全確保 順序要麼因爲 下載都有自己本地隊列 性能原因所以最好的 你可以做的是優先請求 但不能確保其確切的順序。

6

谷歌小組討論建議在請求對象中使用優先級屬性。 Scrapy保證默認情況下在DFO中抓取網址。但是它並不能確保按照您在解析回調中獲得的順序訪問這些網址。

而不是產生請求對象,你想返回一個請求數組,從中彈出對象直到它爲空。

你可以嘗試類似的東西嗎?

from scrapy.spider import BaseSpider 
from scrapy.http import Request 
from scrapy.selector import HtmlXPathSelector 
from mlbodds.items import MlboddsItem 

class MLBoddsSpider(BaseSpider): 
    name = "sbrforum.com" 
    allowed_domains = ["sbrforum.com"] 

    def start_requests(self): 
     start_urls = reversed([ 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110328/", 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110329/", 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110330/" 
     ]) 

     return [ Request(url = start_url) for start_url in start_urls ] 

    def parse(self, response): 
     hxs = HtmlXPathSelector(response) 
     sites = hxs.select('//div[@id="col_3"]//div[@id="module3_1"]//div[@id="moduleData4952"]') 
     items = [] 
     for site in sites: 
      item = MlboddsItem() 
      item['header'] = site.select('//div[@class="scoreboard-bar"]//h2//span[position()>1]//text()').extract()# | /*//table[position()<2]//tr//th[@colspan="2"]//text()').extract() 
      item['game1'] = site.select('/*//table[position()=1]//tr//td[@class="tbl-odds-c2"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c4"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c6"]//text()').extract() 
      items.append(item) 
     return items 
1

當然,你可以控制它。 最高機密是如何餵養貪婪的引擎/ Schedulor。你的要求只是一個小小的。請參閱我添加名爲「task_urls」的列表。

from scrapy.spider import BaseSpider 
from scrapy.selector import HtmlXPathSelector 
from scrapy.http.request import Request 
from dirbot.items import Website 

class DmozSpider(BaseSpider): 
    name = "dmoz" 
    allowed_domains = ["sbrforum.com"] 
    start_urls = [ 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110328/", 
    ] 
    task_urls = [ 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110328/", 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110329/", 
     "http://www.sbrforum.com/mlb-baseball/odds-scores/20110330/" 
    ] 
    def parse(self, response): 

     hxs = HtmlXPathSelector(response) 
     sites = hxs.select('//div[@id="col_3"]//div[@id="module3_1"]//div[@id="moduleData4952"]') 
     items = [] 
     for site in sites: 
      item = Website() 
      item['header'] = site.select('//div[@class="scoreboard-bar"]//h2//span[position()>1]//text()').extract()# | /*//table[position()<2]//tr//th[@colspan="2"]//text()').extract() 
      item['game1'] = site.select('/*//table[position()=1]//tr//td[@class="tbl-odds-c2"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c4"]//text() | /*//table[position()=1]//tr//td[@class="tbl-odds-c6"]//text()').extract() 
      items.append(item) 
     # Here we feed add new request 
     self.task_urls.remove(response.url) 
     if self.task_urls: 
      r = Request(url=self.task_urls[0], callback=self.parse) 
      items.append(r) 

     return items 

如果你想要一些更復雜的情況下,請參閱我的項目: https://github.com/wuliang/TiebaPostGrabber

2

的解決方案是連續的。
該解決方案類似於@wuliang

我開始與@Alexis德Tréglodé方法,但達到了一個問題:
start_requests()方法返回URLS
return [ Request(url = start_url) for start_url in start_urls ]
的名單,這一事實導致輸出爲非順序(異步)

如果返回是單個響應,則通過創建替代other_urls可以滿足要求。此外,還可以使用other_urls添加從其他網頁抓取的網址。

from scrapy import log 
from scrapy.spider import BaseSpider 
from scrapy.http import Request 
from scrapy.selector import HtmlXPathSelector 
from practice.items import MlboddsItem 

log.start() 

class PracticeSpider(BaseSpider): 
    name = "sbrforum.com" 
    allowed_domains = ["sbrforum.com"] 

    other_urls = [ 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110328/", 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110329/", 
      "http://www.sbrforum.com/mlb-baseball/odds-scores/20110330/", 
      ] 

    def start_requests(self): 
     log.msg('Starting Crawl!', level=log.INFO) 
     start_urls = "http://www.sbrforum.com/mlb-baseball/odds-scores/20110327/" 
     return [Request(start_urls, meta={'items': []})] 

    def parse(self, response): 
     log.msg("Begin Parsing", level=log.INFO) 
     log.msg("Response from: %s" % response.url, level=log.INFO) 
     hxs = HtmlXPathSelector(response) 
     sites = hxs.select("//*[@id='moduleData8460']") 
     items = response.meta['items'] 
     for site in sites: 
      item = MlboddsItem() 
      item['header'] = site.select('//div[@class="scoreboard-bar"]//h2//span[position()>1]//text()').extract() 
      item['game1'] = site.select('/*//table[position()=1]//tr//td[@class="tbl-odds-c2"]//text()').extract() 
      items.append(item) 

     # here we .pop(0) the next URL in line 
     if self.other_urls: 
      return Request(self.other_urls.pop(0), meta={'items': items}) 

     return items 
9

Scrapy '請求' 現在有一個優先級屬性。 http://doc.scrapy.org/en/latest/topics/request-response.html#request-objects如果你有在功能上有許多「請求」,並希望先處理特定請求,您可以設置

def parse(self,response): url = http://www.example.com/first yield Request(url=url,callback = self.parse_data,priority=1) url = http://www.example.com/second yield Request(url=url,callback = self.parse_data)

Scrapy將處理一個具有優先級1第一。

0

我個人喜歡@ user1460015的實現後,我設法讓我自己的解決方案。

我的解決方案是使用Python的子進程通過url調用scrapy url直到所有的url都被照顧好。

在我的代碼中,如果用戶沒有指定他/她想要依次解析url,我們可以用普通的方式啓動蜘蛛。

process = CrawlerProcess({'USER_AGENT': 'Mozilla/4.0 (compatible; \ 
    MSIE 7.0; Windows NT 5.1)'}) 
process.crawl(Spider, url = args.url) 
process.start() 

如果用戶指定它需要依次完成,我們可以這樣做:

for url in urls: 
    process = subprocess.Popen('scrapy runspider scrapper.py -a url='\ 
     + url + ' -o ' + outputfile) 
    process.wait() 

需要注意的是:這種做法不處理錯誤。

0

有一個更容易讓scrapy遵循starts_url順序的方法:你可以取消註釋並更改settings.py併發請求1.

Configure maximum concurrent requests performed by Scrapy (default: 16) 
CONCURRENT_REQUESTS = 1