老规矩,先把相关的git地址上齐
scrapyjs:===>scrapyjs<====
splash: ===>splash<====
在上一篇文档:关于动态js或者ajax的处理,我简单的说了一下splash的使用,如果融入到scrapy中,我们需要使用官网的提供做法,先安装scrapyjs库,然后安装好splash等docker 。这次的例子我们还是选择搜狗的微信搜索
准备环境
先安装scrapy-splash库:
1 |
pip install scrapy-splash |
然后将我们的docker起起来
1 |
docker run -p 8050:8050 scrapinghub/splash |
如果关于docker安装还有更多的问题,请查考:
scrapy配置
- 将splash server的地址放在你的settings.py文件里面,如果是你在本地起的,那地址应该是http://127.0.0.1:8050,我的地址如下
1SPLASH_URL = 'http://192.168.99.100:8050' - 在你的下载器中间件:download_middleware 里面启用如下的中间文件,注意启用的顺序
12345DOWNLOADER_MIDDLEWARES = {'scrapy_splash.SplashCookiesMiddleware': 723,'scrapy_splash.SplashMiddleware': 725,'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,}
另外注意:
scrapy_splash.SplashMiddleware(725)的顺序是在默认的HttpProxyMiddleware(750)之前,要不然顺序的紊乱会造成功能的紊乱的
HttpCompressionMiddleware的优先级和顺序也应该适当的更改一下,这样才能更能处理请求 查看:https://github.com/scrapy/scrapy/issues/1895 .里面提到的一些问题
- 在settings.py启用SplashDeduplicateArgsMiddleware中间件
123SPIDER_MIDDLEWARES = {'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,} - 我们来设置一个去重的类
1DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter' - 如果你使用scrapy http 缓存系统,那你就有必要启用这个scrapy-splash的缓存系统
1HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
该bug 是由于default_request_headers 里面的host 与我要爬的sougou不匹配,这当然会出错,不得不说scrapy的官方维护人反应真的很迅速。大家添加的headers的时候注意这些细节内容。
编写代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# -*- coding: utf-8 -*- from scrapy import Request from scrapy.spiders import Spider from scrapy_splash import SplashRequest from scrapy_splash import SplashMiddleware 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) def start_requests(self): #text/html; charset=utf-8 for url in self.start_urls: yield SplashRequest(url ,self.parse ,args={'wait':'0.5'} #,endpoint='render.json' ) 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: self.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} |
代码分析
其实现在SplashRequest就是对splash http api的另一层封装,有人问了,如果我不想使用scrapy ,只使用requests应该怎么玩呢,简单呀
1 2 3 4 5 6 7 8 9 10 11 12 |
import requests import json def get_content_from_splash(): render_html = 'http://192.168.99.100:8050/render.html' url = 'http://www.cnblogs.com' body = json.dumps({"url": url, "wait": 5,'images':0,'allowed_content_types':'text/html; charset=utf-8'}) headers = {'Content-Type': 'application/json'} print requests.post(url=render_html,headers=headers,data=body).text if __name__ == '__main__': get_content_from_splash() |
几行代码轻松实现,关于实现的原理,我就不多讲了,感觉没有啥特别的。
实际运行
兄弟你这scrapy如何实现分布式的啊?
分布式之前在群里面分享过一次,我先整理一下,这个周会更新一次分布式的架构 。感谢支持。
兄弟,你结合的分布式是用的scrapy-redis这个包吗?用了splash后,每次发出一个链接的请求,都会先post到render,在发送我原本的链接请求,这样去重队列就会生成两个fingerprint,有没有办法只生成一个就可以了吗?否则对内存伤害太大。
我是基于scrapy自己写的分布式框架,你说的情况其实splash_request本质也会生成一种类似render.html + taget_url 的形式,其实也可以提取出来。
博主,你用scrapy+splash做过爬取AJAX分页类型的网站没?(mouse_click)。 求分享经验。
splash经常莫名其妙的崩溃不知道博主遇到过没有
目前还没有遇到过,你有装最新版的镜像吗?
robots.txt 禁爬,修改 配置 COOKIES_ENABLED = False ROBOTSTXT_OBEY = False
你好,我用博主的第一种介绍的SplashRequest方法去获取http://www.iqiyi.com/v_19rr9dz99o.html页面上的“播放”次数时候,并没有渲染js,仅仅获取了网页源码。跟用chrome浏览器查看网页源代码时候得到的结果一样。请问可能是哪里出了问题呢?求指教
还要分析请求,他的数据是从某个地方来的,看具体的请求,参考前面一篇分析ajax的。