实践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实现bucket排序算法实例分析
May 04 Python
如何用itertools解决无序排列组合的问题
May 18 Python
Python学习入门之区块链详解
Jul 25 Python
Python 基础教程之str和repr的详解
Aug 20 Python
python使用SMTP发送qq或sina邮件
Oct 21 Python
Python简单计算给定某一年的某一天是星期几示例
Jun 27 Python
python matlibplot绘制多条曲线图
Feb 19 Python
python利用requests库进行接口测试的方法详解
Jul 06 Python
对python3中pathlib库的Path类的使用详解
Oct 14 Python
python下PyGame的下载与安装过程及遇到问题
Aug 04 Python
Windows+Anaconda3+PyTorch+PyCharm的安装教程图文详解
Apr 03 Python
OpenCV图像变换之傅里叶变换的一些应用
Jul 26 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和ACCESS写聊天室(一)
2006/10/09 PHP
php cookies中删除的一般赋值方法
2011/05/07 PHP
php连接数据库代码应用分析
2011/05/29 PHP
php读取excel文件的简单实例
2013/08/26 PHP
zend framework重定向方法小结
2016/05/28 PHP
CMSPRESS 10行代码搞定 PHP无限级分类2
2018/03/30 PHP
基于jquery的jqDnR拖拽溢出的修改
2011/02/12 Javascript
Jjcarousellite 实现图片列表滚动的简单实例
2013/11/29 Javascript
javascript实现网页屏蔽Backspace事件,输入框不屏蔽
2015/07/21 Javascript
jQuery+HTML5+CSS3制作支持响应式布局时间轴插件
2016/08/10 Javascript
Vuejs第一篇之入门教程详解(单向绑定、双向绑定、列表渲染、响应函数)
2016/09/09 Javascript
ES2015 Symbol 一种绝不重复的值
2016/12/25 Javascript
Angular+Node生成随机数的方法
2017/06/16 Javascript
Javascript 严格模式use strict详解
2017/09/16 Javascript
React Native中NavigatorIOS组件的简单使用详解
2018/01/27 Javascript
Vue.js 中制作自定义选择组件的代码附演示demo
2020/02/28 Javascript
Vue 实现监听窗口关闭事件,并在窗口关闭前发送请求
2020/09/01 Javascript
[43:36]Liquid vs Mineski 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
Python操作列表之List.insert()方法的使用
2015/05/20 Python
python实现多线程端口扫描
2019/08/31 Python
Python requests.post方法中data与json参数区别详解
2020/04/30 Python
CSS3结构性伪类选择器九种写法
2012/04/18 HTML / CSS
使用css实现android系统的loading加载动画
2019/07/25 HTML / CSS
html5 桌面提醒:Notifycations应用介绍
2012/11/27 HTML / CSS
Html5 webRTC简单实现视频调用的示例代码
2020/09/23 HTML / CSS
英国在线购买轮胎、预订汽车、汽车维修和装配网站:Protyre
2020/04/12 全球购物
高中毕业生自我鉴定范文
2013/09/26 职场文书
鲜花方阵解说词
2014/02/13 职场文书
报告会主持词
2014/04/02 职场文书
优秀共产党员先进事迹材料
2014/05/06 职场文书
企业员工爱岗敬业演讲稿
2014/08/26 职场文书
2014年妇联工作总结
2014/11/21 职场文书
经典导游欢迎词
2015/01/26 职场文书
2015年公民道德宣传日活动总结
2015/03/23 职场文书
2016年春季开学典礼新闻稿
2015/11/25 职场文书
Golang jwt身份认证
2022/04/20 Golang