不知不觉这个系列就已经写到了第十篇,如果你跟着前面教程一步一步来,我想你对于scrapy的熟练的程度已经超过了很多人了,这个时候你可能会思考,如果我自己去写这样一个爬虫框架,我会怎么来写,如果是我,我对于队列,数据库的解析又应该怎么来处理呢。如果你能深入到这样的一个地步,我想你一定会进步很快,带着问题去学东西始终是最快的。好了,又啰嗦一些,我们进入今天的主题,关于js的解析。

   关于动态内容的解析,对于各个爬虫新手来说一直是一个山,因为自己没有思路,所以大部分时候都被这个大山挡住了前进的脚步(ps:如果你系统的做过web前端或者后端,对于学习爬虫会有十分大的帮助)

   通常来说,对于js的动态内容一般两种解法

分析请求的接口,得到数据直接操作(70%的情况可以这样操作)

   关于js的处理(一)

使用js的环境来处理

   这里,js的环境分为两种,一种是有界面的,一种是无界面的,有界面的可以参考selenium ,splinter 这些解决方案或多或少都会打开一个浏览器窗口,不利于进一步的操作。

   另外一种就是大名鼎鼎的phantomjs,应用很多,不过和scrapy结合不够紧密。那scrapy有没有官方的操作方式呢?答案显然有的

   先上github的地址:===>scrapyjs<====

   因为是基于splash:  ===>splash<====

 文档地址:===>doc for splash<===

  关于安装的过程,我就不安利了,按照官方提供的文档操作就可以了。(前提条件是你对docker有熟悉的感觉)

docker run -p 5023:5023 -d -p 8050:8050 --name=splash_server -p 8051:8051 scrapinghub/splash

-d 是以fork的方式运行,-name指定容器名字,-p 绑定端口 ,ok,docker已经运行起来了,那我们怎么访问呢?别急,我们先看看docker 的ip地址是多少

± |master ✓| → boot2docker ip 
192.168.59.103

然后我们直接访问http://192.168.59.103:8050/

可以看到scrapyjs服务已经起来了

比如你们一直想爬的微信数据的抓取方式,我们可以在render me里面直接输入URL:

http://weixin.sogou.com/weixin?type=2&query=%E4%BB%8A%E5%A4%A9&ie=utf8

然后出来的结果:

结果已经很明显的出来了,javascript render出来的内容,还能系统的反应出来统计的内容,根据内容我们可以进一步的过滤出来不要的内容,比如css,比如图片。

那在scrapy中我们怎么写呢?

直接上代码:

# -*- coding: utf-8 -*-
from scrapy.http import Request
from scrapy.spiders import Spider
from scrapy.http.headers import Headers
import json
from scrapy.log import logger


class WeiXinSpider(Spider):
    name = 'weixin'
    # main address since it has the fun list of the products
    start_urls = [
        'http://weixin.sogou.com/weixin?page={}&type=2&query=%E4%B8%AD%E5%9B%BD'.format(a) for a in xrange(1,10)
    ]

    allowed_domains = [
        'sogou.com'
    ]

    def __init__(self, *args, **kwargs):
         super(WeiXinSpider, self).__init__(*args, **kwargs)

    RENDER_HTML_URL = "http://192.168.59.103:8050/render.html"


    def start_requests(self):
        #text/html; charset=utf-8
        for url in self.start_urls:
            body = json.dumps({"url": url, "wait": 5,'images':0,'allowed_content_types':'text/html; charset=utf-8'})
            headers = Headers({'Content-Type': 'application/json'})
            yield Request(self.RENDER_HTML_URL, self.parse, method="POST",
                                 body=body, headers=headers)
        pass

    def parse(self, response):
        self.logger.info('now you can see the url %s' % response.url)
        div_results = response.xpath('//div[@class="results"]/div')
        if not div_results:
            logger.error(msg='there is not any body in the %s' % response.body)
            return
        for div_item in div_results:
            title = div_item.xpath('descendant::div[@class="txt-box"]//h4//text()')
            if title:
                txt = ''.join(title.extract())
                yield {'title':txt}

输出的结果:

{'title': u'\n\u3010\u672c\u671f\u9884\u544a\u3011APEC\u670d\u88c5\u8bbe\u8ba1\u5e08\u5982\u4f55\u5c55\u793a\u4e2d\u56fd\u670d\u9970\u4e4b\u7f8e?\n'}
2016-03-07 21:34:15 [scrapy] DEBUG: Scraped from <200 http://192.168.59.103:8050/render.html>
{'title': u'\n\u3010889\u4f53\u575b\u5feb\u62a5\u3011\u63d0\u524d\u51fa\u7ebf!\u4e2d\u56fd\u5973\u8db31-0\u97e9\u56fd\u65f6\u96948\u5e74\u91cd\u8fd4\u5965\u8fd0\u4f1a\n'}
2016-03-07 21:34:15 [scrapy] DEBUG: Scraped from <200 http://192.168.59.103:8050/render.html>
{'title': u'\n\u4e2d\u56fd\u6240\u6709\u8001\u4e2d\u533b\u7684\u96c6\u4f53\u7ed3\u6676,\u6700\u597d\u80cc\u4e0b\u6765!\n'}
2016-03-07 21:34:15 [scrapy] DEBUG: Scraped from <200 http://192.168.59.103:8050/render.html>
{'title': u'\n\u4e2d\u56fd\u91d1\u6728\u540c\u6e90\u5802\u6da6\u517b\u9aa8\u9ad3\u7684\u9886\u822a\u8005\n'}
2016-03-07 21:34:15 [scrapy] DEBUG: Scraped from <200 http://192.168.59.103:8050/render.html>
{'title': u'\n2016\u5e74\u4e2d\u56fdVR\u884c\u4e1a\u5e02\u573a\u73af\u5883\u559c\u5fe7\u53c2\u534a \u5934\u6761\u6570\u8bfb\n'}
2016-03-07 21:34:15 [scrapy] DEBUG: Scraped from <200 http://192.168.59.103:8050/render.html>
{'title': u'\n\u94ff\u9535\u73ab\u7470 \u534e\u4e3d\u7efd\u653e\u4e28\u8d3a\u4e2d\u56fd\u5973\u8db3\u6210\u529f\u664b\u7ea72016\u91cc\u7ea6\u5965\u8fd0\u4f1a\n'}
2016-03-07 21:34:15 [scrapy] DEBUG: Scraped from <200 http://192.168.59.103:8050/render.html>
{'title': u'\n\u5168\u7403\u5341\u4e2a\u6700\u5bb9\u6613\u53d1\u751f\u4e00\u591c\u60c5\u7684\u5730\u65b9 \u4e2d\u56fd\u7adf\u662f\u8fd9\u91cc!\n'}
2016-03-07 21:34:15 [scrapy] DEBUG: Scraped from <200 http://192.168.59.103:8050/render.html>
{'title': u'\n\u8d70\u8fdb\u4e2d\u5c71\u5927\u5b66   \u5e9e\u4e2d\u82f1\u8c08\u4e2d\u56fd\u4e0e\u4e16\u754c\u79e9\u5e8f\n'}
2016-03-07 21:34:15 [scrapy] INFO: Closing spider (finished)
2016-03-07 21:34:15 [scrapy] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 4986,
'downloader/request_count': 9,
'downloader/request_method_count/POST': 9,
'downloader/response_bytes': 352723,
'downloader/response_count': 9,
'downloader/response_status_count/200': 9,
'finish_reason': 'finished',
'finish_time': datetime.datetime(2016, 3, 7, 13, 34, 15, 332505),
'item_scraped_count': 89,
'log_count/DEBUG': 100,
'log_count/ERROR': 2,
'log_count/INFO': 16,
'response_received_count': 9,
'scheduler/dequeued': 9,
'scheduler/dequeued/memory': 9,
'scheduler/enqueued': 9,
'scheduler/enqueued/memory': 9,
'start_time': datetime.datetime(2016, 3, 7, 13, 33, 18, 174402)}
2016-03-07 21:34:15 [scrapy] INFO: Spider closed (finished)

这样是不是很方便的解决了javascript 各种麻烦?那有没有不方便的地方呢?当然有,你交给渲染引擎来操作这些内容 ,那就要速度与性能的损耗,如果在大规模的抓取中,就要考虑splash的集群了,单独的集群来解析这些动态内容。不用想,各大搜索引擎也是这样类似的做法,没有区别的。