scrapy爬虫实践详解

爬虫框架里最出色的的的确确还是Scrapy。

Scrapy是Python开发的一个快速,高层次的屏幕抓取和Web抓取框架,用于抓取Web站点并从页面中提取结构化的数据。

下图展示了Scrapy的大致架构,其中包含了主要组件和系统的数据处理流程(绿色箭头表示)。下面会对组件和流程进行了一个简单的解释。

框架图

##来看看图中的组件

1.Scrapy Engine(Scrapy引擎)

Scrapy引擎是用来控制整个系统的数据处理流程,并进行事务处理的触发。更多的详细内容可以看下面的数据处理流程。

2.Scheduler(调度程序)

调度程序从Scrapy引擎接受请求并排序列入队列,并在Scrapy引擎发出请求后返还给它们。

3.Downloader(下载器)

下载器的主要职责是抓取网页并将网页内容返还给蜘蛛(Spiders)。

4.Spiders(蜘蛛)

蜘蛛是有Scrapy用户自己定义用来解析网页并抓取制定URL返回的内容的类,每个蜘蛛都能处理一个域名或一组域名。换句话说就是用来定义特定网站的抓取和解析规则。

5.Item Pipeline(项目管道)

项目管道的主要责任是负责处理有蜘蛛从网页中抽取的项目,它的主要任务是清晰、验证和存储数据。当页面被蜘蛛解析后,将被发送到项目管道,并经过几个特定的次序处理数据。每个项目管道的组件都是有一个简单的方法组成的Python类。它们获取了项目并执行它们的方法,同时还需要确定的是是否需要在项目管道中继续执行下一步或是直接丢弃掉不处理。

项目管道通常执行的过程有:

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

6.Middlewares(中间件)

中间件是介于Scrapy引擎和其他组件之间的一个钩子框架,主要是为了提供一个自定义的代码来拓展Scrapy的功能。

##数据的处理流程

Scrapy的整个数据处理流程有Scrapy引擎进行控制,其主要的运行方式为:

  1. 引擎打开一个域名,时蜘蛛处理这个域名,并让蜘蛛获取第一个爬取的URL。
  2. 引擎从蜘蛛那获取第一个需要爬取的URL,然后作为请求在调度中进行调度。
  3. 引擎从调度那获取接下来进行爬取的页面。
  4. 调度将下一个爬取的URL返回给引擎,引擎将它们通过下载中间件发送到下载器。
  5. 当网页被下载器下载完成以后,响应内容通过下载中间件被发送到引擎。
  6. 引擎收到下载器的响应并将它通过蜘蛛中间件发送到蜘蛛进行处理。
  7. 蜘蛛处理响应并返回爬取到的项目,然后给引擎发送新的请求。
  8. 引擎将抓取到的项目项目管道,并向调度发送请求。
  9. 系统重复第二步后面的操作,直到调度中没有请求,然后断开引擎与域之间的联系。

##新建工程

scrapy startproject doubanmovie  

目录就不详细再说了。

##定义项目(Item)

1
2
3
4
5
6
7
8
9
10
11
# _*_ coding=utf-8 _*_

from scrapy.item import Item, Field

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

这个地方只是需要相应添加需要的字段。

##编写爬虫 (spiders)

Spider是整个项目中最核心的类,在这个类中我们需要定义抓取对象(url 域名)以及抓取规则。 scrapy官方文档中的教程是基于BaseSpider的,但是这个只能爬取给定的url,无法根据一个初始的url向外扩展。
还有scrapy.contrib.spiders.CrawlSpider可以使用。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

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

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

def parse_item(self,response):
sel=Selector(response)
item=DoubanmovieItem()
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[1]/div[2]/strong/text()').extract()
item['director']=sel.xpath('//*[@id="info"]/span[1]/span[2]/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
```

首先是`name` 这个是定义spider名字的字符串,必须的。
接着是`allowed_domains` 包含了spider允许爬取的域名(domain)列表(list),可选。
`start_urls` URL列表。当没有制定特定的URL时,spider将从该列表中开始进行爬取。 因此,第一个被获取到的页面的URL将是该列表之一。 后续的URL将会从获取到的数据中提取。
`rules` 一个包含一个(或多个) Rule 对象的集合(list)。 每个 Rule 对爬取网站的动作定义了特定表现。 Rule对象在下边会介绍。 如果多个rule匹配了相同的链接,则根据他们在本属性中被定义的顺序,第一个会被使用。

然后就是使用xpath从网页从抽取出来item,这里是较为重要的部分,一定要搞清楚抓取的item是什么类型的,要做哪些处理放入字典当中去。
这个需要不断尝试,耐心调试下
![shell 调试](http://o7b8j5zne.bkt.clouddn.com/shell%E8%B0%83%E8%AF%95.png)

scrapy shell (domains)


Scrapy 使用一种叫做 XPath selectors的机制,它基于 XPath表达式。如果你想了解更多selectors和其他机制可以查阅资料[官网](http://doc.scrapy.org/topics/selectors.html#topics-selectors )
这是一些XPath表达式的例子和他们的含义


为了方便使用XPaths,Scrapy提供XPathSelector 类, 有两种口味可以选择, HtmlXPathSelector (HTML数据解析) 和XmlXPathSelector (XML数据解析)。 为了使用他们你必须通过一个 Response 对象对他们进行实例化操作。你会发现Selector对象展示了文档的节点结构。因此,第一个实例化的selector必与根节点或者是整个目录有关 。
Selectors 有三种方法

path():返回selectors列表, 每一个select表示一个xpath参数表达式选择的节点.
extract():返回一个unicode字符串,该字符串为XPath选择器返回的数据
re(): 返回unicode字符串列表,字符串作为参数由正则表达式提取出来


##管道文件 (pipeline)

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

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

#Connect to the MongoDB database
client = pymongo.MongoClient()
db = client['pythontest']
item_info = db['mydouban']

class MongoDBPipeline(object):


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_movie = {
"name":item['name'][0],
"year":item['year'][0],
"score":item['score'][0],
"director":item['director'],
"classification":item['classification'],
"actor":item['actor']
}
item_info.insert_one(new_movie)
log.msg("Item wrote to MongoDB database %s/%s" %
('pythontest', 'mydouban'),
level=log.DEBUG, spider=spider)
return item

```

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

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

这里考虑数据的复杂性,使用mongodb数据库来存储。

##配置文件(settings)

```python
BOT_NAME = 'doubanmovie'
SPIDER_MODULES = ['doubanmovie.spiders']
NEWSPIDER_MODULE = 'doubanmovie.spiders'
ITEM_PIPELINES={
'doubanmovie.pipelines.MongoDBPipeline':300,

}
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

在settings.py里设置一些信息。