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

作者:王宇昊 时间:2021-04-26 21:27:11 

安装部署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,Scrapy
0
投稿

猜你喜欢

  • python学习笔记之列表(list)与元组(tuple)详解

    2022-04-13 00:15:13
  • pyqt5 实现 下拉菜单 + 打开文件的示例代码

    2023-12-17 09:20:28
  • Django中对数据查询结果进行排序的方法

    2021-01-31 23:40:18
  • pycharm new project变成灰色的解决方法

    2023-10-29 20:29:23
  • 解决使用pip安装报错:Microsoft Visual C++ 14.0 is required.

    2022-05-02 14:42:15
  • Postman使用详解

    2023-09-03 05:59:36
  • Vue+express+Socket实现聊天功能

    2024-06-05 09:19:03
  • SQL Server UPDATE语句的用法详解

    2024-01-17 06:39:23
  • Pytorch损失函数torch.nn.NLLLoss()的使用

    2021-02-07 16:08:57
  • 使用php get_headers 判断URL是否有效的解决办法

    2023-11-24 00:02:50
  • JavaScript实现判断图片是否加载完成的3种方法整理

    2024-04-23 09:24:26
  • Python对XML文件实现增删改查操作

    2023-11-19 20:42:03
  • Python GUI布局尺寸适配方法

    2022-03-07 05:52:26
  • 在Node.js下运用MQTT协议实现即时通讯及离线推送的方法

    2024-05-03 15:54:50
  • python检测空间储存剩余大小和指定文件夹内存占用的实例

    2022-10-30 06:52:51
  • python批量识别图片指定区域文字内容

    2021-04-26 13:59:54
  • 详细分析vue表单数据的绑定

    2023-07-02 16:27:51
  • python实现决策树

    2021-11-07 19:14:05
  • 扫盲大讲堂:SQL查询结果集对注入的影响及利用

    2009-09-05 09:49:00
  • python paramiko模块学习分享

    2021-11-10 16:57:00
  • asp之家 网络编程 m.aspxhome.com