Python线程 - 内部缓冲区错误 - 内存不足

时间:2015-01-16 12:47:15

标签: python beautifulsoup out-of-memory web-crawler python-multithreading

我使用urllib和beautifulsoup制作了一个简单的网页抓取工具,从网页上的表格中提取数据。为了加快数据拉取,我尝试使用线程,但是我收到以下错误: “内部缓冲区错误:内存分配失败:增长缓冲区” 此消息出现了很多次,然后说明: “记忆力不足”

感谢您的帮助。

from bs4 import BeautifulSoup
from datetime import datetime
import urllib2
import re
from threading import Thread

stockData = []

#Access the list of stocks to search for data
symbolfile = open("stocks.txt")
symbolslist = symbolfile.read()
newsymbolslist = symbolslist.split("\n")

#text file stock data is stored in
myfile = open("webcrawldata.txt","a")

#initializing data for extraction of web data
lineOfData = ""
i=0

def th(ur):
    stockData = []
    lineOfData = ""
    dataline = ""
    stats = ""
    page = ""
    soup = ""
    i=0
    #creates a timestamp for when program was won
    timestamp = datetime.now()
    #Get Data ONLINE
    #bloomberg stock quotes
    url= "http://www.bloomberg.com/quote/" + ur + ":US"
    page = urllib2.urlopen(url)
    soup = BeautifulSoup(page.read())
    #Extract key stats table only
    stats = soup.find("table", {"class": "key_stat_data" })
    #iteration for <tr>
    j = 0
    try:
        for row in stats.findAll('tr'):
            stockData.append(row.find('td'))
            j += 1
        except AttributeError:
            print "Table handling error in HTML"
    k=0
    for cell in stockData:
        #clean up text
        dataline = stockData[k]
        lineOfData = lineOfData + " " + str(dataline)
        k += 1
    g = str(timestamp) + " " + str(ur)+ ' ' + str(lineOfData) + ' ' +  ("\n\n\n")    
    myfile.write(g)
    print (ur + "\n")
    del stockData[:]
    lineOfData = ""
    dataline = ""
    stats = None
    page = None
    soup = None
    i += 1

threadlist = []

for u in newsymbolslist:
    t = Thread(target = th, args = (u,))
    t.start()
    threadlist.append(t)

for b in threadlist:
    b.join()enter code here

2 个答案:

答案 0 :(得分:2)

您启动的每个线程都有一个线程堆栈大小,即8 kb Linux系统中的默认值(参见ulimit -s),因此线程所需的内存总量将超过20千兆字节。

您可以使用一个线程池,例如10个线程;什么时候 已经完成了它的工作,它还有另一个任务要做。

但是:一般来说,运行比CPU内核更多的线程是无稽之谈。所以我的 建议是停止使用线程。您可以使用gevent之类的库 在不使用OS级线程的情况下完成同样的事情。

关于gevent的好处是猴子修补:你可以告诉gevent 要改变Python标准库的行为,这将改变你的行为 透明地线程化为“greenlet”对象(请参阅gevent文档 更多细节)。 gevent提出的并发性是 特别适用于您正在进行的密集I / O.

在您的代码中,只需在开头添加以下内容:

from gevent import monkey; monkey.patch_all()

在a上同时打开的文件描述符不能超过1024个 默认情况下Linux系统(参见ulimit -n)所以你必须增加 如果您希望同时打开2700个文件描述符,则可以使用此限制。

答案 1 :(得分:0)

不要重新发明轮子并使用Scrapy web-scraping framework

  

Scrapy是一个用于抓取网站的应用程序框架   提取可用于各种各样的结构化数据   有用的应用程序,如数据挖掘,信息处理或   历史档案。

考虑一下 - 可扩展性/并行化/性能问题为您解决 - 您是否真的希望继续潜入python中Thread的精彩世界,让您的代码闻起来怀疑地,达到了CPU和内存限制,处理冲突,最终使代码无法调试和维护 - 而不是专注于提取和收集数据?而且,即使使用gevent,我怀疑您的最终代码在任何方面都会更加简单和可读,就像您基于Scrapy实现的那样。为什么不使用经过大量用户测试和使用的工具,这些用户被证明是在网络搜索python世界中创建的最佳工具?

这是一个工作蜘蛛,它以类似的方式擦除引号:

from scrapy.spider import Spider
from scrapy.item import Item, Field
from scrapy.http import Request


class BloombergItem(Item):
    ur = Field()
    parameter = Field()
    value = Field()


class BloombergSpider(Spider):
    name = 'bloomberg'
    allowed_domains = ['www.bloomberg.com']

    def start_requests(self):
        with open("stocks.txt") as f:
            for ur in f:
                yield Request("http://www.bloomberg.com/quote/%s:US" % ur)

    def parse(self, response):
        for parameter in response.css('table.key_stat_data tr'):
            item = BloombergItem()
            item['ur'] = response.xpath('//title/text()').extract()[0].split(':')[0]
            item['parameter'] = parameter.xpath('th/text()').extract()[0]
            item['value'] = parameter.xpath('td/text()').extract()[0]
            yield item

如果stocks.txt的内容是:

AAPL

蜘蛛会输出(例如,如果您选择以JSON格式输出):

[
  {
    "parameter": "Current P/E Ratio (ttm)",
    "value": "16.6091",
    "ur": "AAPL"
  },
  {
    "parameter": "Estimated P/E(09/2015)",
    "value": "13.6668",
    "ur": "AAPL"
  },
  {
    "parameter": "Relative P/E vs.",
    "value": "0.9439",
    "ur": "AAPL"
  },
  ...
]

开始使用Scrapy的好地方是Scrapy Tutorial