gpt4 book ai didi

python - Scrapy 隐藏的内存泄漏

转载 作者:行者123 更新时间:2023-12-03 09:32:43 24 4
gpt4 key购买 nike

背景 - TLDR:我的项目内存泄漏
花了几天时间用scrapy查看内存泄漏文档,但找不到问题。
我正在开发一个中等规模的scrapy 项目,每天大约有 4 万个请求。
我使用scrapinghub的预定运行来主持这个。
scrapinghub ,每月 9 美元,您基本上可以获得 1 个具有 1GB RAM 的 VM 来运行您的爬虫。
我在本地开发了一个爬虫并上传到scrapinghub,唯一的问题是在运行结束时,我超出了内存。
本地化设置 CONCURRENT_REQUESTS=16工作正常,但导致在 50% 点超过了 scrapinghub 上的内存。当我设置 CONCURRENT_REQUESTS=4 , 我在 95% 点超过了内存,所以减少到 2 应该可以解决问题,但是我的爬虫变得太慢了。
另一种解决方案是为 2 个 VM 付费以增加 RAM,但我有一种感觉,我设置爬网程序的方式会导致内存泄漏。
对于此示例,该项目将抓取在线零售商。
在本地运行时,我的 memusage/max是 2.7GB,带有 CONCURRENT_REQUESTS=16 .
我现在将运行我的scrapy结构

  • 获取要抓取的总页数
  • 使用以下命令遍历所有这些页面:www.example.com/page={page_num}
  • 在每个页面上,收集 48 个产品的信息
  • 对于这些产品中的每一个,请访问他们的页面并获取一些信息
  • 使用该信息,为每个产品直接调用 API
  • 使用项目管道保存这些(我在本地写入 csv,但不在 scrapinghub 上)
  • 管道
  •     class Pipeline(object):
    def process_item(self, item, spider):
    item['stock_jsons'] = json.loads(item['stock_jsons'])['subProducts']
    return item
  • 商品
  •     class mainItem(scrapy.Item):
    date = scrapy.Field()
    url = scrapy.Field()
    active_col_num = scrapy.Field()
    all_col_nums = scrapy.Field()
    old_price = scrapy.Field()
    current_price = scrapy.Field()
    image_urls_full = scrapy.Field()
    stock_jsons = scrapy.Field()

    class URLItem(scrapy.Item):
    urls = scrapy.Field()
  • 主蜘蛛
  •     class ProductSpider(scrapy.Spider):
    name = 'product'
    def __init__(self, **kwargs):
    page = requests.get('www.example.com', headers=headers)
    self.num_pages = # gets the number of pages to search

    def start_requests(self):
    for page in tqdm(range(1, self.num_pages+1)):
    url = 'www.example.com/page={page}'
    yield scrapy.Request(url = url, headers=headers, callback = self.prod_url)

    def prod_url(self, response):
    urls_item = URLItem()
    extracted_urls = response.xpath(####).extract() # Gets URLs to follow
    urls_item['urls'] = [# Get a list of urls]
    for url in urls_item['urls']:
    yield scrapy.Request(url = url, headers=headers, callback = self.parse)

    def parse(self, response) # Parse the main product page
    item = mainItem()
    item['date'] = DATETIME_VAR
    item['url'] = response.url
    item['active_col_num'] = XXX
    item['all_col_nums'] = XXX
    item['old_price'] = XXX
    item['current_price'] = XXX
    item['image_urls_full'] = XXX

    try:
    new_url = 'www.exampleAPI.com/' + item['active_col_num']
    except TypeError:
    new_url = 'www.exampleAPI.com/{dummy_number}'

    yield scrapy.Request(new_url, callback=self.parse_attr, meta={'item': item})


    def parse_attr(self, response):
    ## This calls an API Step 5
    item = response.meta['item']
    item['stock_jsons'] = response.text
    yield item
    到目前为止我尝试过什么?
  • psutils,没有太大帮助。
  • trackref.print_live_refs()最后返回以下内容:
  • HtmlResponse                       31   oldest: 3s ago
    mainItem 18 oldest: 5s ago
    ProductSpider 1 oldest: 3321s ago
    Request 43 oldest: 105s ago
    Selector 16 oldest: 3s ago
  • 随着时间的推移打印前 10 个全局变量
  • 随着时间的推移打印前 10 种项目类型

  • 问题
  • 我怎样才能找到内存泄漏?
  • 谁能看到我可能在哪里泄漏内存?
  • 我的scrapy结构是否存在根本问题?

  • 如果需要更多信息,请告诉我
    要求提供其他信息
  • 请注意,以下输出来自我的本地机器,我有足够的内存,因此我正在抓取的网站成为瓶颈。在使用scrapinghub时,由于1GB的限制,疑似内存泄漏成为问题。

  • 请让我知道是否需要scrapinghub的输出,我认为应该是一样的,但是完成原因的消息是内存超出。
    1.从开始的日志行(从信息:Scrapy xxx开始到蜘蛛打开)。
    2020-09-17 11:54:11 [scrapy.utils.log] INFO: Scrapy 2.3.0 started (bot: PLT)
    2020-09-17 11:54:11 [scrapy.utils.log] INFO: Versions: lxml 4.5.2.0, libxml2 2.9.10, cssselect 1.1.0, parsel 1.6.0, w3lib 1.22.0, Twisted 20.3.0, Python 3.7.4 (v3.7.4:e09359112e, Jul 8 2019, 14:54:52) - [Clang 6.0 (clang-600.0.57)], pyOpenSSL 19.1.0 (OpenSSL 1.1.1g 21 Apr 2020), cryptography 3.1, Platform Darwin-18.7.0-x86_64-i386-64bit
    2020-09-17 11:54:11 [scrapy.crawler] INFO: Overridden settings:
    {'BOT_NAME': 'PLT',
    'CONCURRENT_REQUESTS': 14,
    'CONCURRENT_REQUESTS_PER_DOMAIN': 14,
    'DOWNLOAD_DELAY': 0.05,
    'LOG_LEVEL': 'INFO',
    'NEWSPIDER_MODULE': 'PLT.spiders',
    'SPIDER_MODULES': ['PLT.spiders']}
    2020-09-17 11:54:11 [scrapy.extensions.telnet] INFO: Telnet Password: # blocked
    2020-09-17 11:54:11 [scrapy.middleware] INFO: Enabled extensions:
    ['scrapy.extensions.corestats.CoreStats',
    'scrapy.extensions.telnet.TelnetConsole',
    'scrapy.extensions.memusage.MemoryUsage',
    'scrapy.extensions.logstats.LogStats']
    2020-09-17 11:54:12 [scrapy.middleware] INFO: Enabled downloader middlewares:
    ['scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
    'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
    'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
    'scrapy.downloadermiddlewares.retry.RetryMiddleware',
    'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
    'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
    'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
    'scrapy.downloadermiddlewares.stats.DownloaderStats']
    2020-09-17 11:54:12 [scrapy.middleware] INFO: Enabled spider middlewares:
    ['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
    'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
    'scrapy.spidermiddlewares.referer.RefererMiddleware',
    'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
    'scrapy.spidermiddlewares.depth.DepthMiddleware']
    =======
    17_Sep_2020_11_54_12
    =======
    2020-09-17 11:54:12 [scrapy.middleware] INFO: Enabled item pipelines:
    ['PLT.pipelines.PltPipeline']
    2020-09-17 11:54:12 [scrapy.core.engine] INFO: Spider opened
    2.结束日志行(信息:Dumping Scrapy stats to end)。
    2020-09-17 11:16:43 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
    {'downloader/request_bytes': 15842233,
    'downloader/request_count': 42031,
    'downloader/request_method_count/GET': 42031,
    'downloader/response_bytes': 1108804016,
    'downloader/response_count': 42031,
    'downloader/response_status_count/200': 41999,
    'downloader/response_status_count/403': 9,
    'downloader/response_status_count/404': 1,
    'downloader/response_status_count/504': 22,
    'dupefilter/filtered': 110,
    'elapsed_time_seconds': 3325.171148,
    'finish_reason': 'finished',
    'finish_time': datetime.datetime(2020, 9, 17, 10, 16, 43, 258108),
    'httperror/response_ignored_count': 10,
    'httperror/response_ignored_status_count/403': 9,
    'httperror/response_ignored_status_count/404': 1,
    'item_scraped_count': 20769,
    'log_count/INFO': 75,
    'memusage/max': 2707484672,
    'memusage/startup': 100196352,
    'request_depth_max': 2,
    'response_received_count': 42009,
    'retry/count': 22,
    'retry/reason_count/504 Gateway Time-out': 22,
    'scheduler/dequeued': 42031,
    'scheduler/dequeued/memory': 42031,
    'scheduler/enqueued': 42031,
    'scheduler/enqueued/memory': 42031,
    'start_time': datetime.datetime(2020, 9, 17, 9, 21, 18, 86960)}
    2020-09-17 11:16:43 [scrapy.core.engine] INFO: Spider closed (finished)
  • self.num_pages 变量使用什么值?

  • 我正在抓取的网站有大约 2 万种产品,每页显示 48 个。所以它转到站点,查看 20103 个产品,然后除以 48(然后是 math.ceil)以得到页数。
  • 更新中间件后添加爬取集线器的输出
  • downloader/request_bytes    2945159
    downloader/request_count 16518
    downloader/request_method_count/GET 16518
    downloader/response_bytes 3366280619
    downloader/response_count 16516
    downloader/response_status_count/200 16513
    downloader/response_status_count/404 3
    dupefilter/filtered 7
    elapsed_time_seconds 4805.867308
    finish_reason memusage_exceeded
    finish_time 1600567332341
    httperror/response_ignored_count 3
    httperror/response_ignored_status_count/404 3
    item_scraped_count 8156
    log_count/ERROR 1
    log_count/INFO 94
    memusage/limit_reached 1
    memusage/max 1074937856
    memusage/startup 109555712
    request_depth_max 2
    response_received_count 16516
    retry/count 2
    retry/reason_count/504 Gateway Time-out 2
    scheduler/dequeued 16518
    scheduler/dequeued/disk 16518
    scheduler/enqueued 17280
    scheduler/enqueued/disk 17280
    start_time 1600562526474

    最佳答案

    1.Scheruler 队列/事件请求
    self.numpages = 418 .
    此代码行将创建 418 个请求对象(包括 -to ask OS 委派内存以容纳 418 个对象)并将它们放入调度程序队列:

    for page in tqdm(range(1, self.num_pages+1)):
    url = 'www.example.com/page={page}'
    yield scrapy.Request(url = url, headers=headers, callback = self.prod_url)
    每个“页面”请求生成 48 个新请求。
    每个“产品页面”请求生成 1 个“api_call”请求
    每个“api_call”请求都返回项目对象。
    由于所有请求具有相同的优先级 - 在最坏的情况下,应用程序将需要内存一次在 RAM 中保存 ~20000 个请求/响应对象。
    为了排除这种情况 priority参数可以添加到 scrapy.Request .
    并且可能您需要将蜘蛛配置更改为如下所示:
        def start_requests(self):
    yield scrapy.Request(url = 'www.example.com/page=1', headers=headers, callback = self.prod_url)

    def prod_url(self, response):
    #get number of page
    next_page_number = int(response.url.split("/page=")[-1] + 1
    #...
    for url in urls_item['urls']:
    yield scrapy.Request(url = url, headers=headers, callback = self.parse, priority = 1)

    if next_page_number < self.num_pages:
    yield scrapy.Request(url = f"www.example.com/page={str(next_page_number)}"

    def parse(self, response) # Parse the main product page
    #....
    try:
    new_url = 'www.exampleAPI.com/' + item['active_col_num']
    except TypeError:
    new_url = 'www.exampleAPI.com/{dummy_number}'

    yield scrapy.Request(new_url, callback=self.parse_attr, meta={'item': item}, priority = 2)
    使用此蜘蛛配置 - 蜘蛛仅在处理完前一页的产品后才会处理下一页的产品页面,并且您的应用程序不会收到长队列的请求/响应。
    2.Http压缩
    许多网站压缩 html 代码以减少流量负载。
    例如,亚马逊网站使用 gzip 压缩其产品页面。
    亚马逊产品页面压缩html的平均大小~250Kb
    未压缩的 html 大小可以超过 ~1.5Mb。
    如果您的网站使用压缩并且未压缩 html 的响应大小与亚马逊产品页面的大小相似 - 应用程序将需要花费大量内存来保存压缩和未压缩的响应正文。
    DownloaderStats填充中间件 downloader/response_bytes stats 参数不会计算未压缩响应的大小,因为它是 process_response之前调用的方法 process_response HttpCompressionMiddleware的方法中间件。
    为了检查它,您需要通过将其添加到设置来更改下载器统计中间件的优先级:
    DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.stats.DownloaderStats':50
    }
    在这种情况下: downloader/request_bytes stats 参数 - 将减少,因为它不会计算中间件填充的某些 header 的大小。 downloader/response_bytes stats 参数 - 如果网站使用压缩,将大大增加。

    关于python - Scrapy 隐藏的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63936759/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com