本篇將談一些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/