这是一个网络爬虫学习的技术分享,主要通过一些实际的案例对爬虫的原理进行分析,达到对爬虫有个基本的认识,并且能够根据自己的需要爬到想要的数据。有了数据后可以做数据分析或者通过其他方式重新结构化展示。
网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。via 百度百科网络爬虫
网络蜘蛛(Web spider)也叫网络爬虫(Web crawler),蚂蚁(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)
主要思路是分成两部分,第一部分用来发起请求分析出播放列表然后丢到队列中,第二部分在队列中逐条下载文件到本地,一般分析列表速度更快,下载速度比较慢可以借助多线程同时进行下载。
主要代码如下:
#-*- 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这类爬虫框架来实现一个新的爬虫来加深对网络爬虫的理解!
特别申明:本文所提到的落网是我本人特别喜欢的一个音乐网站,本文只是拿来进行爬虫的技术交流学习,读者涉及到的所有侵权问题都与本人无关
本文首发在sudo rm -rf 采用署名(BY)-非商业性使用(NC)-禁止演绎(ND) 转载请注明原作者
–EOF–