实践Python的爬虫框架Scrapy来抓取豆瓣电影TOP250


Posted in Python onJanuary 20, 2016

安装部署Scrapy

在安装Scrapy前首先需要确定的是已经安装好了Python(目前Scrapy支持Python2.5,Python2.6和Python2.7)。官方文档中介绍了三种方法进行安装,我采用的是使用 easy_install 进行安装,首先是下载Windows版本的setuptools(下载地址:http://pypi.python.org/pypi/setuptools),下载完后一路NEXT就可以了。
安装完setuptool以后。执行CMD,然后运行一下命令:

easy_install -U Scrapy

同样的你可以选择使用pip安装,pip的地址:http://pypi.python.org/pypi/pip
使用pip安装Scrapy的命令为

pip install Scrapy

如果你的电脑先前装过visual studio 2008 或 visual studio 2010那么一起顺利,Scrapy已经安装完成。如果出现下列报错:Unable to find vcvarsall.bat 那么你需要折腾下。你可以安装visual studio 后进行安装或采用下面的方式进行解决:
首先安装MinGW(MinGW下载地址:http://sourceforge.net/projects/mingw/files/),在MinGW的安装目录下找到bin的文件夹,找到mingw32-make.exe,复制一份更名为make.exe;
把MinGW的路径添加到环境变量path中,比如我把MinGW安装到D:\MinGW\中,就把D:\MinGW\bin添加到path中;
打开命令行窗口,在命令行窗口中进入到要安装代码的目录下;
输入如下命令 setup.py install build ?compiler=mingw32 就可以安装了。

如果出现“xslt-config' 不是内部或外部命令,也不是可运行的程序或批处理文件。”错误,原因主要是lxml安装不成功,只要上http://pypi.python.org/simple/lxml/下载个exe文件进行安装就可以了。
下面就可以进入正题了。

新建工程
让我们来用爬虫获取豆瓣电影Top 250的电影信息吧。开始之前,我们新建一个Scrapy工程。因为我用的Win7,所以在CMD中进入一个我希望保存代码的目录,然后执行:

D:\WEB\Python>scrapy startproject doubanmoive

这个命令会在当前目录下创建一个新的目录doubanmoive,目录结构如下:

D:\WEB\Python\doubanmoive>tree /f
Folder PATH listing for volume Data
Volume serial number is 00000200 34EC:9CB9
D:.
│ scrapy.cfg
│
└─doubanmoive
 │ items.py
 │ pipelines.py
 │ settings.py
 │ __init__.py
 │
 └─spiders
   __init__.py

这些文件主要为:

  • doubanmoive/items.py: 定义需要获取的内容字段,类似于实体类。
  • doubanmoive/pipelines.py: 项目管道文件,用来处理Spider抓取的数据。
  • doubanmoive/settings.py: 项目配置文件
  • doubanmoive/spiders: 放置spider的目录

定义项目(Item)

Item是用来装载抓取数据的容器,和Java里的实体类(Entity)比较像,打开doubanmoive/items.py可以看到默认创建了以下代码。

from scrapy.item import Item, Field

class DoubanmoiveItem(Item):
  pass

我们只需要在 Doubanmoive 类中增加需要抓取的字段即可,如 name=Field() ,最后根据我们的需求完成代码如下。

from scrapy.item import Item, Field

class DoubanmoiveItem(Item):
 name=Field()#电影名
 year=Field()#上映年份
 score=Field()#豆瓣分数
 director=Field()#导演
 classification=Field()#分类
 actor=Field()#演员

编写爬虫(Spider)

Spider是整个项目中最核心的类,在这个类里我们会定义抓取对象(域名、URL)以及抓取规则。Scrapy官方文档中的教程是基于 BaseSpider 的,但 BaseSpider 只能爬取给定的URL列表,无法根据一个初始的URL向外拓展。不过除了 BaseSpider ,还有很多可以直接继承 Spider 的类,比如 scrapy.contrib.spiders.CrawlSpider 。

在 doubanmoive/spiders 目录下新建moive_spider.py文件,并填写代码。

# -*- coding: utf-8 -*-
from scrapy.selector import Selector
from scrapy.contrib.spiders import CrawlSpider,Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from doubanmoive.items import DoubanmoiveItem

class MoiveSpider(CrawlSpider):
 name="doubanmoive"
 allowed_domains=["movie.douban.com"]
 start_urls=["http://movie.douban.com/top250"]
 rules=[
  Rule(SgmlLinkExtractor(allow=(r'http://movie.douban.com/top250\?start=\d+.*'))),
  Rule(SgmlLinkExtractor(allow=(r'http://movie.douban.com/subject/\d+')),callback="parse_item"),  
 ]

 def parse_item(self,response):
  sel=Selector(response)
  item=DoubanmoiveItem()
  item['name']=sel.xpath('//*[@id="content"]/h1/span[1]/text()').extract()
  item['year']=sel.xpath('//*[@id="content"]/h1/span[2]/text()').re(r'\((\d+)\)')
  item['score']=sel.xpath('//*[@id="interest_sectl"]/div/p[1]/strong/text()').extract()
  item['director']=sel.xpath('//*[@id="info"]/span[1]/a/text()').extract()
  item['classification']= sel.xpath('//span[@property="v:genre"]/text()').extract()
  item['actor']= sel.xpath('//*[@id="info"]/span[3]/a[1]/text()').extract()
  return item

代码说明: MoiveSpider 继承Scrapy中的 CrawlSpider , name , allow_domains , start_url 看名字就知道什么含义,其中rules稍微复杂一些,定义了URL的抓取规则,符合 allow 正则表达式的链接都会加入到Scheduler(调度程序)。通过分析豆瓣电影Top250的分页URL http://movie.douban.com/top250?start=25&filter=&type= 可以得到以下规则

Rule(SgmlLinkExtractor(allow=(r'http://movie.douban.com/top250\?start=\d+.*'))),
而我们真正要抓取的页面是每一个电影的详细介绍,如肖申克的救赎的链接为 http://movie.douban.com/subject/1292052/ ,那只有 subject 后面的数字是变化的,根据正则表达式得到如下代码。我们需要抓取这种类型链接中的内容,于是加入callback属性,将Response交给parse_item函数来处理。

Rule(SgmlLinkExtractor(allow=(r'http://movie.douban.com/subject/\d+')),callback="parse_item"),     
在 parse_item 函数中的处理逻辑非常简单,获取符合条件链接的代码,然后根据一定的规则抓取内容赋给item并返回 Item Pipeline 。获取大部分标签的内容不需要编写复杂的正则表达式,我们可以使用 XPath 。 XPath 是一门在 XML 文档中查找信息的语言,但它也可以用在HTML中。下表列出了常用表达式。

表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

如 //*[@id="content"]/h1/span[1]/text() 获取的结果是在id为content的任意元素下h1元素下的span列表中第一个元素的文本内容。我们可以通过Chrome开发者工具(F12)来获取某内容的XPath表达式,具体操作为在需要抓取的内容上点击审查元素,下方就会出现开发者工具,并定位到该元素,在内容上点击右键,选择复制XPath。

实践Python的爬虫框架Scrapy来抓取豆瓣电影TOP250

存储数据

爬虫获取到数据以后我们需要将其存储到数据库中,之前我们提到该操作需要靠项目管道(pipeline)来处理,其通常执行的操作为:

  • 清洗HTML数据
  • 验证解析到的数据(检查项目是否包含必要的字段)
  • 检查是否是重复数据(如果重复就删除)
  • 将解析到的数据存储到数据库中

由于我们获取的数据格式多种多样,有一些存储在关系型数据库中并不方便,所以我在写完MySQL版本的Pipeline之后又写了一个MongoDB的。

MySQL版本: 

# -*- coding: utf-8 -*-
from scrapy import log
from twisted.enterprise import adbapi
from scrapy.http import Request

import MySQLdb
import MySQLdb.cursors


class DoubanmoivePipeline(object):
 def __init__(self):
  self.dbpool = adbapi.ConnectionPool('MySQLdb',
    db = 'python',
    user = 'root',
    passwd = 'root',
    cursorclass = MySQLdb.cursors.DictCursor,
    charset = 'utf8',
    use_unicode = False
  )
 def process_item(self, item, spider):
  query = self.dbpool.runInteraction(self._conditional_insert, item)
  query.addErrback(self.handle_error)
  return item

 def _conditional_insert(self,tx,item):
  tx.execute("select * from doubanmoive where m_name= %s",(item['name'][0],))
  result=tx.fetchone()
  log.msg(result,level=log.DEBUG)
  print result
  if result:
   log.msg("Item already stored in db:%s" % item,level=log.DEBUG)
  else:
   classification=actor=''
   lenClassification=len(item['classification'])
   lenActor=len(item['actor'])
   for n in xrange(lenClassification):
    classification+=item['classification'][n]
    if n<lenClassification-1:
     classification+='/'
   for n in xrange(lenActor):
    actor+=item['actor'][n]
    if n<lenActor-1:
     actor+='/'

   tx.execute(\
    "insert into doubanmoive (m_name,m_year,m_score,m_director,m_classification,m_actor) values (%s,%s,%s,%s,%s,%s)",\
    (item['name'][0],item['year'][0],item['score'][0],item['director'][0],classification,actor))
   log.msg("Item stored in db: %s" % item, level=log.DEBUG)

 def handle_error(self, e):
  log.err(e)

MongoDB版本:

# -*- coding: utf-8 -*-
import pymongo

from scrapy.exceptions import DropItem
from scrapy.conf import settings
from scrapy import log

class MongoDBPipeline(object):
 #Connect to the MongoDB database
 def __init__(self):
  connection = pymongo.Connection(settings['MONGODB_SERVER'], settings['MONGODB_PORT'])
  db = connection[settings['MONGODB_DB']]
  self.collection = db[settings['MONGODB_COLLECTION']]

 def process_item(self, item, spider):
  #Remove invalid data
  valid = True
  for data in item:
   if not data:
   valid = False
   raise DropItem("Missing %s of blogpost from %s" %(data, item['url']))
  if valid:
  #Insert data into database
   new_moive=[{
    "name":item['name'][0],
    "year":item['year'][0],
    "score":item['score'][0],
    "director":item['director'],
    "classification":item['classification'],
    "actor":item['actor']
   }]
   self.collection.insert(new_moive)
   log.msg("Item wrote to MongoDB database %s/%s" %
   (settings['MONGODB_DB'], settings['MONGODB_COLLECTION']),
   level=log.DEBUG, spider=spider) 
  return item

可以看到其基本的处理流程是一样,但是MySQL不太方便的一点就是需要将数组类型的数据通过分隔符转换。而MongoDB支持存入List、Dict等多种类型的数据。

配置文件

在运行爬虫之前还需要将在 settings.py 中增加一些配置信息。

BOT_NAME = 'doubanmoive'
SPIDER_MODULES = ['doubanmoive.spiders']
NEWSPIDER_MODULE = 'doubanmoive.spiders'
ITEM_PIPELINES={
 'doubanmoive.mongo_pipelines.MongoDBPipeline':300,
 'doubanmoive.pipelines.DoubanmoivePipeline':400,
}
LOG_LEVEL='DEBUG'

DOWNLOAD_DELAY = 2
RANDOMIZE_DOWNLOAD_DELAY = True
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.54 Safari/536.5'
COOKIES_ENABLED = True

MONGODB_SERVER = 'localhost'
MONGODB_PORT = 27017
MONGODB_DB = 'python'
MONGODB_COLLECTION = 'test'

ITEM_PIPELINES 中定义了MySQL和MongoDB两个Pipeline文件,后面的数字代表执行的优先级顺序,范围为0~1000。 而中间的 DOWNLOAD_DELAY 等信息是为了防止爬虫被豆瓣Ban掉,增加了一些随机延迟,浏览器代理等。最后的就是MongoDB的配置信息,MySQL也可以参考这种方式来写。

至此为止,抓取豆瓣电影的爬虫就已经完成了。在命令行中执行 Scrapy crawl doubanmoive 让蜘蛛开始爬行吧!

Python 相关文章推荐
python监控网卡流量并使用graphite绘图的示例
Apr 27 Python
浅谈插入排序算法在Python程序中的实现及简单改进
May 04 Python
python脚本实现数据导出excel格式的简单方法(推荐)
Dec 30 Python
python 限制函数调用次数的实例讲解
Apr 21 Python
Python用字典构建多级菜单功能
Jul 11 Python
python 批量添加的button 使用同一点击事件的方法
Jul 17 Python
django-rest-swagger的优化使用方法
Aug 29 Python
win10子系统python开发环境准备及kenlm和nltk的使用教程
Oct 14 Python
Python调用scp向服务器上传文件示例
Dec 22 Python
简单了解Django项目应用创建过程
Jul 06 Python
python爬虫使用正则爬取网站的实现
Aug 03 Python
单身狗福利?Python爬取某婚恋网征婚数据
Jun 03 Python
Python的爬虫包Beautiful Soup中用正则表达式来搜索
Jan 20 #Python
Python使用Beautiful Soup包编写爬虫时的一些关键点
Jan 20 #Python
Python制作爬虫抓取美女图
Jan 20 #Python
编写Python爬虫抓取豆瓣电影TOP100及用户头像的方法
Jan 20 #Python
以视频爬取实例讲解Python爬虫神器Beautiful Soup用法
Jan 20 #Python
使用Python的urllib和urllib2模块制作爬虫的实例教程
Jan 20 #Python
使用python实现省市三级菜单效果
Jan 20 #Python
You might like
PHP实现在线阅读PDF文件的方法
2015/06/17 PHP
php数组生成html下拉列表的方法
2015/07/20 PHP
PHP模板引擎Smarty内置变量调解器用法详解
2016/04/11 PHP
Laravel框架实现redis集群的方法分析
2017/09/14 PHP
浅谈PHP array_search 和 in_array 函数效率问题
2019/10/15 PHP
输入框的字数时时统计—关于 onpropertychange 和 oninput 使用
2011/10/21 Javascript
利用js实现遮罩以及弹出可移动登录窗口
2013/07/08 Javascript
JS实现拖动示例代码
2013/11/01 Javascript
javascript确认框的三种使用方法
2013/12/17 Javascript
AngularJS中isolate scope的用法分析
2016/11/22 Javascript
ES6通过babel转码使用webpack使用import关键字
2016/12/13 Javascript
Vue组件tree实现树形菜单
2017/04/13 Javascript
详解angular 中的自定义指令之详解API
2017/06/20 Javascript
vue非父子组件通信问题及解决方法
2018/06/11 Javascript
利用Vue构造器创建Form组件的通用解决方法
2018/12/03 Javascript
小程序登录/注册页面设计的实现代码
2019/05/24 Javascript
微信小程序 接入腾讯地图的两种写法
2021/01/12 Javascript
python 实现文件的递归拷贝实现代码
2012/08/02 Python
Python类的多重继承问题深入分析
2014/11/09 Python
python flask实现分页效果
2017/06/27 Python
解析Python中的eval()、exec()及其相关函数
2017/12/20 Python
python如何通过twisted实现数据库异步插入
2018/03/20 Python
Python从Excel中读取日期一列的方法
2018/11/28 Python
pandas每次多Sheet写入文件的方法
2018/12/10 Python
在PyCharm中控制台输出日志分层级分颜色显示的方法
2019/07/11 Python
FFrpc python客户端lib使用解析
2019/08/24 Python
html5实现的便签特效(实战分享)
2013/11/29 HTML / CSS
世界上最大的家庭自动化公司:Smarthome
2017/12/20 全球购物
Eclipse面试题
2014/03/22 面试题
人力资源部副职的竞聘演讲稿
2014/01/07 职场文书
新闻传媒系求职信范文
2014/04/19 职场文书
企业安全生产承诺书
2014/05/22 职场文书
个人查摆问题及整改措施
2014/10/16 职场文书
教师专业技术工作总结2015
2015/05/13 职场文书
公司开业主持词
2015/07/02 职场文书
物资采购管理制度
2015/08/06 职场文书