pyspider的基本使用
计划爬取去哪儿网的旅游攻略,链接为http://travel.qunar.com/travelbook/list.htm,要将所有攻略的作者、标题、出发日期、人均费用、攻略正文等保存下来,存储到MongoDB中。
启动pyspider
执行如下命令启动pyspider:
pyspider all
运行结果如下:
这样可以启动pyspider的所有组件,包括PhantomJS、ResultWorker、Processer、Fetcher、Scheduler、WebUI,这些都是pyspider运行必备的组件。最后一行输出提示WebUI运行在5000端口上。可以打开浏览器,输入链接http://localhost:5000,这时打开浏览器,可以看到:
此页面便是pyspider的WebUI,我们可以用它来管理项目、编写代码、在线调试、监控任务等。
创建项目
在WebUI中,点击右侧的Create按钮,在弹出的浮窗里输入项目的名称和爬取的链接,再点击Create按钮,这样就成功创建一个项目。接下来会看到pyspider的项目遍及和调试页面,如图所示:
左侧就是代码的调试页面,点击左侧右上角的run单步调试爬虫程序,在左侧下半部分可以预览当前的爬取页面。右侧是代码编辑页面,我们可以直接编辑代码和保存代码,不需要借助IDE。
注意右侧,pyspider已经帮我们生成了一段代码,代码如下所示:
from pyspider.libs.base_handler import *
class Handler(BaseHandler):
crawl_config = {
}
@every(minutes=24 * 60)
def on_start(self):
self.crawl('http://travel.qunar.com/travelbook/list.htm', callback=self.index_page)
@config(age=10 * 24 * 60 * 60)
def index_page(self, response):
for each in response.doc('a[href^="http"]').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('title').text(),
}
这里的Handler就是pyspider爬虫的主类,我们可以在此处定义爬取、解析、存储的逻辑。整个爬虫的功能只需要一个Handler即可完成。
接下来我们可以看到一个crawl_fonfig属性。我们可以将本项目的所有爬取配置统一定义到这里,如定义Headers、设置代理等,配置之后全局生效。
然后,on_start()方法是爬取入口,初始的爬取请求会在这里产生,该方法通过调用crawl()方法即可新建一个爬取请求,第一个参数是爬取的URL,这里自动替换成我们所定义的URL。crawl()方法还有一个参数callback,它指定了这个页面爬取成功后用哪个方法进行解析,代码中指定为index_page()方法,即如果这个URL对应的页面爬取成功了,那Response将交给index_page()方法解析。
index_page()方法恰好接收这个Response参数,Response对接了pyquery。我们直接调用doc()方法传入相应的CSS选择器,就可以像pyquery一样解析此页面,代码中默认是a[href^=“http”],也就是说该方法解析了页面的所有链接,然后将链接遍历,再次调用了crawl()方法生成了新的爬取请求,同时再指定了callback为detail_page,意思是说这些页面爬取成功了就调用detail_page()方法解析。这里,index_page()实现了两个功能,一是将爬取的结果进行解析,二是生成新的爬取请求。
detail_page()同样接收Response作为参数。detail_page()抓取的就是详情页的信息,就不会生成新的请求,只对Response对象做解析,解析之后将结果以字典形式返回。当然我们也可以进行后续处理,如将结果保存到数据库。
爬取首页
点击左栏右上角的run按钮,即可看到页面下方follow便会出现一个标注,其中包含数字1,这代表有新的爬取请求产生,如图所示:
左栏左上角会出现当前run的配置文件,这里有一个callback为on_start,这说明点击run之后实际是执行了on_start()方法。在on_start()方法中,我们利用crawl()方法生成一个爬取请求,那下方follows部分的数字1就代表了这一个爬取请求。
点击下方的follows按钮,即可看到生成的爬取请求的链接。每个链接的右侧还有一个箭头按钮,如图所示。点击该箭头,我们就可以对此链接进行爬取,也就是爬取攻略的首页内容。
点击下方的follows按钮,即可看到生成的爬取请求的链接。每个链接的右侧还有一个箭头按钮,点击该箭头,我们就可以对此链接进行爬取,也就是爬取攻略的首页内容,如上图所示。
上方的callback已经变成了index_page,这就代表当前运行了index_page()方法。index_page()接收到的response参数就是刚才生成的第一个爬取请求的Response对象。index_page()方法通过调用doc()方法,传入提取所有a节点的CSS选择器,然后获取a节点的属性href,这样实际上就是获取了第一个爬取页面中的所有链接。然后再index_page()方法里遍历了所有链接,同时调用crawl()方法,就把这一个个的链接构造成新的爬取请求了。所以最下方follows按钮部分有229的数字标记,这代表新生成了229个爬取请求,同时这些请求的URL都呈现在当前页面了。
再点击下方的web按钮,即可预览当前爬取结果的页面。点击html按钮即可查看当前页面的源代码,如果需要分析代码的结构,我们可以直接参考页面源码。
我们刚才在index_page()方法中提取了所有的链接并生成了新的爬取请求。但是很明显要爬取的肯定不是所有链接,只需要攻略详情的页面链接就够了,所以我们要修改一下当前index_page()里提取链接时的CSS选择器。
接下来需要另外一个工具。首先切换到Web页面,找到攻略的标题,点击下方的enable css selector helper,点击标题。这时候我们看到标题外多了一个红框,上方出现了一个CSS选择器,这就是当前标题对应的CSS选择器。
在右侧代码选中要更改的区域,点击左栏的右箭头,此时在上方出现的标题的CSS选择器会被替换到右侧代码中:
这样就成功完成了CSS选择器的替换,非常便捷。
重新点击左栏右上角的run按钮,即可重新执行index_page()方法。此时的follows就变成了10个,也就是说我们提取的只有当前页面的10个攻略。
我们现在抓取的只是第一页的内容,还需要抓取后续页面,所以还需要一个爬取链接,即爬取下一页的攻略列表页面。我们再利用crawl()方法添加下一页的爬取请求,在index_page()方法里面添加如下代码,然后点击save保存:
next = response.doc('.next').attr.href
self.crawl(next, callback=self.index_page)
利用CSS选择器选中下一页的链接,获取它的href属性,也就获取了页面的URL。然后将该URL传给crawl()方法,同时指定回调函数,注意这里回调函数仍然指定为index_page()方法,因为下一页的结构与此页相同。
重新点击run按钮,这时就可以看到11个爬取请求。follows按钮会显示11,这就代表我们成功添加了下一页的爬取请求。
现在,索引列表页的解析过程我们就完成了。
爬取详情页
任意选取一个详情页进入,点击前10个爬取请求中的任意一个的右箭头,执行详情页的爬取。
切换到Web页面预览效果,页面下拉之后,头图正文中的一些图片一直显示加载中。查看源代码,我们没有看到img节点。出现此现象的原因是pyspider默认发送HTTP请求,请求的HTML文档本身就不包含img节点。但是在浏览器中我们看到了图片,这时因为这张图片时后期经过JavaScript出现的。
我们将index_page()中生成抓取详细页的请求方法添加一个参数fetch_type,改写的index_page()变为如下内容:
def index_page(self, response):
for each in response.doc('li > .tit > a').items():
self.crawl(each.attr.href, callback=self.detail_page, fetch_type='js')
next = response.doc('.next').attr.href
self.crawl(next, callback=self.index_page)
重新抓取,可以发现页面中的图片被正常加载了,这就是启用了PhantomJS渲染后的结果。只需要添加一个fetch_type参数即可。
最后就是将详情页中需要的信息提取出来。
(运行过程中,出现过一个问题,就是获取信息部位,出现了HTTP 501 错误,查询之后发现,原来在使用PhantomJS后,PhantomJS未加入PATH,安装好后便能正常运行了。)
左栏中输出了最终构造的字典信息,这就是一篇攻略的抓取结果。