Scrapy定向爬虫教程(三)——爬取多个页面
本節(jié)內(nèi)容
本部分所實現(xiàn)的功能是,批量的爬取網(wǎng)頁信息,不再是像以前那樣只能下載一個頁面了。也就是說,分析出網(wǎng)頁的url規(guī)律后,用特定的算法去迭代,達到把整個網(wǎng)站的有效信息都拿下的目的。
因為本部分講完后,功能已經(jīng)到了可以使用的地步,所以我把本部分的結(jié)果獨立出來,把項目上傳到了github,小伙伴可以下載參考,地址https://github.com/kongtianyi/heartsong。教程余下的其他部分是添加功能和優(yōu)化,今后我會在github上創(chuàng)建擁有不同擴展功能的分支。
分析url
不管是Discuz模板,phpWind模板,還是百度貼吧,甚至某些新聞網(wǎng),都是采用id的方式來組織網(wǎng)頁url的。這就給我們編寫定向爬蟲帶來了極大的便利。好,來看一下Discuz模板心韻論壇的url:
http://www.heartsong.top/forum.php?mod=viewthread&tid=13&extra=page%3D1 http://www.heartsong.top/forum.php?mod=viewthread&tid=31 http://www.heartsong.top/forum.php?mod=viewthread&tid=31&extra=&page=2共同點一目了然,其實我們不妨把參數(shù)改一改,空的參數(shù)去掉,下面三個url跟上面的三個請求到的頁面是一樣的
http://www.heartsong.top/forum.php?mod=viewthread&tid=13 http://www.heartsong.top/forum.php?mod=viewthread&tid=31 http://www.heartsong.top/forum.php?mod=viewthread&tid=31&page=2局勢更清晰了,所謂的tid,就是帖子的id,而參數(shù)page,就是若主題帖分頁的話,主題帖的某一頁,當然第一頁也可以加上page參數(shù),http://www.heartsong.top/forum.PHP?mod=viewthread&tid=13&page=1,一樣可以請求到網(wǎng)頁。
大部分的網(wǎng)站首頁上都會有“最新帖子”,“最新新聞”這種模塊,點進去就能找到tid的上限,若是沒有的話,那就乖乖多次嘗試吧,下限一般都是從零開始,不必多說。而page參數(shù),需要我們在主題帖的第一頁通過網(wǎng)頁元素的分析去尋找出來。
根據(jù)我的經(jīng)驗,在很多論壇里,包括我的這個小破論壇,都或多或少的遭到廣告的侵襲,會有很多tid對應的帖子被管理員刪掉,所以下面的代碼里我們要對這種帖子做相應的處理。一般來說,Discuz被刪帖的tid或者是還沒排到的tid會返回如下頁面
爬取思路
通過以上的分析,我們可以得出這樣的思路:
1 通過某種機制去迭代tid
2 在主題帖第一頁中分析出總頁數(shù),去迭代帶page參數(shù)的url
一些雜項
對于某些網(wǎng)站,他們有識別爬蟲的機制,所以我們需要對我們的爬蟲進行一定的偽裝,在heartsong_spider.py中加入以下項。
其中,cookies在后面教程的回帖部分會用到,此處可以先置空。
- 1
重載start_requests
在找到tid的上限后,要想帶著上面配置的雜項去發(fā)起Request請求,我們需要重載一個函數(shù),配置使用star_urls所發(fā)起的第一條請求。
這里對heartsong_spider.py中的yield做一下解釋,它既可以傳出一個item到pipeline進行加工,也可以傳出一個新的Request請求。在傳出一個新請求的時候,就會多開啟一個線程,Scrapy是異步多線程的爬蟲框架,不需要我們對多線程有過多的了解。
- 1
編寫迭代tid的函數(shù)
找到了tid的上限之后,我們的策略是從上限向0迭代,當然,要生成新的url,只需要對老的url串進行簡單的處理就OK了
def get_next_url(self, oldUrl):'''description: 返回下次迭代的url:param oldUrl: 上一個爬去過的url:return: 下次要爬取的url'''# 傳入的url格式:http://www.heartsong.top/forum.php?mod=viewthread&tid=34l = oldUrl.split('=') #用等號分割字符串oldID = int(l[2])newID = oldID - 1if newID == 0: # 如果tid迭代到0了,說明網(wǎng)站爬完,爬蟲可以結(jié)束了returnnewUrl = l[0] + "=" + l[1] + "=" + str(newID) #構(gòu)造出新的urlreturn str(newUrl) # 返回新的url- 12
迭代request請求
有了找到下一個url的函數(shù)之后,我們就可以在適當?shù)奈恢锰砑尤缦麓a,發(fā)起新的請求,“適當?shù)奈恢谩卑ㄒ韵聝煞N情況:
* 本頁的數(shù)據(jù)獲取完成
* 本頁被刪除,無內(nèi)容
- 1
分析總頁數(shù)
打開一個有分頁的主題帖,和一個沒有分頁的主題貼,找不同
先判斷頁面內(nèi)有沒有分頁的框,通過之前介紹的檢查網(wǎng)頁元素的辦法找到總頁數(shù),通過XPath定位,然后通過一個簡單的正則把總頁數(shù)拿出來。
- 1
迭代帶page參數(shù)的url
分析出了總頁數(shù)之后,無非就是拼接出子頁的url,然后發(fā)起Request請求,不過要注意,回調(diào)函數(shù)不能再是parse了,因為那樣的話會在這里無限的生成Request。所以我們需要自己定義一個函數(shù)sub_parse,去處理子頁的response。
# response.url格式: http://www.heartsong.top/forum.php?mod=viewthread&tid=34 # 子utl格式: http://www.heartsong.top/forum.php?mod=viewthread&tid=34&page=1 tmp = response.url.split('=') # 以=分割url # 循環(huán)生成所有子頁面的請求 for page_num in xrange(2, int(pages) + 1): # 構(gòu)造新的urlsub_url = tmp[0] + '=' + tmp[1] + '=' + tmp[2] + 'page=' + str(page_num)# 注意此處的回調(diào)函數(shù)是self.sub_parse,就是說這個請求的response會傳到# self.sub_parse里去處理yield Request(sub_url,callback=self.sub_parse, headers=self.headers,cookies=self.cookies, meta=self.meta)sub_parse:
def sub_parse(self, response):"""用以爬取主題貼除首頁外的其他子頁:param response::return:"""selector = Selector(response)table = selector.xpath('//*[starts-with(@id, "pid")]') # 取出所有的樓層for each in table:item = HeartsongItem() # 實例化一個item# 通過XPath匹配信息,注意extract()方法返回的是一個listitem['author'] = each.xpath('tr[1]/td[@class="pls"]/div[@class="pls favatar"]/div[@class="pi"]/div[@class="authi"]/a/text()').extract()[0]item['post_time'] = each.xpath('tr[1]/td[@class="plc"]/div[@class="pi"]').re(r'[0-9]+-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+')[0]content_list = each.xpath('.//td[@class="t_f"]').xpath('string(.)').extract()content = "".join(content_list) # 將list轉(zhuǎn)化為stringitem['url'] = response.url # 用這種方式獲取網(wǎng)頁的url# 把內(nèi)容中的換行符,空格等去掉item['content'] = content.replace('\r\n', '').replace(' ', '').replace('\n', '')yield item # 將創(chuàng)建并賦值好的Item對象傳遞到PipeLine當中進行處理完整代碼
settings.py,pipelines.py,item.py相較于第二節(jié)都沒有改動。
heart_song.py:
- 124
運行
同教程二。區(qū)別在于爬的數(shù)據(jù)是多個帖子的數(shù)據(jù)。
小結(jié)
至此,一個較為完整的定向爬蟲已經(jīng)寫完了,項目地址https://github.com/kongtianyi/heartsong。接下來的教程中,我會介紹如何拓展功能。比如某些帖子內(nèi)容需要回復可見,我們需要爬蟲自動回復。再比如有些網(wǎng)站會檢測出你是爬蟲然后封你的IP,這時候就需要啟用代理。等等……
總結(jié)
以上是生活随笔為你收集整理的Scrapy定向爬虫教程(三)——爬取多个页面的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Scrapy保存到txt文件或者数据库里
- 下一篇: ElasticSearch5.3的 he