網頁爬蟲--scrapy進階

本篇將談一些scrapy的進階內容,幫助你們能更熟悉這個框架。css


1. 站點選取

如今的大網站基本除了pc端都會有移動端,因此須要先肯定爬哪一個。html

好比爬新浪微博,有如下幾個選擇:python

  1. www.weibo.com,主站
  2. www.weibo.cn,簡化版
  3. m.weibo.cn,移動版

上面三個中,主站的微博數據是動態加載的,意味着光看源碼是看不到數據的,想爬的話要麼搞清楚其api訪問狀況,要麼模擬js,那樣的話花的力氣可能就有點多了。weibo.cn是一個簡化版,數據能直接從網頁源代碼中解析出來,但使用正則或xpath取網頁中的元素仍然是無聊且費時的,更不用說偶爾的頁面結構錯誤更讓人抓狂。 相比之下,移動版的爬蟲最好寫,由於移動版能直接拿到json格式的數據。mysql

通常來講,有移動版的網站優先爬移動版,會節省不少力氣。git


2. 模擬登陸

如今須要登陸才能正常瀏覽的網站的愈來愈多了,對爬蟲也愈來愈不友好...因此模擬登陸在很多場合都是必不可少的。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)

3. 網頁解析

通常來講,使用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)

4. 數據存儲

可使用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),能夠大大提升讀寫速度。


5. scrapy小知識點

  • Request傳遞消息。在Request中加入meta,便可將meta傳遞給response。
Request(url, meta={'how': 'ok'}, callback=self.parse_page)

def parse_page(self, response):
    print response.meta['how']
  • CrawlSpider。都知道在寫本身的spider的時候須要繼承scrapy的spider,除了scrapy.Spider外,scrapy還提供了好幾種spider,其中CrawlSpider算是比較經常使用的。CrawlSpider的優點在於能夠用rules方便地規定新的url的樣子,即經過正則匹配來約束url。而且不須要本身生成新的url,CrawlSpider會本身尋找源碼中全部符合要求的新url的。另外,rules的回調方法名字最好不要叫parse。
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)
    ]
  • parse中既返回item又生成新的request。平時在parse中return item便可返回item,return request則生成新的request請求。若是咱們將return換爲yield的話便可既返回item又生成新的request。注意一旦使用了yield,那麼parse方法中就不能有return了。
def parse_page(self, response):
    item = CnblogItem()
    ****
    yield item
    yield Request(new_url, callback=self.parse_page)
  • 每一個spider不一樣設置。在spider中加入custom_settings便可覆蓋settings.py中相應的設置,這樣的話在settings.py中只須要放一些公用的設置就好了。最經常使用的就是設置每一個spider的pipeline。
custom_settings={
    'ITEM_PIPELINES' : {
        'cnblog_project.pipelines.CnblogPipeline': 300,
    }
}
  • 讀取本身的設置。無論在spider中仍是pipeline中,均可以寫from_crawler這個方法(注意spider中參數數目不一樣)。此方法在初始化階段由scrapy本身調用,其最大的做用就是從settings.py讀取本身的設置了。下面的代碼從settings.py中讀取了MONGO_URI。
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')
        )
  • 順序。settings.py中middleware和pipeline設置的時候須要在後面跟上一個數字,而這個數字的含義就是調用的順序,數字越小越早調用。
DOWNLOADER_MIDDLEWARES = {
    'cnblog_project.my_mv.middleware.UserAgentMiddleware': 543,
    'cnblog_project.my_mv.middleware.ProxyMiddleware':544,
}
  • pipeline中spider.name的應用。pipeline中的process_item中能夠根據spider.name來對不一樣的item進行不一樣的處理。
def process_item(self, item, spider):
    if spider.name == 'a':
        ****
    if spider.name == 'b':
        ****
  • 多個pipeline處理一個item。pipeline中的process_item方法必須返回一個item或者raise一個DropItem的異常,若是返回item的話這個item將會被以後的pipeline接收到。
def process_item(self, item, spider):
    return item
  • 以後可能會有添加。

其實這些在scrapy的官方文檔都有說起,在此只是總結一些經常使用的知識點。若想更深刻地瞭解scrapy,定然是閱讀其官方文檔最好了。:)


6. scrapy_redis簡介

scrapy_redis是一個分佈式爬蟲的解決方案。其思想爲多個爬蟲共用一個爬取隊列,此隊列使用redis存儲,由於redis是一個內存數據庫,因此速度上與單機的隊列相差不大。

那麼,scrapy_redis到底對scrapy作了什麼修改,達到分佈式的目的呢?

查看github上面的源碼,能夠發現其功能性代碼集中在scheduler.py,dupefilter.py和queue.py中,分別是調度器(部分功能),去重,以及redis隊列的實現。scrapy_redis就是將這些功能代替了scrapy本來的功能(並無修改scrapy源碼,只須要在settings.py中進行設置便可),從而達到了分佈式的效果。


參考資料

scrapy官中文檔

轉載請註明出處:http://www.cnblogs.com/rubinorth/

相關文章
相關標籤/搜索