实践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多线程下的变量问题
Apr 28 Python
Python基于回溯法子集树模板解决全排列问题示例
Sep 07 Python
Python简单实现控制电脑的方法
Jan 22 Python
pandas 获取季度,月度,年度首尾日期的方法
Apr 11 Python
Python退火算法在高次方程的应用
Jul 26 Python
bluepy 一款python封装的BLE利器简单介绍
Jun 25 Python
python 模拟创建seafile 目录操作示例
Sep 26 Python
django中间键重定向实例方法
Nov 10 Python
tensorflow 固定部分参数训练,只训练部分参数的实例
Jan 20 Python
解决Jupyter NoteBook输出的图表太小看不清问题
Apr 16 Python
什么是Python中的顺序表
Jun 02 Python
python爬虫分布式获取数据的实例方法
Nov 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
第六节 访问属性和方法 [6]
2006/10/09 PHP
php mysql 判断update之后是否更新了的方法
2012/01/10 PHP
php function用法如何递归及return和echo区别
2014/03/07 PHP
php自动识别文字编码并转换为目标编码的方法
2015/08/08 PHP
php抽象方法和普通方法的区别点总结
2019/10/13 PHP
javascript获取作用在元素上面的样式属性代码
2012/09/20 Javascript
jQuery EasyUI菜单与按钮详解
2016/07/13 Javascript
完美实现八种js焦点轮播图(上篇)
2016/07/18 Javascript
使用json来定义函数,在里面可以定义多个函数的实现方法
2016/10/28 Javascript
JS基于面向对象实现的多个倒计时器功能示例
2017/02/28 Javascript
JS实现加载和读取XML文件的方法详解
2017/04/24 Javascript
nodejs简单访问及操作mysql数据库的方法示例
2018/03/15 NodeJs
es6 symbol的实现方法示例
2019/04/02 Javascript
发布订阅模式在vue中的实际运用实例详解
2019/06/09 Javascript
Layui 导航默认展开和菜单栏选中高亮设置的方法
2019/09/04 Javascript
layui动态渲染生成左侧3级菜单的方法(根据后台返回数据)
2019/09/23 Javascript
原生JavaScript写出Tabs标签页的实例代码
2020/07/20 Javascript
小程序组件传值和引入sass的方法(使用vant Weapp组件库)
2020/11/24 Javascript
nodejs+express最简易的连接数据库的方法
2020/12/23 NodeJs
Python 条件判断的缩写方法
2008/09/06 Python
Python中处理unchecked未捕获异常实例
2015/01/17 Python
python实现类的静态变量用法实例
2015/05/08 Python
Python 过滤错误log并导出的实例
2019/12/26 Python
tensorflow 2.0模式下训练的模型转成 tf1.x 版本的pb模型实例
2020/06/22 Python
基于Python组装jmx并调用JMeter实现压力测试
2020/11/03 Python
美国宠物商店:Wag.com
2016/10/25 全球购物
马来西亚演唱会订票网站:StubHub马来西亚
2018/10/18 全球购物
远程学习的教学用品和家庭学习资源:Really Good Stuff
2020/04/27 全球购物
师范教师毕业鉴定
2014/01/13 职场文书
工作时间上网检讨书
2014/02/03 职场文书
副检察长四风问题对照检查材料思想汇报
2014/10/07 职场文书
MBA推荐信怎么写
2015/03/25 职场文书
员工辞退通知书
2015/04/17 职场文书
幼儿园元旦主持词
2015/07/06 职场文书
低版本Druid连接池+MySQL驱动8.0导致线程阻塞、性能受限
2021/07/01 MySQL
Redis+AOP+自定义注解实现限流
2022/06/28 Redis