概述
這是一個網路爬蟲學習的技術分享,主要通過一些實際的案例對爬蟲的原理進行分析,達到對爬蟲有個基本的認識,並且能夠根據自己的需要爬到想要的資料。有了資料後可以做資料分析或者通過其他方式重新結構化展示。
什麼是網路爬蟲
網路爬蟲(又被稱為網頁蜘蛛,網路機器人,在FOAF社群中間,更經常的稱為網頁追逐者),是一種按照一定的規則,自動地抓取全球資訊網資訊的程式或者指令碼。另外一些不常使用的名字還有螞蟻、自動索引、模擬程式或者蠕蟲。via 百度百科網路爬蟲
網路蜘蛛(Web spider)也叫網路爬蟲(Web crawler)[1],螞蟻(ant),自動檢索工具(automatic indexer),或者(在FOAF軟體概念中)網路疾走(WEB scutter),是一種“自動化瀏覽網路”的程式,或者說是一種網路機器人。它們被廣泛用於網際網路搜尋引擎或其他類似網站,以獲取或更新這些網站的內容和檢索方式。它們可以自動採集所有其能夠訪問到的頁面內容,以供搜尋引擎做進一步處理(分檢整理下載的頁面),而使得使用者能更快的檢索到他們需要的資訊。via 維基百科網路蜘蛛
以上是百度百科和維基百科對網路爬蟲的定義,簡單來說爬蟲就是抓取目標網站內容的工具,一般是根據定義的行為自動進行抓取,更智慧的爬蟲會自動分析目標網站結構類似與搜尋引擎的爬蟲,我們這裡只討論基本的爬蟲原理。
###爬蟲工作原理
網路爬蟲框架主要由控制器、解析器和索引庫三大部分組成,而爬蟲工作原理主要是解析器這個環節,解析器的主要工作是下載網頁,進行頁面的處理,主要是將一些JS指令碼標籤、CSS程式碼內容、空格字元、HTML標籤等內容處理掉,爬蟲的基本工作是由解析器完成。所以解析器的具體流程是:
入口訪問->下載內容->分析結構->提取內容
分析爬蟲目標結構
這裡我們通過分析一個網站[落網:http://luoo.net] 對網站內容進行提取來進一步瞭解!
第一步 確定目的
抓取目標網站的某一期所有音樂
第二步 分析頁面結構
訪問落網的某一期刊,通過Chrome的開發者模式檢視播放列表中的歌曲,右側用紅色框線圈出來的是一些需要特別注意的語義結構,見下圖所示:
以上紅色框線圈出的地方主要有歌曲名稱,歌曲的編號等,這裡並沒有看到歌曲的實際檔案地址,所以我們繼續檢視,點選某一個歌曲就會立即在瀏覽器中播放,這時我們可以看到在Chrome的開發者模式的Network中看到實際請求的播放檔案,如下圖所示:
根據以上分析我們可以得到播放清單的位置和音樂檔案的路徑,接下來我們通過Python來實現這個目的。
實現爬蟲
Python環境安裝請自行Google
主要依賴第三方庫
Requests(http://www.python-requests.org) 用來發起請求
BeautifulSoup(bs4) 用來解析HTML結構並提取內容
faker(http://fake-factory.readthedocs.io/en/stable/)用來模擬請求UA(User-Agent)
主要思路是分成兩部分,第一部分用來發起請求分析出播放列表然後丟到佇列中,第二部分在佇列中逐條下載檔案到本地,一般分析列表速度更快,下載速度比較慢可以藉助多執行緒同時進行下載。
主要程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
#-*- coding: utf-8 -*- '''by sudo rm -rf http://imchenkun.com''' import os import requests from bs4 import BeautifulSoup import random from faker import Factory import Queue import threading fake = Factory.create() luoo_site = 'http://www.luoo.net/music/' luoo_site_mp3 = 'http://luoo-mp3.kssws.ks-cdn.com/low/luoo/radio%s/%s.mp3' proxy_ips = [ '27.15.236.236' ] # 替換自己的代理IP headers = { 'Connection': 'keep-alive', 'User-Agent': fake.user_agent() } def random_proxies(): ip_index = random.randint(0, len(proxy_ips)-1) res = { 'http': proxy_ips[ip_index] } return res def fix_characters(s): for c in ['<', '>', ':', '"', '/', '\\\\', '|', '?', '*']: s = s.replace(c, '') return s class LuooSpider(threading.Thread): def __init__(self, url, vols, queue=None): threading.Thread.__init__(self) print '[luoo spider]' print '=' * 20 self.url = url self.queue = queue self.vol = '1' self.vols = vols def run(self): for vol in self.vols: self.spider(vol) print '\\ncrawl end\\n\\n' def spider(self, vol): url = luoo_site + vol print 'crawling: ' + url + '\\n' res = requests.get(url, proxies=random_proxies()) soup = BeautifulSoup(res.content, 'html.parser') title = soup.find('span', attrs={'class': 'vol-title'}).text cover = soup.find('img', attrs={'class': 'vol-cover'})['src'] desc = soup.find('div', attrs={'class': 'vol-desc'}) track_names = soup.find_all('a', attrs={'class': 'trackname'}) track_count = len(track_names) tracks = [] for track in track_names: _id = str(int(track.text[:2])) if (int(vol) < 12) else track.text[:2] # 12期前的音樂編號1~9是1位(如:1~9),之後的都是2位 1~9會在左邊墊0(如:01~09) _name = fix_characters(track.text[4:]) tracks.append({'id': _id, 'name': _name}) phases = { 'phase': vol, # 期刊編號 'title': title, # 期刊標題 'cover': cover, # 期刊封面 'desc': desc, # 期刊描述 'track_count': track_count, # 節目數 'tracks': tracks # 節目清單(節目編號,節目名稱) } self.queue.put(phases) class LuooDownloader(threading.Thread): def __init__(self, url, dist, queue=None): threading.Thread.__init__(self) self.url = url self.queue = queue self.dist = dist self.__counter = 0 def run(self): while True: if self.queue.qsize() <= 0: pass else: phases = self.queue.get() self.download(phases) def download(self, phases): for track in phases['tracks']: file_url = self.url % (phases['phase'], track['id']) local_file_dict = '%s/%s' % (self.dist, phases['phase']) if not os.path.exists(local_file_dict): os.makedirs(local_file_dict) local_file = '%s/%s.%s.mp3' % (local_file_dict, track['id'], track['name']) if not os.path.isfile(local_file): print 'downloading: ' + track['name'] res = requests.get(file_url, proxies=random_proxies(), headers=headers) with open(local_file, 'wb') as f: f.write(res.content) f.close() print 'done.\\n' else: print 'break: ' + track['name'] if __name__ == '__main__': spider_queue = Queue.Queue() luoo = LuooSpider(luoo_site, vols=['680', '721', '725', '720'],queue=spider_queue) luoo.setDaemon(True) luoo.start() downloader_count = 5 for i in range(downloader_count): luoo_download = LuooDownloader(luoo_site_mp3, 'D:/luoo', queue=spider_queue) luoo_download.setDaemon(True) luoo_download.start() |
以上程式碼執行後結果如下圖所示
Github地址:https://github.com/imchenkun/ick-spider/blob/master/luoospider.py
總結
通過本文我們基本瞭解了網路爬蟲的知識,對網路爬蟲工作原理認識的同時我們實現了一個真實的案例場景,這裡主要是使用一些基礎的第三方Python庫來幫助我們實現爬蟲,基本上演示了網路爬蟲框架中基本的核心概念。通常工作中我們會使用一些比較優秀的爬蟲框架來快速的實現需求,比如 scrapy框架,接下來我會通過使用Scrapy這類爬蟲框架來實現一個新的爬蟲來加深對網路爬蟲的理解!
特別申明:本文所提到的落網是我本人特別喜歡的一個音樂網站,本文只是拿來進行爬蟲的技術交流學習,讀者涉及到的所有侵權問題都與本人無關