引言
- 网络爬虫是抓取互联网信息的利器,成熟的开源爬虫框架主要集中于两种语言Java和Python。主流的开源爬虫框架包括:
1.分布式爬虫框架:Nutch
2.Java单机爬虫框架:Crawler4j, WebMagic, WebCollector、Heritrix
3.python单机爬虫框架:scrapy、pyspider
- Nutch是专为搜索引擎设计的的分布式开源框架,上手难度高,开发复杂,基本无法满足快速开发的需要。
- Java单机类爬虫框架普遍容易上手,最大的优势是在Java技术的生态圈非常完善,轮子很多,同一个功能的轮子都有好几个不同的厂家提供,对于搭建完整的系统或者整合到现有的Java系统中都较为容易。
- 使用python开发的爬虫框架最大的特点是开发效率非常高,做相同的事情,代码量要远少于使用Java开发。缺点也比较明显,如果对python不熟悉的情况下,代码调试的时间会耗费掉编码时省下来的时间,甚至会浪费得更多,在爬取业务非常复杂的情况下,会花掉更多的调试时间。
- 总结下各个爬虫框架的特点:
语言 |
插件 |
上手难度 |
支持分布式 |
支持JS页面抓取 |
特点 |
Nutch |
Java |
支持,开发复杂 |
高 |
支持 |
不支持 |
专门为搜索引擎设计 |
Crawler4j |
Java |
不支持 |
低 |
不支持 |
不支持 |
简单 |
WebMagic |
Java |
支持 |
低 |
不支持 |
不支持 |
scrapy的Java版本 |
WebCollector |
Java |
支持 |
低 |
不支持 |
支持 |
内置selenium |
Heritrix |
Java |
支持 |
低 |
不支持 |
不支持 |
扩展性较好 |
scrapy |
Java |
支持 |
低 |
扩展可支持 |
不支持 |
扩展性强 |
pyspider |
Java |
支持 |
低 |
支持 |
支持 |
强大的爬虫管理和监控 |
1. URL管理,URL去重
2. 多线程抓取,线程池管理
3. 代理
4. 指定cookie
5. 集成网页解析器
6. 广度遍历或者深度遍历抓取
- 使用框架的最大好处是上述的问题都已经被解决,减少了开发的成本。缺点是开发模式受限于框架的设计思想,由于对框架的掌控程度有限,深度定制需要修改源码时会带来额外的成本。
python爬虫
- 使用python编写爬虫具有非常明显的优势,当满足以下需求时,强烈建议首选python作为开发语言。
1. 减少开发量,并快速的看到结果
2. 垂直领域的精确抓取,具有明确的抓取对象,需要格式化抽取数据
3. 指定ajax请求抓取,或有抓取有限的url请求数据
4. 不作为持续集成的系统,仅需要快速收集数据
basic版爬虫
- 在进行精确数据抓取时,使用框架并不一定具有优势。框架提供的主要功能:1.多线程抓取,2.URL管理和去重,3.增量抓取,4.监控。
1. 多线程抓取的作用是提高抓取效率,但是现在主流的搜索平台,在关键数据上都做了防抓取的工作,过快的抓取频率很容易导致当前IP的流量被视为异常流量,最坏的结果是IP被列入网站的黑名单,被限流或禁止访问一段时间。
2. 在进行精确数据抓取时,URL去重的作用并不大,甚至不需要。
3. 增量抓取是个很实用的功能,对于大规模的数据抓取很有必要,因为在数据抓取的过程中很可能出现异常,或者某些原因需要暂停抓取过程,导致某些数据没有成功获取,增量抓取机制能够很好的支持在特殊情况下的数据抓取延续性。
4. 监控,大型数据抓取系统必不可少的一个功能。
- 很多爬虫框架并不直接支持增量抓取和监控功能,作为一个小范围的快速的精确数据抓取的爬虫,增量抓取和监控并不是必须的。
- basic爬虫具有的基本功能:
1. 发起http请求
2. html解析
3. json解析
- 只要实现这三个功能,就能够写出一个从搜索平台上抓取数据的爬虫,在python的世界里,使用urllib2+BeautifulSoup+simplejson/json就能快速的实现一个爬虫。
- 发起一个http登录请求:
import urllib2
import json
header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36','Content-Type':'application/json'}
data = json.dumps({"userName":"wenbin", "password":"passwd"})
request = urllib2.Request(loginUrl, None, header)
response = urllib2.urlopen(request)
data = response.read()
response.close()
- 有时需要保存cookie,或者携带cookie发送请求,将代码改造一下:
import urllib2
import json
import cookielib
header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36','Content-Type':'application/json}
loginUrl = "${loginUrl}"
data = json.dumps({"userName":"wenbin", "password":"passwd"})
cookieFileName = 'cookie.txt'
# set cookie to request
cookie = cookielib.MozillaCookieJar(cookieFileName)
handler = urllib2.HTTPCookieProcessor(cookie)
opener = urllib2.build_opener(handler)
request = urllib2.Request(loginUrl, data, header)
response = opener.open(request)
# save the cookie
cookie.save(ignore_discard=True, ignore_expires=True)
response.close()
dataUrl = "${dataUrl}"
request = urllib2.Request(dataUrl, None, header)
data = response.read()
response.close()
cookie = cookielib.MozillaCookieJar()
# get cookie from file
cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
handler = urllib2.HTTPCookieProcessor(cookie)
opener = urllib2.build_opener(handler)
request = urllib2.Request(url, None, header)
response = opener.open(request)
data = response.read()
response.close()
data = "100
"
# load html content
soup = BeautifulSoup(data, "html.parser")
# find the first with class=total fl
totalCount = soup.find('h2', attrs={'class': 'total fl'}).span.contents[0]
# find all
with class=clear xiaoquListItem
xiaoquTag = soup.find_all("li", attrs={'class': 'clear xiaoquListItem'})
pageSize = len(xiaoquTag)
for tag in xiaoquTag:
# find all with class=title belong to tag
titleSet = tag.findAll("div", attrs={'class': 'title'})
for title in titleSet:
xiaoquUrl = title.a['href']
xiaoquId = str(xiaoquUrl).split('/')[4]
xiaoquName = title.a.contents[0]
xiaoquList.append([xiaoquId,xiaoquName,xiaoquUrl])
print xiaoquList
- 在web 2.0的时代,许多数据是通过ajax请求的方式异步获取的,通常返回的数据为json格式,在许多情况下需要进行json解析, python提供了非常简单易用的json解析工具:json/simplejson,json和simplejson几乎是一样的,但simplejson在python 2.4以上的版本可以用,json在python 2.6以上版本可用
import json
jstr = '{"demoName":"demo","version":1}'
jObject = json.loads(json_string)
print jObject['version']
object={"demoName":"demo","version":1,"cont":[1,2]}
print json.dumps(object)
- 使用urllib2+BeautifulSoup+simplejson/json编写的简易爬虫,能够实现对垂直类搜索平台的数据精确抓取和数据解析。
爬虫进阶
- basic版爬虫能够完成大部分的数据的抓取,但是仍然在一些情况下是无法抓取到目标数据的,比如当对方的数据是通过javascript对接口数据进行复杂的处理之后才输出的,那么直接抓取接口就无法获得正确的数据,而basic版的爬虫是无法运行javascript脚本的。
- 为了能够正确运行javascript脚本,获取异步加载的、经过复杂处理之后才输出的数据,需要升级basic爬虫。在python体系下,推荐使用Selenium+PhantomJS+BeautifulSoup的组合实现新的爬虫。PhantomJS用来解析javascript和页面渲染,Selenium用来驱动PhantomJS,BeautifulSoup用来解析渲染结束后的html页面。
- 在mac os下,使用PhantomJS发起请求,并使用BeautifulSoup解析渲染完成的html:
from selenium import webdriver
import os
from bs4 import BeautifulSoup
driverDir = "${dirvierDir}"
os.environ["webdriver.phantomjs.driver"] = driverDir
driver = webdriver.PhantomJS(driverDir)
url = '${url}'
driver.get(url)
data = self.driver.page_source
print data
soup = BeautifulSoup(data, "html.parser")
driver.close()
- 其中,chromedriverDir为chrome浏览器的内核所在的文件目录。
- selenium的webdriver还支持chrome、firefox和ie等浏览器,使用PhantomJS的原因是PhantomJS没有UI,因此运行速度更快,消耗的资源更少。进阶的爬虫具有更好的反爬虫能力,能够适应更多的场景。
高级爬虫
- 一个高级的爬虫,并不仅仅在于实现的技术、设计的理念有多先进,更在于其对被抓取的网站的服务器有多友好。无视服务器压力的爬虫一般会定义为低级的爬虫,把服务器拖垮,即影响了服务提供方的正常运营,也直接导致所需的数据无法获取。当然,肆无忌惮的爬虫也很容易被反爬虫程序发现,然后被封杀,或被‘投毒’。高级的爬虫需要具备以下几个特点:
1. 控制对目标网站的抓取速度,如果仅仅是需要收集数据,而不要求数据的非常高的实时性,建议将访问频率控制到正常浏览的水平。
2. 具有生命周期的管控,数据收集完成后,需要停止爬虫的工作。
3. 健壮性,需要保证爬虫程序的健壮性,能够容忍坏的HTML、奇怪的服务器行为,不会因此而程序中断。
4. 可扩展性,通用功能组件化,便于复用,也便于修改。
5. 适当的监控,留下关键的抓取记录,便于异常恢复和抓取进度监控。
6. 具有一定的‘反爬虫’抗性
- 只要爬虫存在,‘反爬虫’就不会消失,做一个有礼貌的爬虫,将会极大的降低被‘反’的风险。
小结&DEMO
- 使用python能够非常快速、便捷的编写数据抓取的爬虫程序,能够满足大部分情况下的数据收集需求,带来无与伦比的快速反馈体验
- 做一个有礼貌的爬虫,为了世界和平,请控制抓取速度
- 真正的爬虫和反爬虫之战是RMB玩家的游戏,是技术和金钱的较量。
- demo的github地址:https://github.com/huntertan/lstm-spider