2015年6月12日 星期五

[Python][教學] 網路爬蟲(crawler)進階實務 -- 頁中有頁(二)

enter image description here
上一回[Python][教學] 網路爬蟲(crawler)進階實務– 自動爬蟲(一)介紹了怎麼繼承已經寫好的CrawlSpider物件來達到根據規則自動爬網頁的效果,但是如果你要爬的分頁沒有特定的規則,或是只想抓取頁面中的特定連結又該如何處理呢?
例如我今天要抓這個網站的掛號資料可以透過網址比對找出類似的網址之後,再一層一層的解析這個表格:



    def parse_table(self, response):

        items = []
        sel = Selector(response)
        ##一個頁面有四個table
        dept = sel.xpath('//span[@id="labTitle"]/b/font/text()').extract()
        dept2 = sel.xpath('//span[@id="labTitle2"]/b/font/text()').extract()

        if (len(dept) == 0):
            dept = dept2[0]
        else:
            dept = dept[0]

        #print dept
        tables = sel.xpath('//tr/td/table')
        #table1 = sel.xpath('//table[@id="Table1"]/tr')
        #print table1.extract()
        #print 'len of table = '+ str(len(tables))

        for t in range(len(tables)):
            if (t<2):
            ##每個table看有幾個row
                table = tables[t].xpath('.//tr')
                #print "t = " + str(t)

                for n in range(len(table)-1):
                    #print "n = " + str(n)

                    ##每個row看有幾行            
                    tds = table[n+1].xpath('.//td')
                    #print "tds = " + str(len(tds))

                    for day in range(len(tds)-2):
                        #print "day = " + str(day)
                        item = NtuhItem()
                        try:
                            name = table[n+1].xpath('.//td')[day+2].xpath('.//font/text()').extract()
                            if (name == []):
                                continue

                            item['outpatient'] = table[n+1].xpath('.//td')[0].xpath('.//b/text()')[0].extract()
                            item['name'] = name[0]
                            item['hospital'] = 'ntuh'
                            item['crawlTime'] = unicode(datetime.now().strftime("%Y%m%d %H:%M"))

                            ##區分成人及兒童
                            if (t < 2):
                                item['dept'] = dept
                            else:
                                item['dept'] = 'Chld_PSY'


                            date = table[0].xpath('.//b/text()')[day+2].extract().split(" ")[0].split(".")
                            date = str(int(date[0])+1911) + date[1] + date[2]
                            item['date'] = date

                            ##區分時段
                            if (t % 2 == 0):
                                item['time'] = 'morning'
                            else:
                                item['time'] = 'afternoon'


接下來比較麻煩的地方在於若要點進每個醫生的連結中,抓取特定資料時,就要用以下的方式:


##抓取連結位置以及link
item['link'] = "https://reg.ntuh.gov.tw/webadministration/" + \
                                table[n+1].xpath('.//td')[day+2].xpath('.//a/@href').extract()[0]

                            yield Request(item['link'], callback = self.parse_shift, meta = {'item': item})                
                            items.append(item)


接著透過callbak來呼叫另外一個parsing的function,這邊要注意的是在Request涵式中,需要使用meta參數將item的格式帶過去,表示callback的parsing的item定義的內容與當前用的item定義相同:


def parse_shift(self, response):

        ##一開始要先呼要剛剛傳過來的item
        item = response.meta['item']
        # 先判斷有幾個欄位,有些醫生兩個禮拜都有診有些醫生只有一個禮拜
        rows = len(Selector(response).xpath('//table[@id= "DataTable"]/tr'))

        # 如果診數>1 欄位讀取依週數判斷
        if (rows > 2):
            week = int(response.request.headers.get('Referer', None)[-1])
        else:
            week = 1

        isFull = Selector(response).xpath('//table[@id= "DataTable"]/tr')[week].\
            xpath('.//font/text()').extract()[0].strip()

        if (isFull == u'名額已滿'):
            item['full'] = u'名額已滿'
        else:
            item['full'] = u'可掛號'

        return item


這樣拆開來講好像不是很清楚,有興趣的可以到我的github看完整的程式碼,另外呼籲各位不要透過爬蟲攻擊網站(爬蟲就是丟request給醫院,間隔沒設好跟DDOS沒兩樣).


沒有留言:

張貼留言