阅读文本大概须要 13 分钟。html
经过以前的文章介绍,你如今应该对 pyspider 有了必定的认识。若是你还不清楚的话,能够再回顾下以前的文章「高效率爬虫框架之 pyspider」。务必要对 pysdpier 有个总体认知,这样你的学习效率才会高。
如今咱们用一个实战项目,来进一步掌握 pyspider 框架的使用。这次的项目爬取的目标是「去哪儿网」,我要将全部攻略的做者、标题、出发日期、人均费用、攻略正文等保存下来,存储到 MongoDB 中。python
请确保已经安装了 pyspider 和 PhantomJS,安装好了 MongoDB 并正常运行服务,还须要安装 PyMongo 库。这些教程网上都有详细资料,你们自行搜索。web
执行以下命令就能够启动 pyspider:算法
pyspider all
复制代码
运行效果:
数据库
这样能够启动 pyspider 的全部组件,包括 PhantomJS、ResultWorker、Processer、Fetcher、Scheduler、WebUI,这些都是 pysipder 运行必备的组件。最后一行输出 WebUI 运行在 5000 端口上。能够打开浏览器,输入连接 http://localhost:5000,这时咱们会看到页面。浏览器
此页面即是 pyspider 的 WebUI,咱们能够用它来管理项目、编写代码、在线调试、监控任务等bash
新建一个项目,点击右边的 Create 按钮,在弹出的浮窗里输入项目的名称和爬取的连接,再点击 create 按钮,这样就成功建立了一个项目。框架
接下来会看到 pyspider 的项目编辑和调试页面编辑器
左侧就是代码的调试页面,点击左侧右上角的 run 单步调试爬虫程序,在左侧下半部分能够预览当前的爬取页面。右侧是代码编辑页面,咱们能够直接编辑代码和保存代码,不须要借助于 IDE。
注意右侧,pyspider 已经帮咱们生成了一段代码。代码以下所示:ide
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_config 属性。咱们能够将本项目的全部爬取配置统必定义到这里,如定义 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 按钮,便可看到页面下方 follows 便会出现一个标注,其中包含数字 1 ,这表明有新的爬取请求产生。
左栏左上角会出现当前 run 的配置文章,这里有一个 callback 为 on_start,这说明点击 run 以后实际是执行了 on_start() 方法。在 on_start() 方法中,咱们利用 crawl() 方法生成一个爬取请求,那下方 follows 部分的数字 1 就表明了这一个爬取请求。
点击下方的 follows 按钮,便可看到生成的爬取请求的连接。每一个连接的右侧还有一个箭头按钮。
点击该箭头,咱们就能够对此连接进行爬取,也就是爬取攻略的首页内容。
上方的 callback 已经变成了 index_page,这就表明当前运行了 index_page() 方法。index_page() 接收到的 response 参数就是刚才生成的第一个爬取请求的 Response 对象。index_page() 方法经过调用 doc() 方法,传入提取全部 a 节点的 CSS 选择器,而后获取 a 节点的属性 href,这样实际上就是获取了第一个爬取页面中的全部连接。而后在 index_page() 方法里遍历了全部连接,同时调用 crawl() 方法,就把这一个个的连接构形成新的爬取请求了。因此最下方 follows 按钮部分有 231 的数字标记,这表明新生成了 231 个爬取请求,同时这些请求的 URL 都呈如今当前的页面了。
再点击下方的 web 按钮,便可预览当前爬取结果的页面。
这里编辑器并非很友好,显示的页面只有一小些,但并不会妨碍咱们的抓取。当前看到的页面结果和浏览器看到的几乎是彻底一致的,在这里咱们能够方便地查看页面请求的结果。
点击 html 按钮便可查看当前页面的源代码。
咱们刚才在 index_page() 方法中提取了全部的连接并生成了新的爬取请求。可是很明显要爬取的确定不是全部连接,只须要攻略详情的页面连接就够了,因此咱们要修改一下当前 index_page() 里提取连接时的 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 节点。可是在浏览器中咱们看到了图片,这是由于这张图片是后期通过 JavaScrpit 出现的。那么,咱们该如何获取呢?
幸运的是,pyspider 内部对接了 PhatomJS,那么咱们只须要修改一个参数便可。
咱们将 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)
复制代码
接下来,咱们来试试它的抓取效果。
点击左栏上方的左箭头返回,从新调用 index_page() 方法生成新的爬取详情页的 Request。
再点击新生成的详情页的 Request 的爬取按钮,这时咱们即可以看到页面变成了这样子。
图片被成功渲染处理,这就是启用了 PhantomJS 渲染后的结果。只须要添加一个 fetch_type 参数便可,这很是方便。
最后就是将详情页面中须要的信息提取处理。最终的 detail_page() 方法改写以下:
def detail_page(self, response):
return {
"url": response.url,
"title": response.doc('#booktitle').text(),
"date": response.doc('.when .data').text(),
"day": response.doc('.howlong .data').text(),
"who": response.doc('.who .data').text(),
"text": response.doc('#b_panel_schedule').text(),
"image": response.doc('.cover_img').attr.src,
}
复制代码
咱们分别提取了页面的连接、标题、出行日期、出现天数、人物、攻略正文、头图信息,将这些信息构形成一个字典。
从新运行,便可发现输出结果。
左栏中输出了最终构造的字典信息,这就是一篇攻略的抓取结果。
返回爬虫的主页面,将爬虫的 status 设置成 DEBUG 或 RUNNING,点击右侧的 Run 按钮便可开始爬取。
在最左侧咱们能够定义项目的分组,以方便管理。rate/burst 表明当前的爬取速率。rate 表明 1 秒发出多少个请求,burst 至关于流量控制中的令牌桶算法的令牌数,rate 和 burst 设置的越大,爬取速率越快,固然速率须要考虑本机性能和爬取过快被封的问题。process 中的 5m、1h、1d 指 的是最近 5 分、1 小时、1 天内的请求状况,all 表明全部的请求状况。请求由不一样颜色表示、蓝色的表明等待被执行的请求,绿色的表明成功的请求,黄色的表明请求失败后等待重试的请求,红色的表明失败次数过多而被忽略的请求,这样能够直观知道爬取的进度和请求状况。
点击 Active Tasks,便可查看最近请求的详细情况。
点击 Result,便可查看全部的爬取结果。
点击右上角的按钮,便可获取数据的 JSON、CSV 格式。
本文首发于公众号「痴海」,天天分享 python 干货,回复「1024」,你懂得。