2011-07-04 63 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