【发布时间】:2015-07-19 12:28:53
【问题描述】:
我用 scrapy 编写了一个爬虫,可以从一些新闻网站抓取数十万页。当我从命令行工具启动它并且内存使用率在我的 4GB PC 上达到 20% 的稳定水平时,它运行良好。 (我对请求使用了优先级来确保不会有太多的请求存活。)但是当我从 python 脚本启动它时,内存的使用量继续增长,直到我的蜘蛛吃掉所有的内存空间。这是我的启动脚本:
class CrawlersInitiator(object):
def __init__(self, spiders, start=datetime.now()-timedelta(minutes=30), end=datetime.now()):
self.setting = get_project_settings()
self.crawlers = []
self.spiders = spiders
self.start_time = start
self.end_time = end
# log file
self.info_log = None
log_dir = self.setting.get("LOG_DIR")
if not os.path.exists(log_dir):
os.mkdir(log_dir)
# counter used to stop reactor
self.stopped_crawler = 0
self.lock = RLock()
def __del__(self):
self.close_log_file()
def create_log_file(self):
""" create log file with crawl date in file name
"""
self.close_log_file()
dir_path = self.setting.get("LOG_DIR")+"/{0}".format(self.end_time.strftime("%Y-%m"))
file_suffix = self.end_time.strftime("%Y-%m-%d")
if not os.path.exists(dir_path):
os.mkdir(dir_path)
self.info_log = open("{0}/log-{1}.log".format(dir_path, file_suffix), "a") # info
def close_log_file(self):
if self.info_log and not self.info_log.closed:
self.info_log.close()
self.info_log = None
def get_crawler(self, spider):
crawler = Crawler(self.setting)
crawler.signals.connect(self.stop, signal=signals.spider_closed)
crawler.configure()
crawler.crawl(spider(start_time=self.start_time, end_time=self.end_time))
return crawler
def stop(self):
"""callback to stop reactor
"""
self.lock.acquire()
self.stopped_crawler += 1
if self.stopped_crawler >= len(self.crawlers):
reactor.stop()
self.lock.release()
def run_spiders(self):
"""run spiders
"""
self.crawlers = []
self.stopped_crawler = 0
# get crawlers
for Spider in self.spiders:
self.crawlers.append(self.get_crawler(Spider))
# log
self.create_log_file()
ScrapyFileLogObserver(self.info_log, level=log.INFO).start()
self.info_log.write("\nCrawlers starting...\n")
self.info_log.write("Crawl from {0} to {1}".format(str(self.start_time), str(self.end_time)))
# run
for crawler in self.crawlers:
crawler.start()
reactor.run()
end = datetime.now()
# release crawlers
for crawler in self.crawlers:
del crawler
# log
self.info_log.write("Crawlers finished in {0} !\n".format(str(end-self.end_time)))
self.close_log_file()
def crawl(spiders, start, end):
CrawlersInitiator(spiders, start=start, end=end).run_spiders()
SPIDERS = [MySpider1, MySpider2]
if __name__ == "__main__":
start_time = datetime.strptime(sys.argv[1], "%Y-%m-%d_%H:%M:%S")
end_time = datetime.strptime(sys.argv[2], "%Y-%m-%d_%H:%M:%S")
crawl(SPIDERS, start_time, end_time)
quit()
我尝试使用scrapy trackref 来查找问题。
从命令行工具启动时,prefs() 显示(只启动了一个蜘蛛):
MySpider1 1 oldest: 942s ago
HtmlResponse 13 oldest: 52s ago
Request 6329 oldest: 932s ago
Item 5915 oldest: 932s ago
Selector 13 oldest: 52s ago
从脚本启动时,prefs() 显示:
Response 51 oldest: 657s ago
Request 6966 oldest: 661s ago
Item 5732 oldest: 661s ago
HtmlResponse 377 oldest: 661s ago
Selector 377 oldest: 661s ago
MySpider1 1 oldest: 661s ago
从我的脚本中分层时,scrapy 似乎从不释放任何对象。为什么会发生这种情况以及如何解决?
这是我所有蜘蛛的超类,所有的请求都在这个类中处理:
class IndexSpider(Spider):
__metaclass__ = ABCMeta
# splice _queries onto _search_url to get start_requests (index pages of news)
_search_url = ""
_queries = []
_char_set = "utf8"
def __init__(self, queries=self._queries, start_time=datetime.min, end_time=datetime.now()):
self.queries = queries
self.start_time = start_time
self.end_time = end_time
def start_requests(self):
query_count = 0
query = None
try:
for query in self.queries:
yield Request(self._search_url.format(urllib.quote(query.encode(self._char_set))), self.parse_index)
query_count += 1
except Exception, e:
self.log("Query No.{0} can't be encoded in {1}, because of {2}!"
.format(str(query_count), self.name, e), level=log.WARNING)
yield Request(self._search_url.format(query.encode("gbk")), self.parse_index)
def parse_index(self, response):
"""parse index page
"""
requests = []
page_list = self._get_result(response)
if not page_list:
return requests
next_page = True
for item in page_list:
if isinstance(item, Request):
requests.append(item)
next_page = False
break
if item['publish_time'] <= self.from_time:
next_page = False
break
elif item['publish_time'] > self.end_time:
continue
else:
req = Request(item['url'], self.parse_news, priority=1)
req.meta["item"] = item
requests.append(req)
if next_page:
next_page = self._next_index_page(response)
if next_page:
requests.append(Request(self._next_index_page(response), self.parse_index))
return requests
def parse_news(self, response):
"""parse news page
"""
item = response.meta["item"]
del response.meta['item']
return self._finish_item(item, response)
@abstractmethod
def _get_result(self, response):
"""get news list from index page
:param response: index page
:return: a list of objects of crawlers.items.Base or its subclass, each object represents a news
"""
pass
@abstractmethod
def _next_index_page(self, response):
"""
:param response: current index page
:return: URL of the next index page
"""
pass
@abstractmethod
def _finish_item(self, item, response):
"""parse news page
:param item: news item get from the index page
:param response: news page
:return: news item or new request
"""
pass
【问题讨论】:
-
嗨!您也可以粘贴蜘蛛的代码吗?大多数情况下,这是因为scrapy调度程序在其内存中仍然有Request对象,它可以在
yield请求或return请求之间切换。如果您想查看运行多个蜘蛛的基本实现,我有一个博客 here 可以帮助您。 -
@kiran.koduru 我已经发布了我所有蜘蛛的超类,所有请求都在其中创建和处理。
-
你还在处理这个问题吗?你可以在运行至少 5-10 分钟后发布
prefs()吗? -
@kiran.koduru 抱歉这么晚才回复!我发布的 prefs() 都运行了超过 10 分钟。我的蜘蛛在跑了 20-30 分钟后崩溃了。从脚本启动时,scrapy 似乎没有回收任何对象。我怀疑我是否有什么问题扭曲...