pyspider 实战项目之爬取去哪儿

阅读文本大概须要 13 分钟。html

经过以前的文章介绍,你如今应该对 pyspider 有了必定的认识。若是你还不清楚的话,能够再回顾下以前的文章「高效率爬虫框架之 pyspider」。务必要对 pysdpier 有个总体认知,这样你的学习效率才会高。
如今咱们用一个实战项目,来进一步掌握 pyspider 框架的使用。这次的项目爬取的目标是「去哪儿网」,我要将全部攻略的做者、标题、出发日期、人均费用、攻略正文等保存下来,存储到 MongoDB 中。python

1 准备工做

请确保已经安装了 pyspider 和 PhantomJS,安装好了 MongoDB 并正常运行服务,还须要安装 PyMongo 库。这些教程网上都有详细资料,你们自行搜索。web

2 启动 pyspider

执行以下命令就能够启动 pyspider:算法

pyspider all
复制代码

运行效果:
数据库

这样能够启动 pyspider 的全部组件,包括 PhantomJS、ResultWorker、Processer、Fetcher、Scheduler、WebUI,这些都是 pysipder 运行必备的组件。最后一行输出 WebUI 运行在 5000 端口上。能够打开浏览器,输入连接 http://localhost:5000,这时咱们会看到页面。浏览器

此页面即是 pyspider 的 WebUI,咱们能够用它来管理项目、编写代码、在线调试、监控任务等bash

3 建立项目

新建一个项目,点击右边的 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 对象作解析,解析以后将结果以字典的形式返回。固然咱们也能够进行后续处理,如将结果保存到数据库。

接下来,咱们改写一下代码来实现攻略的爬取。

4 爬取首页

点击左栏右上角的 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,这就表明咱们成功添加了下一页的爬取请求。

如今,索引列表页面的解析过程咱们就完成了。

5 爬取详情页

任意选取一个详情页进入,点击前 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,
        }
复制代码

咱们分别提取了页面的连接、标题、出行日期、出现天数、人物、攻略正文、头图信息,将这些信息构形成一个字典。
从新运行,便可发现输出结果。

左栏中输出了最终构造的字典信息,这就是一篇攻略的抓取结果。

6 启动爬虫

返回爬虫的主页面,将爬虫的 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」,你懂得。


相关文章
相关标签/搜索