【发布时间】:2013-02-08 01:36:55
【问题描述】:
我正在获取 GZipped LXML 文件并尝试将产品条目写入数据库模型。以前我遇到了本地内存问题,通过 SO (question) 的帮助解决了这些问题。现在我一切正常并部署了它,但是在服务器上我收到以下错误:
Exceeded soft private memory limit with 158.164 MB after servicing 0 requests total
现在我尽我所能减少内存使用,目前正在使用下面的代码。 GZipped 文件约为 7 MB,而解压缩后为 80 MB。在本地,代码运行良好。我尝试将它作为 HTTP 请求以及 Cron Job 运行,但没有任何区别。现在我想知道是否有任何方法可以提高效率。
关于 SO 的一些类似问题参考了我不熟悉的前端和后端规范。我正在运行 GAE 的免费版本,这个任务必须每周运行一次。任何关于前进的最佳方式的建议将不胜感激。
from google.appengine.api.urlfetch import fetch
import gzip, base64, StringIO, datetime, webapp2
from lxml import etree
from google.appengine.ext import db
class GetProductCatalog(webapp2.RequestHandler):
def get(self):
user = XXX
password = YYY
url = 'URL'
# fetch gziped file
catalogResponse = fetch(url, headers={
"Authorization": "Basic %s" % base64.b64encode(user + ':' + password)
}, deadline=10000000)
# the response content is in catalogResponse.content
# un gzip the file
f = StringIO.StringIO(catalogResponse.content)
c = gzip.GzipFile(fileobj=f)
content = c.read()
# create something readable by lxml
xml = StringIO.StringIO(content)
# delete unnecesary variables
del f
del c
del content
# parse the file
tree = etree.iterparse(xml, tag='product')
for event, element in tree:
if element.findtext('manufacturer') == 'New York':
if Product.get_by_key_name(element.findtext('sku')):
coupon = Product.get_by_key_name(element.findtext('sku'))
if coupon.last_update_prov != datetime.datetime.strptime(element.findtext('lastupdated'), "%d/%m/%Y"):
coupon.restaurant_name = element.findtext('name')
coupon.restaurant_id = ''
coupon.address_street = element.findtext('keywords').split(',')[0]
coupon.address_city = element.findtext('manufacturer')
coupon.address_state = element.findtext('publisher')
coupon.address_zip = element.findtext('manufacturerid')
coupon.value = '$' + element.findtext('price') + ' for $' + element.findtext('retailprice')
coupon.restrictions = element.findtext('warranty')
coupon.url = element.findtext('buyurl')
if element.findtext('instock') == 'YES':
coupon.active = True
else:
coupon.active = False
coupon.last_update_prov = datetime.datetime.strptime(element.findtext('lastupdated'), "%d/%m/%Y")
coupon.put()
else:
pass
else:
coupon = Product(key_name = element.findtext('sku'))
coupon.restaurant_name = element.findtext('name')
coupon.restaurant_id = ''
coupon.address_street = element.findtext('keywords').split(',')[0]
coupon.address_city = element.findtext('manufacturer')
coupon.address_state = element.findtext('publisher')
coupon.address_zip = element.findtext('manufacturerid')
coupon.value = '$' + element.findtext('price') + ' for $' + element.findtext('retailprice')
coupon.restrictions = element.findtext('warranty')
coupon.url = element.findtext('buyurl')
if element.findtext('instock') == 'YES':
coupon.active = True
else:
coupon.active = False
coupon.last_update_prov = datetime.datetime.strptime(element.findtext('lastupdated'), "%d/%m/%Y")
coupon.put()
else:
pass
element.clear()
UDPATE
根据 Paul 的建议,我实施了后端。经过一些麻烦后,它就像一个魅力 - 找到我在下面使用的代码。
我的 backends.yaml 如下所示:
backends:
- name: mybackend
instances: 10
start: mybackend.app
options: dynamic
而我的app.yaml如下:
handlers:
- url: /update/mybackend
script: mybackend.app
login: admin
【问题讨论】:
-
您多久需要从 xml 文件中导入数据。如果只是偶尔您会发现使用 remote_api 并在本地处理文件并直接写入数据存储更容易。然后完整的可能与您本地机器可以处理的一样大。
-
还要注意
del c可能不会做任何事情,除非你明确调用 gc.collect() 因为这些东西可能在很长一段时间内都不会被收集。还要看看你的代码,你有读取文件/StringIO、xml(这是 c 的 StringIO 包装版本),然后是完整的解析树。你说未压缩它是 80MB,加上你至少有一个你没有 gc'd 的副本加上树。您可能会考虑使用拉解析策略,这意味着您在内存中没有完整的解析树副本以及字符串。 -
感谢蒂姆的意见。是的,我确实考虑了 remote_api 选项,但在某些时候,此脚本将以每日速度运行,这就是我选择当前设置的原因。我将研究您对拉解析策略的建议,看看它是否可以提高性能。再次感谢!
标签: python google-app-engine memory lxml