本篇将谈一些scrapy的进阶内容,帮助你们能更熟悉这个框架。css
如今的大网站基本除了pc端都会有移动端,因此须要先肯定爬哪一个。html
好比爬新浪微博,有如下几个选择:python
上面三个中,主站的微博数据是动态加载的,意味着光看源码是看不到数据的,想爬的话要么搞清楚其api访问状况,要么模拟js,那样的话花的力气可能就有点多了。weibo.cn是一个简化版,数据能直接从网页源代码中解析出来,但使用正则或xpath取网页中的元素仍然是无聊且费时的,更不用说偶尔的页面结构错误更让人抓狂。 相比之下,移动版的爬虫最好写,由于移动版能直接拿到json格式的数据。mysql
通常来讲,有移动版的网站优先爬移动版,会节省不少力气。git
如今须要登陆才能正常浏览的网站的愈来愈多了,对爬虫也愈来愈不友好...因此模拟登陆在很多场合都是必不可少的。github
首先,最简单的模拟登陆是只有用户名密码的登陆。这时候只须要在发送第一个请求时加上登陆表单数据便可:redis
def start_requests(self): return scrapy.FormRequest( formdata={'username': '***', 'password': '***'}, callback=self.after_login )
若是不知道登陆页面是哪个的话,也能够在返回第一个请求后登陆:sql
def parse(self, response): return scrapy.FormRequest.from_response( response, formdata={'username': '***', 'password': '***'}, callback=self.after_login )
为了保持登陆,注意cookie是不能关闭的(默认状况是开着的,能够在settings.py中设置)。mongodb
若是须要验证码的话,网上有一些提取分析验证码图片的包,能够提取出来而后手动输入验证码。chrome
上面只是一些简单的登陆状况,若是验证码很变态(好比须要鼠标滑动)或者登陆过程很复杂,须要各类加密(好比新浪微博pc端的登录)的话,模拟登陆确实是个很让人头大的问题。这时候有另外一个通用办法,那就是cookie模拟登陆。网站想知道咱们的登陆状态,都是经过cookie来确认的,因此咱们只须要在每次request的时候都附带上cookie便可实现已登陆的效果。
那么,如何得到cookie呢?有chrome的能够F12打开Network界面,这时候人工网页登陆,便可在headers中看到cookie。获得cookie后,只须要在request中加入本身的cookie便可。
self.cookie = {"_T_WM": self.cookie_T_WM, ... "SSOLoginState": self.cookie_SSOLoginState} return Request(url, cookies=self.cookie, callback=self.parse_page)
通常来讲,使用xpath和css已经能够应付全部的html源码了,剩下的只是耐心加细心...要是有心的话也能够Item Loaders,方便后期的维护,下面摘自官方文档:
def parse(self, response): l = ItemLoader(item=Product(), response=response) l.add_xpath('name', '//div[@class="product_name"]') l.add_xpath('name', '//div[@class="product_title"]') l.add_xpath('price', '//p[@id="price"]') l.add_css('stock', 'p#stock]') l.add_value('last_updated', 'today') return l.load_item()
值得一提的是若是获取的是json格式的数据,可使用python自带的json库来解析成一个字典或列表:
data = json.loads(response.body)
可使用twisted提供的数据库库来维护一个链接池:
class CnblogPipeline(object): def __init__(self): self.dbpool = adbapi.ConnectionPool('MySQLdb', host='localhost', db='cnblog', user='root', passwd='root', cursorclass=MySQLdb.cursors.DictCursor, charset='utf8', use_unicode=True) def process_item(self, item, spider): self.dbpool.runInteraction(self.cnblog_insert, item) return item def cnblog_insert(self, cur, item): try: cur.execute('insert into ***') exception MySQLdb.Error, e: logging.info("cnblog_insert:%s" % str(e))
若是爬的是社交网站这种有着树型结构关系的网站的话,mongodb其符合人的思惟的存储方式让其成为首选。
若是使用mysql的话记得将innodb_flush_log_at_trx_commit这个参数置为0(每一秒读写一次数据和log),能够大大提升读写速度。
Request(url, meta={'how': 'ok'}, callback=self.parse_page) def parse_page(self, response): print response.meta['how']
class CnblogSpider(CrawlSpider): name = "cnblog_spider" allowed_domain = ["cnblog.com"] start_urls = ["http://www.cnblogs.com/rubinorth"] rules = [ Rule(LinkExtractor(allow=r"http://www.cnblogs.com/rubinorth/p/\d+\.html"), callback="parse_page", follow=True) ]
def parse_page(self, response): item = CnblogItem() **** yield item yield Request(new_url, callback=self.parse_page)
custom_settings={ 'ITEM_PIPELINES' : { 'cnblog_project.pipelines.CnblogPipeline': 300, } }
class MongoPipeline(object): def __init__(self, mongo_uri, mongo_db): self.mongo_uri = mongo_uri @classmethod def from_crawler(cls, crawler): return cls( mongo_uri=crawler.settings.get('MONGO_URI') )
DOWNLOADER_MIDDLEWARES = { 'cnblog_project.my_mv.middleware.UserAgentMiddleware': 543, 'cnblog_project.my_mv.middleware.ProxyMiddleware':544, }
def process_item(self, item, spider): if spider.name == 'a': **** if spider.name == 'b': ****
def process_item(self, item, spider): return item
其实这些在scrapy的官方文档都有说起,在此只是总结一些经常使用的知识点。若想更深刻地了解scrapy,定然是阅读其官方文档最好了。:)
scrapy_redis是一个分布式爬虫的解决方案。其思想为多个爬虫共用一个爬取队列,此队列使用redis存储,由于redis是一个内存数据库,因此速度上与单机的队列相差不大。
那么,scrapy_redis到底对scrapy作了什么修改,达到分布式的目的呢?
查看github上面的源码,能够发现其功能性代码集中在scheduler.py,dupefilter.py和queue.py中,分别是调度器(部分功能),去重,以及redis队列的实现。scrapy_redis就是将这些功能代替了scrapy本来的功能(并无修改scrapy源码,只须要在settings.py中进行设置便可),从而达到了分布式的效果。
转载请注明出处:http://www.cnblogs.com/rubinorth/