所以,我的问题相对简单。我有一个蜘蛛爬行多个站点,我需要它按照我在代码中写入的顺序返回数据。它发布在下面。
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 :(得分:18)
start_urls
定义了start_requests
方法中使用的网址。调用您的parse
方法,并在下载页面时为每个起始网址添加响应。但是你无法控制加载时间 - 第一个启动网址可能是parse
的最后一个。
解决方案 - 覆盖start_requests
方法,并使用meta
密钥向生成的请求添加priority
。在parse
中提取此priority
值并将其添加到item
。在管道中做一些基于此值的事情。 (我不知道为什么以及在哪里需要按此顺序处理这些网址。)
或者让它变得同步 - 将这些启动网址存储在某个地方。放入start_urls
第一个。在parse
处理第一个响应并生成项目,然后从您的存储中获取下一个网址并通过回调parse
发出请求。
答案 1 :(得分:12)
Scrapy'Request'现在有一个优先级属性。http://doc.scrapy.org/en/latest/topics/request-response.html#request-objects如果你在函数中有很多'Request'并且想要先处理一个特定的请求,你可以设置
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的那个。
答案 2 :(得分:7)
Google小组讨论建议在Request对象中使用优先级属性。 Scrapy保证默认情况下在DFO中抓取网址。但它并不能确保按照在解析回调中产生的顺序访问URL。
您希望返回一个请求数组,而不是生成Request对象,而是从中弹出对象直到它为空。
你可以试试这样的吗?
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
答案 3 :(得分:2)
我怀疑除非你玩scrapy内部,否则是否有可能达到你想要的效果。关于scrapy谷歌群体有一些类似的讨论,例如
有一件事也可以帮到我 设置CONCURRENT_REQUESTS_PER_SPIDER 到1,但它不会完全确保 订单要么是因为 downloader有自己的本地队列 出于性能原因,所以最好 您可以做的是优先处理请求 但不能确保其确切的顺序。
答案 4 :(得分:2)
解决方案是顺序的 这个解决方案类似于@wuliang
我从@AlexisdeTré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
答案 5 :(得分: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
答案 6 :(得分:1)
有一种更简单的方法可以使 scrapy 遵循starts_url的顺序:您可以取消注释并将settings.py
中的并发请求更改为1。
Configure maximum concurrent requests performed by Scrapy (default: 16)
CONCURRENT_REQUESTS = 1
答案 7 :(得分:0)
免责声明:未专门针对scrapy工作
刮刀可能会根据超时和HTTP错误对请求进行排队和重新排队,如果您可以从响应页面获取日期会更容易吗?
即。添加另一个抓取日期的hxs.select语句(只是看一下,它肯定在响应数据中),然后将其添加到项目dict中,根据该项目对项目进行排序。
这可能是一种更强大的方法,而不是依赖于刮擦的顺序......
答案 8 :(得分: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请求。这是一种可怕的编码风格,但我只是把它丢掉,以防你需要快速破解。
答案 9 :(得分:0)
在我成功完成解决方案之后,我个人喜欢@ user1460015的实现。
我的解决方案是使用Python的子进程通过url调用scrapy 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()
请注意:此实现不处理错误。