Ps:惯例ps环节,经过前面几章的学习,写一些简单的爬虫已经是手到擒来了吧。这章我们看看如何使用pyspider框架来写爬虫~
环境配置
在这之前简单介绍一下为什么选择这个框架:
- 这个框架是共和国人民写的(最强大的理由,没有之一)。
- 其次,有webUi的界面,如果你有服务器,那么随时随地都可以编写爬虫,在线调试的机制也做得很好。
- 多任务并行,健全的任务管理机制。
- 支持各种主流数据库。
建议大家去看看文档,因为我这里只能介绍少部分功能,后续得还需要大家自己在使用的时候自行挖掘~pyspider文档
- mac环境
mac环境是很好配置的,只需要打开你的pycharm,然后直接输入pyspider安装即可~
或者在终端pip install pyspider
windows环境
同mac环境,最好直接在pycharm中安装linux环境
因为这个框架一般都是在linux服务器上运行的,本机对框架的需求不大。
1.ubuntu系统:
apt-get install python-dev python-distribute libcurl4-openssl-dev libxml2-dev libxslt1-dev python-lxml
安装上述的依赖环境,等待安装完成后执行pip install pyspider
这样就安装完成了
2.centos系统
centos的话建议使用7系列的版本,这样py直接是2.7,否则还得自己安装py2.7以上的版本。
安装依赖yum install -y gcc ruby-devel libxml2 libxml2-devel libxslt libxslt-devel
然后同样的pip install pyspider
即可完成安装
在终端输入pyspider启动框架,进入http://localhost:5000/,如图所示:
这样就可以开始使用了~
代码预览
import re
from pyspider.libs.base_handler import *
class Handler(BaseHandler):
@every(minutes=24 * 60)
def on_start(self):
self.crawl('http://www.imdb.com/search/title?count=100&title_type=feature,tv_series,tv_movie&ref_=nv_ch_mm_1', callback=self.index_page)
@config(age=24 * 60 * 60)
def index_page(self, response):
for each in response.doc('a[href^="http"]').items():
if re.match("http://www.imdb.com/title/tt\\d+/$", each.attr.href):
self.crawl(each.attr.href, callback=self.detail_page)
self.crawl([x.attr.href for x in response.doc('#right a').items()], callback=self.index_page)
@config(priority=2)
def detail_page(self, response):
return {
"url": response.url,
"title": response.doc('.header > [itemprop="name"]').text(),
"rating": response.doc('.star-box-giga-star').text(),
"director": [x.text() for x in response.doc('[itemprop="director"] span').items()],
}
代码剖析
原谅博主偷了个懒,这段代码不是博主自己敲的,主要是下班回家写博客确实很累,而且这段代码在pyspider的官方demo,运行情况如下,用来做入门很不错~
好了,不说废话了。这次我们的目标是IMDb,非常有名的电影资料网站。首先,我们看到这段代码是否觉得和我们上次看到的unittest结构很类似?没错,首先,我们创建的class Handler继承pyspider的BaseHandler。程序的入口是on_start()函数,这段代码没有写crawl_config,一般如果 网站反爬虫机制很强的话,会带上crawl_config,结构如下:
crawl_config = {
"headers" : headers,
"timeout" : 1000,
"cookies" : Cookie
"proxy" : 192.168.1.1:8888
}
这个我们先简单提一下,在随后的课程再详细介绍。
我们注意到在on_start()函数上方有这样一段代码@every(minutes=24 * 60)
,这是告诉scheduler(调度器)每天调度一次on_start()函数。
self.crawl('http://www.imdb.com/search/title?count=100&title_type=feature,tv_series,tv_movie&ref_=nv_ch_mm_1',callback=self.index_page)
crawl()函数的两个参数,前一个指的是url,后一个指的是回调函数,也就是执行完请求之后调用的函数。
接下来回调指向了index_page()
@config(age=24 * 60 * 60)
这个设置代表了在一天之内忽略已经请求过的网站,一般都会设置的比every的时间长一点,保证少做重复工作,但是这样写也没有问题,因为我们扒的网站信息是在不断更新的,并不是一成不变的。crawl会返回一个response对象,所有的返回信息都在这个对象里面。
for each in response.doc('a[href^="http"]').items():
if re.match("http://www.imdb.com/title/tt\\d+/$", each.attr.href):
self.crawl(each.attr.href, callback=self.detail_page)
self.crawl([x.attr.href for x in response.doc('#right a').items()], callback=self.index_page)
response.doc是一个PyQuery对象,支持Css Selector。
上面第一行代码找出返回网页源代码里面所有的网页链接,然后通过re(python自带的正则模块),匹配出所有格式为http://www.imdb.com/title/tt
开头后面跟着若干数字的网页。如http://www.imdb.com/title/tt3040964/
然后挨个请求self.crawl(each.attr.href, callback=self.detail_page)
,回调为detail_page
最后一行代码是一个递归的调用,就是如果这一页有翻译按钮,那么久请求下一页并递归调用index_page()函数,这样就可以把网页中所有的电影全部抓取下来。
return {
"url": response.url,
"title": response.doc('.header > [itemprop="name"]').text(),
"rating": response.doc('.star-box-giga-star').text(),
"director": [x.text() for x in response.doc('[itemprop="director"] span').items()],
}
detail_page函数是用来吧结果储存在数据库中的,如果我们需要对数据进行过滤,还有on_result(self, result)
函数,它会传入result作为参数。重写on_result的函数之后我们就可以对数据进行过滤。比如过滤掉一些无用的数据。上面的四行数据处理的代码如果看不懂可以参看前面讲过的知识。
至此,今天的代码剖析完毕。今天只是讲了一下pyspider的简单应用,pyspider还有很多很实用的方法可以去发觉,比如和phantomJS结合,抓取ajax的动态数据,只需要你的服务器有phantomJs环境,然后再crawl函数中加入self.crawl('http://www.twitch.tv/directory/game/Dota%202', fetch_type='js', callback=self.index_page)
fetch_type即可。
最后我讲一下pyspider的管理台界面都有什么作用,参见上图的2.png,第一个group我也不是太清楚作用,只知道把group设置为delete然后status设置为stop,24小时之后任务会自动被删除。我们要开始一个任务,需要吧status设置为RUNNING然后在点击后面的run按钮。rate指的是每秒抓取多少次网页,一般情况下建议1以下,不容易被封。brust指的是并发度~后面的active Tasks可以看到现在正在执行的task的状态,最后一个就是抓取的result。
写在最后
今天我们讲解了pyspider的简单使用,下一章我们讲一下scrapy的简单使用,让大家能上手这个爬虫界的利器。
最后附上我前两天讲解抓瓜子网信息的代码。
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Created on 2016-05-01 11:31:38
# Project: guazi
from pyspider.libs.base_handler import *
import pymongo
class Handler(BaseHandler):
client = pymongo.MongoClient('localhost', 27017)
guazi2 = client['guazi2']
car = guazi2['car']
crawl_config = {
}
@every(minutes=24 * 60)
def on_start(self):
self.crawl('http://www.guazi.com/bj/buy', callback=self.index_page)
@config(age=10 * 24 * 60 * 60)
def index_page(self, response):
for each in response.doc('body > div.header > div.hd-top.clearfix > div.c2city > div > div > dl > dd > a').items():
self.crawl(each.attr.href, callback=self.second_page)
@config(age=10 * 24 * 60 * 60)
def second_page(self, response):
num = int(response.doc('div.seqBox.clearfix > p > b').text())
urls = [response.url+'o{}/'.format(str(i)) for i in range(1,num/40+2,1)]
for each in urls:
self.crawl(each, callback=self.third_page)
@config(age=10 * 24 * 60 * 60)
def third_page(self, response):
for each in response.doc('div.list > ul > li > div > a').items():
self.crawl(each.attr.href, callback=self.detail_page)
@config(priority=2)
def detail_page(self, response):
return {
"url": response.url,
"title": response.doc('body > div.w > div > div.laybox.clearfix > div.det-sumright.appoint > div.dt-titbox > h1').text(),
"address": response.doc('body > div.w > div > div.laybox.clearfix > div.det-sumright.appoint > ul > li:nth-child(5) > b').text(),
"cartype": response.doc('body > div.w > div > div.laybox.clearfix > div.det-sumright.appoint > ul > li:nth-child(3) > b').text(),
"price": response.doc('body > div.w > div > div.laybox.clearfix > div.det-sumright.appoint > div.basic-box > div.pricebox > span.fc-org.pricestype > b').text().replace(u'¥',''),
"region":response.doc('#base > ul > li.owner').text()
}
def on_result(self, result):
self.car.insert_one(result)
大家做个参考写的不好,轻喷
有兴趣的同学可以加群498945822一起交流学习哦~~
发现问题的同学欢迎指正,直接说就行,不用留面子,博主脸皮厚!