(10)分布式下的爬虫Scrapy应该如何做-关于动态内容js或者ajax处理(2)
不知不觉这个系列就已经写到了第十篇,如果你跟着前面教程一步一步来,我想你对于scrapy的熟练的程度已经超过了很多人了,这个时候你可能会思考,如果我自己去写这样一个爬虫框架,我会怎么来写,如果是我,我对于队列,数据库的解析又应该怎么来处理呢。如果你能深入到这样的一个地步,我想你一定会进步很快,带着问题去学东西始终是最快的。好了,又啰嗦一些,我们进入今天的主题,关于js的解析。
关于动态内容的解析,对于各个爬虫新手来说一直是一个山,因为自己没有思路,所以大部分时候都被这个大山挡住了前进的脚步(ps:如果你系统的做过web前端或者后端,对于学习爬虫会有十分大的帮助)
通常来说,对于js的动态内容一般两种解法
分析请求的接口,得到数据直接操作(70%的情况可以这样操作)
使用js的环境来处理
这里,js的环境分为两种,一种是有界面的,一种是无界面的,有界面的可以参考selenium ,splinter 这些解决方案或多或少都会打开一个浏览器窗口,不利于进一步的操作。
另外一种就是大名鼎鼎的phantomjs,应用很多,不过和scrapy结合不够紧密。那scrapy有没有官方的操作方式呢?答案显然有的
先上github的地址:===>scrapyjs<====
因为是基于splash: ===>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的集群了,单独的集群来解析这些动态内容。不用想,各大搜索引擎也是这样类似的做法,没有区别的。
- 原文作者:大鱼
- 原文链接:https://brucedone.com/archives/560/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。