如何用1g内存服务器去爬取一个大型网站的嵌套数据(一步步解决out of memery)

题主最近想自己爬取某网站的全网数据,本以为很简单!数据库选择了mysql,语言选择了python

此网站结构如下;

第一层:主列表分为中国籍、美籍、日本等等 每页30条

第二层:A-Z 字母代表每个人的姓氏开头的人的列表 每个栏目下乐有300页 每页200条 这边爬取数据较大,每个A-Z都生成了单独的表

第三层:为每个人下属的作品列表 这个约10页 每页30条

第四层:为每个作品的详情 


题主开始:首先每一层,都有一个爬取的方法,并且每一层的调用都是递归调用,除了第四层之外,都是通过page_no和total_page做比较,然后递归,python的大致代码如下:

def load_list(url,pageNo,params):

        data_list = load_url(url,pageNo,params)

        data_list_json = json.loads(data_list)

        total_page = data_list_json.get("totalPage")

        data_list = data_list_json.get("data")

       for data in data_list:

                //下一层的逻辑

        if total_page>pageNo:

                load_list(url,pageNo,params)

题主就开始在windows机器上跑了,遇到了一个坑跑了10分钟,竟然卡住了,进程没有退出,后来研究发现python中urlopen需要设置超时参数,否则一致处于等待状态,

改动过后准确无误,准备上线(ubuntu 16.04系统),由于本人的服务器只有1G内存,没想跑了一段时间就自动挂了

查看系统日志/var/log/syslog 发现原因是进程out of memery 被系统进程杀了


通过各种python性能检测工具发现,代码并没有问题,脑子一转,猜测是由于各种递归,由于递归调用层级多,每层数量有多,导致每次递归没有处理完,内存就out of memery了!

后台想了 准备引入redis 代码如下:

def load_list(url,pageNo,params):

        data_list = load_url(url,pageNo,params)

        data_list_json = json.loads(data_list)

        total_page = data_list_json.get("totalPage")

        data_list = data_list_json.get("data")

       for data in data_list:

                //下一层的逻辑

        if total_page>pageNo:

                RedisUtils.push(层级名,"%s_%s_%s" % (url,pageNo,params))

然后每一层新启一个守护线程:

class Thread(threading.Thread):
  
    def run(self):
        while True:
            val = RedisUtils.pop(RedisUtils.NAME)
            if val:
                val_list = val.split("_")
                if len(val_list) is 3:
                    Data.load_url(val_list[0], val_list[1], val_list[2])
            else:
                time.sleep(3)
这样你会发现,虽然有了线程,但是通用以mysql连接,是会报
Lost connection to MySQL server during query

这样我又把线成做了一线改动

class Thread(threading.Thread):
  def __init__(self):
    threading.Thread.__init__(self)
    self.db = MySqlUtils.Mysql(MySqlUtils.db_name)
    pass
def run( self): while True: val = RedisUtils.pop(RedisUtils.NAME) if val: val_list = val.split( "_") if len(val_list) is 3: Data.load_url(val_list[ 0] , val_list[ 1] , val_list[ 2]) else: time.sleep( 3)

结果mysql是不报错了,但是发现最后一层的那个线程总是不能及时执行,突然想到,python存在一个全局锁的概念

最后决定采用进程去处理,并且编写了以shell脚本去启动每个进程,至此,一项简单的项目就不简单了

                    print "End loadMusicThread:" + val + str(datetime.now())
 

补充:后面继续监控服务器信息,发现,redis-server老是挂,由于redis是内存数据库数据增多到时oom(out of memery)被ubuntu系统监控进程杀死,最后鉴于服务器硬件水平,在redis.push之前加了一条

while 10000 <= r.llen(key):

time.sleep(60)

可以说鉴于貌似完美的解决了问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值