基于python框架Scrapy爬取自己的博客内容过程详解


Posted in Python onAugust 05, 2019

前言

python中常用的写爬虫的库常有urllib2、requests,对于大多数比较简单的场景或者以学习为目的,可以用这两个库实现。这里有一篇我之前写过的用urllib2+BeautifulSoup做的一个抓取百度音乐热门歌曲的例子,有兴趣可以看一下。

本文介绍用Scrapy抓取我在博客园的博客列表,只抓取博客名称、发布日期、阅读量和评论量这四个简单的字段,以求用较简单的示例说明Scrapy的最基本的用法。

环境配置说明

操作系统:Ubuntu 14.04.2 LTS

Python:Python 2.7.6

Scrapy:Scrapy 1.0.3

注意:Scrapy1.0的版本和之前的版本有些区别,有些类的命名空间改变了。

创建项目

执行如下命令创建一个Scrapy项目

scrapy startproject scrapy_cnblogs

创建之后查看项目的目录结构如下:

scrapy_cnblogs
 ├── botcnblogs
 │ ├── __init__.py
 │ 
├── items.py  #用于定义抓取内容的实体
 │ 
├── pipelines.py #处理抓取的item的管道
 │ 
├── settings.py #爬虫需要的配置参数在这里
 │ └── spiders
 │ 
 └── __init__.py
 └── scrapy.cfg   #项目的配置文件,可以不去理会,默认即可

其中scrapy.cfg所在的目录为项目的根目录,此文件是项目的配置文件,项目建立后,此文件的内容可以不用理会。其内容如下:

[settings]
default = botcnblogs.settings
[deploy]
#url = http://localhost:6800/
project = botcnblogs

在items.py文件里定义在抓取网页内容中抽象出来的数据结构的定义,由于这里需要博客名称、发布日期、阅读量和评论量这四个字段,定义的Item结构如下:

from scrapy import Item,Field #引入Item、Field
class BotcnblogsItem(Item):
 # define the fields for your item here like:
 title = Field()  #标题
 publishDate = Field() #发布日期
 readCount = Field() #阅读量
 commentCount = Field() #评论数<br data-filtered="filtered"><br data-filtered="filtered">

在pipelines.py里对爬虫抓取到的信息(这里的信息是已经组织好的上面定义的Item对象)进行处理,官方介绍的典型的应用场景为:

  • 清理HTML数据
  • 验证爬取的数据(检查item包含某些字段)
  • 查重(并丢弃)
  • 将爬取结果保存到数据库中

它的定义也很简单,只需要实现process_item方法即可,此方法有两个参数,一个是item,即要处理的Item对象,另一个参数是spider,即爬虫。

另外还有open_spider和close_spider两个方法,分别是在爬虫启动和结束时的回调方法。

本例中处理很简单,只是将接收的Item对象写到一个json文件中,在__init__方法中以“w+”的方式打开或创建一个item.json的文件,然后把对象反序列化为字符串,写入到item.json文件中。代码如下:

# -*- coding: utf-8 -*- 
import json 
class BotcnblogsPipeline(object):
 def __init__(self):
  self.file = open("item.json", "w+") 
 def process_item(self, item, spider):
  record = json.dumps(dict(item), ensure_ascii=False)+"\n" #此处如果有中文的话,要加上ensure_ascii=False参数,否则可能出现乱码
  self.file.write(record)
  return item 
 def open_spider(self, spider):
  pass 
 def close_spider(self, spider):
  self.file.close()

setting.py是爬虫的配置文件,配置爬虫的一些配置信息,这里用到的就是设置pipelines的ITEM_PIPELINES参数,此参数配置项目中启用的pipeline及其执行顺序,以字典的形式存在,{“pipeline”:执行顺序整数}

此例中的配置如下:

SPIDER_MODULES = ['botcnblogs.spiders']
NEWSPIDER_MODULE = 'botcnblogs.spiders'
ITEM_PIPELINES = {
 'botcnblogs.pipelines.BotcnblogsPipeline': 1,
}

准备工作都做好了,爬虫呢,爬虫在哪里实现呢,我们看到项目中有个spiders目录,里面只有一个init.py文件,没错,爬虫文件需要自己创建,就在这个目录下,这里创建一个botspider.py的文件,对网页进行解析的工作就要在这里实现了,此例中定义的爬虫类继承自CrawlSpider类。

定义一个Spider需要如下几个变量和方法实现:

name:定义spider名字,这个名字应该是唯一的,在执行这个爬虫程序的时候,需要用到这个名字。

allowed_domains:允许爬取的域名列表,例如现在要爬取博客园,这里要写成cnblogs.com

start_urls:爬虫最开始爬的入口地址列表。

rules:如果要爬取的页面不是单独一个或者几个页面,而是具有一定的规则可循的,例如爬取的博客有连续多页,就可以在这里设置,如果定义了rules,则需要自己定义爬虫规则(以正则表达式的方式),而且需要自定义回调函数。

代码说话:

#-*- coding:utf-8 -*-
__author__ = 'linuxfengzheng'
from scrapy.spiders import Spider, Rule
from scrapy.selector import Selector
from botcnblogs.items import BotcnblogsItem
from scrapy.linkextractors import LinkExtractor
import re
from scrapy.spiders import CrawlSpider 
class botspider(CrawlSpider):
 name = "cnblogsSpider" #设置爬虫名称 
 allowed_domains = ["cnblogs.com"] #设置允许的域名
 start_urls = [
  "http://www.cnblogs.com/fengzheng/default.html?page=3", #设置开始爬取页面
 ] 
 rules = (
  Rule(LinkExtractor(allow=('fengzheng/default.html\?page\=([\d]+)', ),),callback='parse_item',follow=True),
 ) #制定规则
  def parse_item(self, response):
  sel = response.selector
  posts = sel.xpath('//div[@id="mainContent"]/div/div[@class="day"]')
  items = []
  for p in posts:
   #content = p.extract()
   #self.file.write(content.encode("utf-8"))
   item = BotcnblogsItem()
   publishDate = p.xpath('div[@class="dayTitle"]/a/text()').extract_first()
 
   item["publishDate"] = (publishDate is not None and [publishDate.encode("utf-8")] or [""])[0]
   #self.file.write(title.encode("utf-8"))
   title = p.xpath('div[@class="postTitle"]/a/text()').extract_first()
   item["title"] = (title is not None and [title.encode("utf-8")] or [""])[0]
 
   #re_first("posted @ 2015-11-03 10:32 风的姿态 阅读(\d+")
 
   readcount = p.xpath('div[@class="postDesc"]/text()').re_first(u"阅读\(\d+\)")
 
   regReadCount = re.search(r"\d+", readcount)
   if regReadCount is not None:
    readcount = regReadCount.group()
   item["readCount"] = (readcount is not None and [readcount.encode("utf-8")] or [0])[0]
 
   commentcount = p.xpath('div[@class="postDesc"]/text()').re_first(u"评论\(\d+\)")
   regCommentCount = re.search(r"\d+", commentcount)
   if regCommentCount is not None:
    commentcount = regCommentCount.group()
   item["commentCount"] = (commentcount is not None and [commentcount.encode("utf-8")] or [0])[0]
   items.append(item) 
  return items
  #self.file.close()

因为1.0版和之前的版本在包上有所改变,这里列出此例中所涉及的不同版本的区别

所在包
1.0版本 之前版本
spider scrapy.spiders scrapy.spider
crawlspider scrapy.spiders scrapy.contrib.spiders
linkextractor scrapy.linkextractors scrapy.contrib.linkextractors
rule scrapy.spiders scrapy.contrib.spiders

爬虫思路:

首先进入到我的博客页面http://www.cnblogs.com/fengzheng/,这是我的博客首页,以列表形式显示已经发布的博文,这是第一页,点击页面下面的下一页按钮,进入第二页,页面地址为http://www.cnblogs.com/fengzheng/default.html?page=2,由此看出网站以page作为参数来表示页数,这样看来爬虫的规则就很简单了, fengzheng/default.html\?page\=([\d]+),这个就是爬虫的规则,爬取default.html页面,page参数为数字的页面,这样无论有多少页都可以遍历到。

当然,如果页面数量很少可以在start_urls列表中,将要爬取的页面都列出来,但是这样当博文数量增多就会出现问题,如下:

start_urls = [
  "http://www.cnblogs.com/fengzheng/default.html?page=1",
  "http://www.cnblogs.com/fengzheng/default.html?page=2",
  "http://www.cnblogs.com/fengzheng/default.html?page=3",
 ]

当爬取的网页具有规则定义的情况下,要继承CrawlSpider爬虫类,使用Spider就不行了,在规则定义(rules)时,如果要对爬取的网页进行处理,而不是简单的需要Url,这时,需要定义一个回调函数,在爬取到符合条件的网页时调用,并且设置follow=Ture,定义如下:

rules = (
  Rule(LinkExtractor(allow=('fengzheng/default.html\?page\=([\d]+)', ),),callback='parse_item',follow=True),
 )

回调函数名称为parse_item,在parse_item方法中,就是真正要分析网页html,获取需要的内容的时候了。观察页面,查看需要的信息在什么位置,如图:

基于python框架Scrapy爬取自己的博客内容过程详解

之后,分析网页源码,分析出xpath

基于python框架Scrapy爬取自己的博客内容过程详解

用如下代码找到所有的class为day的div,每一个就是一个博文区域:

posts = sel.xpath('//div[@id="mainContent"]/div/div[@class="day"]')

之后遍历这个集合,获取所需内容,其中注意一下几点:

因为有中文内容,要对获取的内容进行encode("utf-8")编码

由于评论数和阅读量混在一起,要对那个字符串再进行正则表达式提取 

至此,简单的爬虫已经完成,接下来要运行这个爬虫,cd进入到爬虫项目所在的目录,执行以下命令:

scrapy crawl cnblogsSpider

会输出爬取过程信息

基于python框架Scrapy爬取自己的博客内容过程详解

之后会看到,根目录中多了一个item.json文件,cat此文件内容,可以看到信息已经被提取出来:

基于python框架Scrapy爬取自己的博客内容过程详解

点击这里在github获取源码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python中的进程分支fork和exec详解
Apr 11 Python
Python编程中字符串和列表的基本知识讲解
Oct 14 Python
实例解析Python中的__new__特殊方法
Jun 02 Python
Python程序运行原理图文解析
Feb 10 Python
对numpy中数组元素的统一赋值实例
Apr 04 Python
python实现将读入的多维list转为一维list的方法
Jun 28 Python
python检测文件夹变化,并拷贝有更新的文件到对应目录的方法
Oct 17 Python
python+selenium 鼠标事件操作方法
Aug 24 Python
python中单下划线(_)和双下划线(__)的特殊用法
Aug 29 Python
Python celery原理及运行流程解析
Jun 13 Python
Pytorch中的数据集划分&正则化方法
May 27 Python
opencv用VS2013调试时用Image Watch插件查看图片
Jul 26 Python
基于python实现的百度音乐下载器python pyqt改进版(附代码)
Aug 05 #Python
使用coverage统计python web项目代码覆盖率的方法详解
Aug 05 #Python
基于python实现的百度新歌榜、热歌榜下载器(附代码)
Aug 05 #Python
selenium2.0中常用的python函数汇总
Aug 05 #Python
Django中使用CORS实现跨域请求过程解析
Aug 05 #Python
Flask使用Pyecharts在单个页面展示多个图表的方法
Aug 05 #Python
PyQt5基本控件使用详解:单选按钮、复选框、下拉框
Aug 05 #Python
You might like
十大催泪虐心动漫,你能坚持看到第几部?
2020/03/04 日漫
冰滴咖啡制作步骤
2021/03/03 冲泡冲煮
php 设计模式之 工厂模式
2008/12/19 PHP
逆序二维数组插入一元素的php代码
2012/06/08 PHP
利用curl抓取远程页面内容的示例代码
2013/07/23 PHP
关于 Laravel Redis 多个进程同时取队列问题详解
2017/12/25 PHP
浅谈Laravel中的三种中间件的作用
2019/10/13 PHP
Thinkphp极验滑动验证码实现步骤解析
2020/11/24 PHP
Javascript优化技巧(文件瘦身篇)
2008/01/28 Javascript
JavaScript中常用的运算符小结
2012/01/18 Javascript
JS定义回车事件(实现代码)
2013/07/08 Javascript
点击显示指定元素隐藏其他同辈元素的方法
2014/02/19 Javascript
javascript实现的元素拖动函数宿主为浏览器
2014/07/21 Javascript
jquery图形密码实现方法
2015/03/11 Javascript
浅谈DOCTYPE对$(window).height()取值的影响
2016/07/21 Javascript
NodeJs下的测试框架Mocha的简单介绍
2017/02/22 NodeJs
从零开始做一个pagination分页组件
2017/03/15 Javascript
详解webpack 入门总结和实践(按需异步加载,css单独打包,生成多个入口文件)
2017/06/20 Javascript
关于express与koa的使用对比详解
2018/01/25 Javascript
jQuery表单校验插件validator使用方法详解
2020/02/18 jQuery
微信小程序动态添加和删除组件的现实
2020/02/28 Javascript
python dict remove数组删除(del,pop)
2013/03/24 Python
轻松实现python搭建微信公众平台
2016/02/16 Python
Python 数据结构之旋转链表
2017/02/25 Python
python3+PyQt5 创建多线程网络应用-TCP客户端和TCP服务器实例
2019/06/17 Python
使用python画社交网络图实例代码
2019/07/10 Python
Python:二维列表下标互换方式(矩阵转置)
2019/12/02 Python
基于python+selenium的二次封装的实现
2020/01/06 Python
django使用JWT保存用户登录信息
2020/04/22 Python
pip/anaconda修改镜像源,加快python模块安装速度的操作
2021/03/04 Python
国际化的太阳镜及太阳镜配件零售商:Sunglass Hut
2016/07/26 全球购物
Java编程面试题
2016/04/04 面试题
周年庆促销方案
2014/03/15 职场文书
焦裕禄精神心得体会
2014/09/02 职场文书
2014年效能监察工作总结
2014/11/21 职场文书
Flutter Navigator 实现路由传递参数
2022/04/22 Java/Android