我有网络抓取工具和http接口。
抓取工具将分组的网址设为字典。我需要在JSON中以相同的格式返回结果。但我面临着大量的内存使用,而不是返回给操作系统。如何在没有大量内存使用的情况下实现此解决方案?
代码:
#!/usr/bin/env python
# coding=utf-8
import collections
import tornado.web
import tornado.ioloop
import tornado.queues
import tornado.httpclient
class ResponseError(Exception):
pass
class Crawler(object):
client = tornado.httpclient.AsyncHTTPClient()
def __init__(self, groups, concurrency=10, retries=3, validators=None):
self.groups = groups
self.concurrency = concurrency
self.retries = retries
self.validators = validators or []
self.requests = tornado.queues.Queue()
self.responses = collections.defaultdict(list)
async def worker(self):
while True:
await self.consume()
async def validate(self, response):
for validator in self.validators:
validator(response)
async def save(self, response):
self.responses[response.request.group].append(response.body.decode('utf-8'))
async def consume(self):
async for request in self.requests:
try:
response = await self.client.fetch(request, raise_error=False)
await self.validate(response)
await self.save(response)
except ResponseError:
if request.retries < self.retries:
request.retries += 1
await self.requests.put(request)
finally:
self.requests.task_done()
async def produce(self):
for group, urls in self.groups.items():
for url in urls:
request = tornado.httpclient.HTTPRequest(url)
request.group = group
request.retries = 0
await self.requests.put(request)
async def fetch(self):
await self.produce()
for __ in range(self.concurrency):
tornado.ioloop.IOLoop.current().spawn_callback(self.worker)
await self.requests.join()
class MainHandler(tornado.web.RequestHandler):
async def get(self):
urls = []
with open('urls') as f: # mock
for line in f:
urls.append(line.strip())
crawler = Crawler({'default': urls})
await crawler.fetch()
self.write(crawler.responses)
if __name__ == '__main__':
app = tornado.web.Application(
(tornado.web.url(r'/', MainHandler),), debug=True
)
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
答案 0 :(得分:0)
在我看来,大多数内存使用都用于self.responses
。因为你似乎在订购&#34; group&#34;在将它们写入文件之前,我可以理解为什么这样做。一个想法是使用&#34;组&#34;将它们存储在数据库(MySQL或MongoDB或其他)中。作为数据库记录中的列或字段值。
数据库可能是数据的最终目标,或者在crawler.fetch完成之前,它可能是存储数据的临时位置。然后,查询数据库中的所有数据,按&#34; group&#34;排序,并将其写入文件。
这并不是解决问题,它只是意味着数据库进程负责大部分内存使用,而不是Python进程。但是,这可能对您更有利。