美文网首页python办公自动化
scrapy+scrapy_splash + docker爬取J

scrapy+scrapy_splash + docker爬取J

作者: lizb | 来源:发表于2019-07-09 15:10 被阅读0次

    最近一段时间做了一个特别恶心的项目,先来吐槽一下,项目需求大致就是给网址分类,鉴别出它是属于什么类型的网站,比如娱乐游戏、音乐影视、新闻咨询等。可能有的公司是用AI来鉴别的,但我们没有那么高大上,用的就是土办法,爬取网页的特定内容,比如title、keywords,description等,中文分词后,和每个类型的核心词对比(当然核心词是有权重的),从而鉴别出网址所属分类。这只是项目的最后一道环节,网址还需要通过前面的几道环节,例如备案等,这里就不再详述,总的来说准确率还是不错的,粗略的算差不多90%左右。其中最恶心的地方就是这个网址源,其来源不方便说,其中有些是与后台的交互Json,有些网址还有乱码,有些还带有中文,总的来说50%左右是打不开的,真的算是最恶劣的数据源,恶心到吐。
    言归正传,想到这种大规模的网址爬取,我们采用了scrapy框架,但是这个框架只能爬取静态的网页,有一部分网页是JS动态加载的,所以单用scrapy就不行了,通过查找资料,发现通过scrapy +scrapy_splash+docker可以达到我们的要求,于是撸起袖子就开始干了。
    总共有两步:
    1. 完成scrapy和scrapy_splash的对接
    2. docker环境的搭建

    一、scrapy项目中接入scrapy_splash

    这一步非常简单,我这里python的版本是3.6.3,我们只需要通过pip安装scrapy_splash库即可:

    pip install scrapy_splash
    

    因为我之前是通过scrapy爬取的静态网页,不了解scrapy框架的需要先了解一下,然后在我们请求网址的时候将原来的scrapy.Request替换为SplashRequest:

    
    from scrapy_splash import SplashRequest
    
        def start_requests(self):
            # 拿取网址源数据
            self.urls = self.db.get_url()
    
            for key, value in self.urls.items():
                # request = scrapy.Request(value, callback=self.my_parse, dont_filter=True)
                request = SplashRequest(value, self.my_parse, args={'wait': 3}, dont_filter=True)
                # 将id保存在meta中
                request.meta['param'] = key
                yield request
    
    

    接下来需要在settings.py文件中加入以下内容:

    
    SPIDER_MIDDLEWARES = {
       'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
    }
    
    DOWNLOADER_MIDDLEWARES = {
        'scrapy_splash.SplashCookiesMiddleware': 723,
        'scrapy_splash.SplashMiddleware': 725,
        'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,  # 不配置查不到信息
    }
    
    HTTPCACHE_ENABLED = True
    HTTPCACHE_EXPIRATION_SECS = 0
    HTTPCACHE_DIR = 'httpcache'
    
    SPLASH_URL = "http://localhost:8050/"  # 自己安装的docker里的splash位置
    DUPEFILTER_CLASS = "scrapy_splash.SplashAwareDupeFilter"
    HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
    
    

    OK,这样就完成了scrapy和scrapy_splash的对接,是不是很简单。

    二、docker环境的搭建

    对于docker环境的搭建,确实需要费一番力气,如果你的服务器是centOS 7以上的还好,我这里是centOS 6.5,CentOS 6.5 的内核一般都是2.6,在2.6的内核下,Docker运行会比较卡,所以一般会选择升级到更高版本。通过uname -r 命令查看你的内核版本号:

    # uname -r
    2.6.32-696.el6.x86_64
    

    现在我们先来升级内核版本:

    1、导入key(需要root权限)

    # rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
    

    问题1:如果报错 curl: (6) Couldn't resolve host 'www.elrepo.org' 则表示DNS解析有问题,需要配置:

    # vi /etc/sysconfig/network-scripts/ifcfg-eth0
    

    在末尾添加DNS配置,如下:

    DNS1=114.114.114.114
    DNS2=8.8.8.8
    

    查看nameserver是否显示正确:

    # cat /etc/resolv.conf | grep names
    nameserver 114.114.114.114
    nameserver 8.8.8.8
    

    然后重新导入key。

    问题2:如果报curl: (35) SSL connect error错误则输入

    # yum update nss
    

    DNS配置完成,重新运行

    # rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
    

    2、安装ELRepo到CentOS

    # rpm -Uvh http://www.elrepo.org/elrepo-release-6-8.el6.elrepo.noarch.rpm
    

    3、安装内核

    # yum --enablerepo=elrepo-kernel install kernel-lt –y
    

    4、修改引导文件,修改为default=0

    # vi /etc/grub.conf
    

    5、重启查看版本

    # uname -r
    4.4.184-1.el6.elrepo.x86_64
    

    经过上面的步骤,我们升级完了内核版本,接下来,我们就来安装docker。
    6、安装docker

    # yum install docker-io
    

    如果提示错误:No package docker-io available,则运行

    # yum -y install http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
    

    7、启动docker

    # service docker start
    

    8、查看docker版本

    #docker version
    Client:
     Version:      17.09.1-ce
     API version:  1.32
     Go version:   go1.8.3
     Git commit:   19e2cf6
     Built:        Thu Dec  7 22:21:47 2017
     OS/Arch:      linux/amd64
    
    Server:
     Version:      17.09.1-ce
     API version:  1.32 (minimum version 1.12)
     Go version:   go1.8.3
     Git commit:   19e2cf6
     Built:        Thu Dec  7 22:28:28 2017
     OS/Arch:      linux/amd64
     Experimental: false
    

    9、镜像加速

    # vim /etc/docker/daemon.json
    

    打开以上路径下的daemon.json文件,如果没有的话就创建一个,添加以下内容:

    {
      "registry-mirrors": ["http://hub-mirror.c.163.com"]
    }
    

    OK,到现在,docker环境已经搭建完成,可能不同的机器会遇到不同的问题,如果在线安装docker不行,可以离线安装,最终目的都是要把docker服务给启动起来,而启动docker服务也不尽相同,有些是直接dockerd命令,而有些是service docker start命令。
    启动docker服务之后,我们的工作还没完,我们得创建并运行一个容器来让我们的爬虫程序得以使用。

    10、下载scrapy_splash镜像

    # sudo docker pull scrapinghub/splash
    Using default tag: latest
    latest: Pulling from scrapinghub/splash
    7b722c1070cd: Pull complete 
    5fbf74db61f1: Pull complete 
    ed41cb72e5c9: Pull complete 
    7ea47a67709e: Pull complete 
    b9ea67282e79: Pull complete 
    8d0589f2b410: Pull complete 
    11f417145dc7: Pull complete 
    14d670a8125e: Pull complete 
    81d8bf1e3bdc: Pull complete 
    Digest: sha256:ec1198946284ccadf6749ad60b58b2d2fd5574376857255342a913ec7c66cfc5
    Status: Downloaded newer image for scrapinghub/splash:latest
    

    11、创建并运行容器

    # sudo docker run -p 8050:8050 scrapinghub/splash
    

    如果要让容器在后台运行(大多数情况下需要这样),则在后面加上 & 。这里可以看到,我们的容器占用的端口是8050,所以之前我们在配置scrapy_splash的setting.py中才有这样一句代码:

    SPLASH_URL = "http://localhost:8050/"  # 自己安装的docker里的splash位置
    

    我们在浏览器中访问服务器的8050端口,就会出现这样的页面:


    好了,到目前为止,所有的工作都已经完成,只要启动我们的爬虫,就会通过这个docker容器来渲染JS动态的页面,然后将页面信息返回给我们。
    前面在讲scapy和scrapy_splash对接的时候,都是讲的关键部分,至于数据源从哪里拿,爬取之后的网页内容怎么处理,这个需要按照自己的业务来,我们在做的时候网址源是从数据库拿取的,对返回的网页内容也是直接写到数据库里面,这里就不再详述了。

    三、docker的常用命令操作
    
    docker images #列出镜像
    docker ps -a #列出容器
    docker info #查看docker的信息
    docker inspect CONTAINERID | IMAGEID #查看容器或镜像的详细信息
    docker stop CONTAINERID #停止正在运行的容器 (默认等待10秒钟再杀死指定容器。可以使用-t参数来设置等待时间。)
    docker rename oldname newname #重命名容器
    docker restart CONTAINERID #重启容器
    docker rm CONTAINERID #删除容器
    docker rmi IMAGEID #删除镜像
    docker stats #查看容器占用的内存情况
    
    
    四、使用docker可能遇到的问题

    接下来讲一下使用这个docker可能遇到的问题,我们在使用过程中发现容器会中途异常退出,而且退出的状态码为139,网上查找了好一段时间,到现在都还没发现到底是什么原因,有些说是底层C库的原因,有的说是内存的问题,遇到这个问题的也很少,本人也是接触docker不多,希望知晓的大牛可以指教一下。不过也不是解决不了,我们可以“曲线救国”,也就是监控容器的状态,当异常退出的时候重启它就可以了(^ _ ^)。
    网上有专门的工具框架来解决这个问题,有兴趣的可以自己去了解一下。
    还有一个问题就是当大规模爬取的时候,内存会占用越来越大,当内存过大的时候是会导致容器退出的,对于这个内存问题,可以自己写个监控程序,当达到阈值的时候就重启一下容器,释放内存即可:

    import os, time
    
    def get_mem_usage_percent():
        try:
            f = open('/proc/meminfo', 'r')
            for line in f:
                if line.startswith('MemTotal:'):
                    mem_total = int(line.split()[1])
                elif line.startswith('MemFree:'):
                    mem_free = int(line.split()[1])
                elif line.startswith('Buffers:'):
                    mem_buffer = int(line.split()[1])
                elif line.startswith('Cached:'):
                    mem_cache = int(line.split()[1])
                elif line.startswith('SwapTotal:'):
                    vmem_total = int(line.split()[1])
                elif line.startswith('SwapFree:'):
                    vmem_free = int(line.split()[1])
                else:
                    continue
            f.close()
        except:
            return None
        physical_percent = usage_percent(mem_total - (mem_free + mem_buffer + mem_cache), mem_total)
        virtual_percent = 0
        if vmem_total > 0:
            virtual_percent = usage_percent((vmem_total - vmem_free), vmem_total)
        return physical_percent, virtual_percent
    
    
    def usage_percent(use, total):
        try:
            ret = (float(use) / total) * 100
        except ZeroDivisionError:
            raise Exception("ERROR - zero division error")
        return ret
    
    statvfs = os.statvfs('/')
    
    while True:
        mem_usage = get_mem_usage_percent()
        mem_usage = int(mem_usage[0])
        if mem_usage > 75:
            # 将containerID换成自己的容器ID。
            os.system(r"(docker restart containerID)")
        time.sleep(5)
    

    注意将containerID换成自己的容器ID。

    相关文章

      网友评论

        本文标题:scrapy+scrapy_splash + docker爬取J

        本文链接:https://www.haomeiwen.com/subject/rlvtkctx.html