当前位置:   article > 正文

Scrapy + Mongo 构建一个网页爬虫

爬虫 网页 中的html和图片 mongo

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

Scrapy官方架构图 Scrapy架构图

各部件职能
  • Scrapy Engine 控制数据流在系统组件中的流动,并回调相关动作事件
  • Scheduler 从引擎接受request并入队,当引擎请求他们时返回request
  • Downloader 获取页面数据并提供给引擎,而后提供给spider
  • Spiders 用户定义的爬虫模块
  • Item Pipeline 处理被spider提取出来的item,包括丢弃、验证和持久化等等
  • Downloader middlewares 引擎及下载器之间的hook,处理Downloader传递给引擎的response,同时支持用户挂载自定义逻辑
  • Spider middlewares 引擎及Spider之间的hook,处理spider的输入(response)和输出(items及requests),同时支持用户挂载自定义逻辑
数据流转
  1. 核心引擎从爬虫获取初始url,并生成一个Request任务投入Scheduler调度计划里
  2. 引擎向调度器请求一个新的Request爬取任务并转发给downloader下载器
  3. 下载器载入页面并返回一个Response响应给引擎
  4. 引擎将Response转发给Spider爬虫做 数据提取 和 搜索新的跟进地址
  5. 处理结果由引擎做分发:提取的数据 -> ItemPipeline管道,新的跟进地址Request -> 调度器
  6. 流程返回第二步循环执行,直至调度器中的任务被处理完毕

这里我们以爬取马蜂窝问答页面(www.mafengwo.cn/wenda)的文章为例,说明如何构建一个Scrapy爬虫

搭建环境,安装依赖
  1. #沙箱环境
  2. virtualenv mafengwoenv
  3. source mafengwoenv/bin/activate #进入沙箱环境
  4. #安装两个兼容包
  5. pip install cryptography ndg-httpsclient
  6. #pip安装本次爬虫项目依赖的包
  7. pip install scrapy #爬虫框架
  8. pip install pymongo #mongo引擎的python驱动
创建Scrapy项目
  1. #创建一个名为 mafengwo 的项目
  2. scrapy startproject mafengwo

将默认创建如下结构层次的项目

  1. mafengwo
  2. ├── mafengwo
  3. │ ├── __init__.py
  4. │ ├── items.py #定义抽象数据模型
  5. │ ├── pipelines.py #定义数据处理管道
  6. │ ├── settings.py #配置文件
  7. │ └── spiders #存放项目所有爬虫
  8. │ └── __init__.py
  9. └── scrapy.cfg
Item建模

为了结构化数据,我们需要定义爬取数据结构的抽象模型(严格的说,Item不是必须的,你也可以直接在spider中返回dict数据,但是使用Item能获得额外的数据验证机制)
在 mafengwo/mafengwo/items.py 文件中定义我们需要爬取的标题、作者、时间和内容属性:

  1. # -*- coding: utf-8 -*-
  2. import scrapy
  3. class WendaItem(scrapy.Item):
  4. title = scrapy.Field()
  5. author = scrapy.Field()
  6. time = scrapy.Field()
  7. content = scrapy.Field()
编写问答页爬虫主程序解析页面
  1. #从基础爬虫模板创建我们的爬虫
  2. scrapy genspider --template basic wenda www.mafengwo.cn/wenda

编辑生成的爬虫程序 mafengwo/mafengwo/spiders/wenda.py,完善我们的数据爬取逻辑:

  1. # -*- coding: utf-8 -*-
  2. import scrapy
  3. from mafengwo import items
  4. class WendaSpider(scrapy.Spider):
  5. name = "wenda"
  6. allowed_domains = ["www.mafengwo.cn"] #必须是和start_urls一致的域名,且不能跟上目录
  7. start_urls = [
  8. 'http://www.mafengwo.cn/wenda/',
  9. ]
  10. #框架默认的页面解析器入口,start_urls页面将被传入
  11. def parse(self, response):
  12. #遍历文章列表
  13. for link in response.xpath("//ul[@class='_j_pager_box']/li"):
  14. url = link.xpath("div[@class='wen']/div[@class='title']/a/@href").extract_first()
  15. url = response.url + url[7:] #详情页地址
  16. yield scrapy.Request(url, callback=self.parse_detail) #跟进详情页
  17. #当前页条目抓取完毕后,跟进下一页
  18. # next = response.xpath('xxx').extract_first()
  19. # if next:
  20. # yield scrapy.Request(next, self.parse)
  21. #我们自定义的详情页解析器
  22. def parse_detail(self, response):
  23. item = items.WendaItem()
  24. #抽取页面信息存入模型
  25. item['author'] = response.xpath("//div[@class='pub-bar fr']/a[@class='name']/text()").extract_first()
  26. item['title'] = response.xpath("//div[@class='q-title']/h1/text()").extract_first()
  27. item['time'] = response.xpath("//div[@class='pub-bar fr']/span[@class='time']/span/text()").extract_first()
  28. item['content'] = response.xpath("//div[@class='q-desc']/text()").extract_first()
  29. yield item

DOM调试
scrapy使用Scrapy Selectors(基于XPath 和 CSS )从网页提取数据

Selector有四个基本方法

  1. xpath() 根据xpath规则返回所有节点的selector list
  2. css() 根据css规则返回所有节点的selector list
  3. extract() 序列化该节点为unicode字符串并返回list
  4. re() 根据传入的正则表达式对数据进行提取,返回unicode字符串list列表

xpath简要

  • xpath分为 绝对路径相对路径 两种,并由 路径表达式 组成:
    • /根节点
    • //匹配节点
    • .当前节点
    • ..父节点
  • 路径表达式步进表达式 组成(轴::节点测试[谓语]):
    • 轴(节点层级的相对关系):precedingpreceding-siblingselffollowing-siblingfollowingancestorparentchildattribute...
    • 节点测试(节点匹配):节点名*text()node()...
    • 谓语(过滤):[索引数字][last()][@class="hot"] ...

我们可以通过以下指令进入ScrapyShell来测试xpath规则

  1. scrapy shell --nolog 'http://www.mafengwo.cn/wenda/' #进入交互式工具
  2. >>>sel.xpath('/div/span') #shell中测试xpath
  3. >>>fetch("http://www.mafengwo.cn") #切换页面,将会刷新response等对象

我们也可以直接在爬虫解析器中嵌入一个钩子 scrapy.shell.inspect_response(response, self),从而在爬取过程中回调到ScrapyShell,并在当时特定场景下进行调试

特别的,对于xpath,我们也可以在浏览器console中通过如下方法来测试xpath

$x('规则')

parse解析器调试
我们可以通过以下命令来调试解析器对页面数据的分析情况

scrapy parse  --spider=爬虫名  -c 解析器名  -d 跟进深度  -v  调试地址
数据处理管道Item Pipeline

当Item数据在Spider中被收集之后,它将会被传递到Item Pipeline,并按序执行所有管道 管道接收到Item后可以执行自定义逻辑,同时也决定此Item是否继续通过pipeline,或是被丢弃 管道典型的运用:

  • 爬取结果持久化
  • 清理HTML数据
  • 验证爬取的数据
  • 查重(并丢弃)

爬虫数据常用的持久化策略是mongo引擎:

  1. 它支持海量采集数据的录入
  2. 有很好的伸缩性拓展性,在后期数据变动调整字段的时候能最小化缩减开发成本。

下面我们通过一个mongo管道做爬虫数据的持久化
当然,你也可以直接将持久化逻辑写入爬虫主程序,但是ItemPipline中的持久化逻辑能避免低配IO对爬虫的阻塞

  1. # -*- coding: utf-8 -*-
  2. import pymongo
  3. from scrapy import exceptions
  4. class MongoPipeline(object):
  5. #不用事先创建mongo数据库、集合 和 定义文档,即插即用
  6. mongo_uri = 'localhost'
  7. mongo_database = 'mafengwo'
  8. collection_name = 'wenda_pages'
  9. def __init__(self, mongo_uri, mongo_db):
  10. self.mongo_uri = mongo_uri
  11. self.mongo_db = mongo_db
  12. #下面这个类方法定义了如何由Crawler对象创建这个管道实例
  13. #在这里我们可以通过crawler参数类似于 `crawler.settings.get()` 形式访问到诸如settings、signals等所有scrapy框架内核组件
  14. @classmethod
  15. def from_crawler(cls, crawler):
  16. return cls(cls.mongo_uri, cls.mongo_database)
  17. def open_spider(self, spider):
  18. self.client = pymongo.MongoClient(self.mongo_uri)
  19. self.collection = self.client[self.mongo_db][self.collection_name]
  20. def close_spider(self, spider):
  21. self.client.close()
  22. # 管道必须实现的一个方法,在此实现具体的持久化逻辑
  23. def process_item(self, item, spider):
  24. if (not item['title']):
  25. raise exceptions.DropItem('丢弃一个标题不存在页面')
  26. else:
  27. self.collection.insert(dict(item))
  28. return item
启用mongo持久化管道

我们可以在 mafengwo/mafengwo/settings.py 文件中写入配置

  1. ITEM_PIPELINES = {
  2. 'mafengwo.pipelines.MongoPipeline': 1, #数字确定了不同管道运行的先后顺序,从低到高
  3. }

但是为了不污染全局管道配置,我们把setting写入爬虫自配置中,即爬虫主程序的 custom_settings 属性中:

  1. class WendaSpider(scrapy.Spider):
  2. custom_settings = {
  3. 'ITEM_PIPELINES': {'mafengwo.pipelines.MongoPipeline': 1}
  4. }
运行我们的问答页爬虫
scrapy crawl wenda

当运行 scrapy爬虫模块时,scrapy尝试从中查找Spider的定义,并且在爬取引擎中运行它。 爬取启动后,scrapy首先依据模块的 start_urls 属性创建请求,并将请求的response作为参数传给默认回调函数 parse 。 在回调函数 parse中,我们可以产生(yield)更多的请求,并将响应传递给下一层次的回调函数。

mongo中查看数据
  1. mongo
  2. >show dbs
  3. >use mafengwo #切换数据库
  4. >show collections
  5. >db.wenda_pages.findOne()
  6. >db.wenda_pages.find().limit(3).pretty()

数据库中可以看到,问答页面数据已经成功抓取并录入了

转载于:https://my.oschina.net/u/2400083/blog/724361

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/928784
推荐阅读
  

闽ICP备14008679号