以上是百度百科和維基百科對網絡爬蟲的定義,簡單來說爬蟲就是抓取目标網站内容的工具,一般是根據定義的行為自動進行抓取,更智能的爬蟲會自動分析目标網站結構類似與搜尋引擎的爬蟲,我們這裡隻讨論基本的爬蟲原理。
###爬蟲工作原理
網絡爬蟲架構主要由控制器、解析器和索引庫三大部分組成,而爬蟲工作原理主要是解析器這個環節,解析器的主要工作是下載下傳網頁,進行頁面的處理,主要是将一些JS腳本标簽、CSS代碼内容、空格字元、HTML标簽等内容處理掉,爬蟲的基本工作是由解析器完成。是以解析器的具體流程是:
入口通路->下載下傳内容->分析結構->提取内容
這裡我們通過分析一個網站[落網:http://luoo.net] 對網站内容進行提取來進一步了解!
第一步 确定目的
抓取目标網站的某一期所有音樂
第二步 分析頁面結構
通路落網的某一期刊,通過Chrome的開發者模式檢視播放清單中的歌曲,右側用紅色框線圈出來的是一些需要特别注意的語義結構,見下圖所示:
以上紅色框線圈出的地方主要有歌曲名稱,歌曲的編号等,這裡并沒有看到歌曲的實際檔案位址,是以我們繼續檢視,點選某一個歌曲就會立即在浏覽器中播放,這時我們可以看到在Chrome的開發者模式的Network中看到實際請求的播放檔案,如下圖所示:
根據以上分析我們可以得到播放清單的位置和音樂檔案的路徑,接下來我們通過Python來實作這個目的。
Python環境安裝請自行Google
主要依賴第三方庫
主要思路是分成兩部分,第一部分用來發起請求分析出播放清單然後丢到隊列中,第二部分在隊列中逐條下載下傳檔案到本地,一般分析清單速度更快,下載下傳速度比較慢可以借助多線程同時進行下載下傳。
主要代碼如下:
Python
#-*- 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()
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):
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'
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()
以上代碼執行後結果如下圖所示
本文轉自 技術花妞妞 51CTO部落格,原文連結:http://blog.51cto.com/xiaogongju/1972647