1. 程式人生 > >python爬蟲學習筆記-scrapy框架之start_url

python爬蟲學習筆記-scrapy框架之start_url

在使用命令列建立scrapy專案後,會發現在spider.py檔案內會生成這樣的程式碼:

name = 'quotes'
allowed_domains = ['quotes.toscrape.com']
start_urls = ['http://quotes.toscrape.com/']

其中比較好理解的是name,這個欄位代表爬蟲專案名稱,在命令列建立時已經指定,allowed_domains代表允許爬取的域名,這兩個欄位均在建立時已經設定好,不需要更改,但比較特殊的是start_url這個欄位,它也是一個列表形式,官方檔案的解釋為包含spider在啟動時爬取的url列表,用於定義初始請求。這樣就可以類比如果不使用scrapy框架,url應該如何定義;
例如爬取豆瓣讀書時發現首頁url為"

https://book.douban.com/top250?start=0",
以後每翻頁一次start欄位值遞增25,這樣就可以很容易用一個迴圈做到url的構建,再比如如果爬取今日頭條圖片,分析Ajax後首頁url為“https://www.toutiao.com/search_content/?offset=0&format=json&keyword=街拍&autoload=true&count=20&cur_tab=1&from=search_tab&pd=synthesis”,以後每頁更改其中offset引數遞增20,則可以利用urlencode()方法構建url。

其實scrapy的start_url構建方法與普通爬蟲的構建本質上沒有任何區別,只是因為在框架的排程使其實現更加容易。
下面以http://images.so.com網站為例:
建立好專案檔案後,spider.py內的程式碼如下:

class ImagesSpider(Spider):
    name = 'images'
    allowed_domains = ['images.so.com']
    start_urls = ['http://images.so.com/']

     def prase(self):
     	pass

如果我們不做任何其他處理,則爬蟲啟動只會爬取"

http://images.so.com/"上的內容。
因為這是一個列表形式,笨一點的方法就是把所有要爬取的url新增到列表中,然後遍歷就可以了:

start_urls = [
           'http://images.so.com/z?ch=beauty',
           'http://images.so.com/z?ch=wallpaper']

       for url in start_urls:
           yield scrapy.Request(url=url, callback=self.parse)

這樣當然沒錯,而且還很好理解,但未免顯得太實在了。既然要返回一組列表,自然而然就可以用到以前的構建方法,構建程式碼如下:

    def start_requests(self):
        data={'ch':'photography','listtype':'new'}
        base_url='https://image.so.com/zj?'
        for page in range(1,self.settings.get('MAX_PAGE')+1):
            data['sn']=page*30
            params=urlencode(data)
            url=base_url+params
            yield Request(url=url,callback=self.parse)

這裡的url是分析Ajax得到的,可以看出構建方法沒有什麼特別之處,這個方法返回的Request請求,而回調函式就是我們解析html的方法,爬蟲執行時,不斷的將請求生成並被parse()方法呼叫解析,parse()方法程式碼如下:

    def parse(self, response):
        result=json.load(response.text)
        for image in result.get('list'):
            item=Images360Item()
            item['id']=image.get('imageid')
            item['url']=image.get('qhimg_url')
            item['title']=image.get('group_title')
            item['thumb']=image.get('qhimg_thumb_url')
            yield item

當涉及到不止一層關係的爬蟲,例如微博的爬取,爬取了微博首頁後,還要爬取它的關注,粉絲,微博列表的情況下,就相對複雜的多了。但其實本質是一樣的,只是下一層的start_url的構建不是在start_request()方法中,而是在第一層羅輯的解析方法中生成,虛擬碼如下,以微博為例:

class WeibocSpider(Spider):
    name = 'weiboc'
    allowed_domains = ['m.weibo.cn']
    user_url=''
    follow_url=''
    fan_url=''
    weibo_url=''
    start_user=['3217179555']
    def start_requests(self):
        for uid in self.start_user:
            yield Request(self.user_url.format(id=uid),callback=self.parse_user)

    def parse_user(self,response):
        self.logger.debug(response)
        result=json.loads(response.text)
        if result.get('data').get('userInfo'):
            user_Info=result.get('data').get('userInfo')
            user_item=UserItem()
            field_map={}
            for field ,attr in field_map.items():
                user_item[field]=user_Info.get['attr']#返回使用者資訊


            #關注
            uid=user_Info.get('id')
            #構造下一頁連結,並返回Request
            yield Request(self.follow_url.format(uid=uid,page=1),callback=self.parse_follows,meta={'page':1,'uid':uid})

            #fans
            yield Request(self.fan_url.format(uid=uid,page=1),callback=self.parse_fans,meta={'page':1,'uid':uid})

            #weibo
            yeild Request(self.weibo_url.format(uid=uid,page=1),callback=self.parse_weibos,meta={'page':1,'uid':uid})





    def parse_follows(self,response):
        """
        解析使用者關注
        :param response: Response物件
        :return:
        """
        result=json.loads(response.text)
        if result.get('ok') and result.get('data').get('cards') and len(result.get('data').get('cards'))
            and result.get('data').get('cards')[-1].get('card_group'):
            #解析關注列表每個使用者資訊併發起新的解析請求
            follows=result.get('data').get('cards')[-1].get('card_group')
            for folow in follows:
                if follow.get('user'):
                    uid=follow.get('user').get('id')
                    yield Request(self.user_url.format(uid=uid)callback=self.parse_user())

            #關注列表
            uid=response.meta.get('uid')
            user_relation_item=UserRelationItem()
            follows=[{'id':follow.get('user').get('id'),'name':follow.get('user').get('screen_name')}for follow in follows]
            user_relation_item['id']=uid
            user_relation_item['follows']=follows
            user_relation_item['fans']=[]
            yield  user_relation_item

            #提取下一頁的關注
            page=response.meta.get('page')+1
            yield  Request(self.follow_url.format(uid=uid,page=page),callback=self.parse_follows,meta={'page':page.'uid':uid})

這裡首先通過start_request()方法生成要爬取的微博首頁請求,在parse_users()方法解析得到使用者資訊後,再生成下一層羅輯需要的請求列表,這樣就可以實現一層層不斷的爬取,貌似用到了深度優先的概念。
ps:GitHub地址:https://github.com/linl12138/image