为什么需要开发下载器
定向抓取时,目标站点的数据不能单次请求获取,需要3,4次或者更多,请求之间有依赖关系,就是需要连续请求完成这个下载事物
前面讲过的 js 动态页面下载
......
注意事项
下载器的不可以影响 twisted 框架本身的异步机制
与 scrapy 原有的下载器调用接口一致,符合插件规范
其他插件依赖的状态要保留
下载器开发代码(我把注意的地方直接写在代码中,方便查看)
1. 新建项目
# 创建项目scrapy startproject jstest# 创建蜘蛛scrapy genspider -t basic testSpider 'sina.com.cn'
2. 修改蜘蛛文件 testSpider.py
from scrapy.spider import Spiderclass TestspiderSpider(Spider): name = "testSpider" allowed_domains = ["sina.com.cn"] start_urls = ( 'http://www.sina.com.cn/', ) def parse(self, response): print response.body print 'download_latency:', response.meta['download_latency']
3. 创建 jstest/handler 目录,新建文件 mydownloader.py
参照 /usr/local/lib/python2.7/dist-packages/scrapy/core/downloader/handlers/http11.py 文件
#!/usr/bin/env python# encoding: utf-8import refrom time import timefrom cStringIO import StringIOfrom urlparse import urldefragfrom zope.interface import implementsfrom twisted.internet import defer, reactor, protocolfrom twisted.web.http_headers import Headers as TxHeadersfrom twisted.web.iweb import IBodyProducerfrom twisted.internet.error import TimeoutErrorfrom twisted.web.http import PotentialDataLossfrom scrapy.xlib.tx import Agent, ProxyAgent, ResponseDone, HTTPConnectionPool, TCP4ClientEndpointfrom scrapy.http import Headersfrom scrapy.responsetypes import responsetypesfrom scrapy.core.downloader.webclient import _parsefrom scrapy.utils.misc import load_objectfrom scrapy.http import HtmlResponsefrom twisted.internet import utilsclass MyLogicDownloader(object): ''' 定制下载逻辑 ''' def __init__(self, agent=None): '''agent: 异步下载代理''' self._agent = agent def download(self, request): ''' 需要异步返回,不可以阻塞,本例子的演示直接调用 phantomjs的一个简单包装脚本 ''' begintime = time() d = self._download(request) d.addCallback(self.parseData, request, begintime) print '证明我是异步的' return d def _download(self, request): '''使用twsited 的函数创建异步进程调用''' d = utils.getProcessOutput('scrapyweb.js', args=(request.url, '24000'), reactor=reactor) def getOutput(result): return result d.addCallback(getOutput) return d def parseData(self, htmldoc, request, begintime): '''解析函数,当请求完成后被调用''' # 这个下载时间在调整下载速度的扩展 AutoThrottle 中被使用 request.meta['download_latency'] = time() - begintime return HtmlResponse(request.url, body=htmldoc + '\n证明我被解析过', request=request)class MyDownloadHandler(object): ''' 下载接口, 被上层所调用 ''' def __init__(self, settings): self._pool = HTTPConnectionPool(reactor, persistent=True) self._pool.maxPersistentPerHost = settings.getint('CONCURRENT_REQUESTS_PER_DOMAIN') self._pool._factory.noisy = False self._contextFactoryClass = load_object(settings['DOWNLOADER_CLIENTCONTEXTFACTORY']) self._contextFactory = self._contextFactoryClass() def download_request(self, request, spider): '''下载的主要被调用接口(异步),返回 deferred (twisted 的延迟回调对象)''' myDownloader = MyLogicDownloader() return myDownloader.download(request) def close(self): return self._pool.closeCachedConnections()
4. 添加配置到 settings.py
DOWNLOAD_HANDLERS = { 'http': 'jstest.handler.mydownloader.MyDownloadHandler' }
5. 在系统目录下新建一个 phantomjs 包装脚本 scrapyweb.js,并添加可执行权限
#!/usr/bin/env phantomjsif (phantom.args.length >= 1){ var url = phantom.args[0]; var timeOut = 10000; if (phantom.args.length == 2){ timeOut = Math.min(30000, Math.max(0, phantom.args[1])); } var page = require('webpage').create(); page.customHeaders = { 'Accept-Language': 'zh-CN,zh;q=0.8', 'Connection': 'keep-alive', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'DNT': '1' }; page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.76 Safari/537.36'; page.open(encodeURI(url), function(status){ if (status != 'success'){ console.log('Err, status=' + status); phantom.exit(1); } console.log(page.content); phantom.exit(); }); setTimeout(function(){ console.log(page.content); phantom.exit(); }, timeOut);}else { console.log('Usage:'); console.log('\tphantomjs scrapyweb.js url timeout'); phantom.exit(1);}
6. 运行
scrapy crawl testSpider
需要注意的问题
例子是下载 js 动态页面的例子。如果需要复杂些的连续下载,需要按照 scrapy 框架的 http11.py 文件修改
request.meta['download_latency'] 需要赋值,这个调整下载速度的 AutoThrottle 扩展依赖这个值判定
原文链接: