Scrapy 按顺序爬取 URL
我的问题其实很简单。我有一个爬虫程序在多个网站上抓取数据,我希望它能按照我在代码中写的顺序返回数据。下面是我的代码。
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,想看看是不是这个导致的问题,但结果没有任何变化。
13 个回答
谷歌小组的讨论建议在请求对象中使用优先级属性。Scrapy 默认保证网址是按照深度优先的顺序进行爬取的。但它并不保证网址会按照你在解析回调中生成的顺序被访问。
与其生成请求对象,不如返回一个请求的数组,从这个数组中逐个取出请求,直到数组为空。
你可以试试这样的做法吗?
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
Scrapy 现在的 Request
对象有了一个 priority
属性。
如果你在一个函数里有很多 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 会先处理 priority=1
的请求。
start_urls
是用来定义一组网址的,这些网址会在 start_requests
方法中使用。当你下载每个网址的页面时,parse
方法会被调用来处理这些页面的响应。不过,你无法控制加载的时间,可能第一个网址的响应最后才会到达 parse
方法。
解决这个问题的方法是重写 start_requests
方法,在生成的请求中添加一个 meta
字段,并设置一个 priority
的键。在 parse
方法中提取这个 priority
值,并将其添加到 item
中。在处理管道中根据这个值进行相应的操作。(我不知道你为什么需要这些网址按这个顺序处理。)
另一种方法是让它变得有点同步——把这些起始网址存储起来。在 start_urls
中放入第一个网址。在 parse
方法中处理第一个响应并返回相应的 item
,然后从存储中取下一个网址,并发出请求,回调到 parse
方法。