Scrapy框架配置与学习

预备知识:

Scrapy框架主要组成部分:

Scrapy EngineScrapy引擎,负责控制整个系统的数据流和事件的触发;

Scheduler:调度器,接收 Scrapy 引擎发来的请求并将其加入队列中,等待引擎后续需要时使用;

Downloader:下载器,爬取网页内容,将爬取到的数据返回给Spiders(爬虫);

Spiders:爬虫,这部分是核心代码,用于解析、提取出需要的数据;

Item Pipeline: 数据管道,处理提取出的数据,主要是数据清洗、验证和数据存储;

Downloader middlewares:下载器中间件,处理 Scrapy 引擎和下载器之间的请求和响应;

Spider middlewares:爬虫中间件,处理爬虫输入的响应以及输出结果或新的请求。

1
2
3
4
5
6
7
8
9
10
Scrapy 中数据流的过程如下:
Scrapy 引擎打开一个网站,找到处理该网站对应的爬虫,并爬取网页的第一个页面;
Scrapy 引擎从爬虫中获取第一个页面地址,并将其作为请求放进调度器中进行调度;
Scrapy 引擎从调度器中获取下一个页面的地址;
调度器返回下一个页面的地址给 Scrapy 引擎,Scrapy 引擎通过下载器中间件传递给下载器进行爬取;
爬取到数据后,下载器通过下载器中间件回传给 Scrapy 引擎;
Scrapy 引擎将爬取到的数据通过爬虫中间件传递给爬虫进行数据解析、提取;
爬虫处理完数据后,将提取出的数据和新的请求回传给 Scrapy 引擎;
Scrapy 将提取出的数据传给数据管道进行数据清洗等操作,同时将新的请求传递给调度器准备进行下一页的爬取;
重复 2-8 步,直到调度器中没有新的请求,数据爬取结束。

scrapy框架命令:

1
2
3
4
#创建工程
scrapy startproject [工程名称]
cd [工程名称]
scrapy genspider [爬虫名称] [网站地址]

配置spider:

scrapy框架中的Request类:

Response类:

输入scrapy

输入explorer .

在所打开的文件夹内即可创建文件进行编码(记得将txt文本改成py后缀)

然后再指令中输入:

1
2
3
4
scrapy runspider spider.py -t csv -o apps.csv
# spider.py 是刚刚写的爬虫代码的文件名
# -t 表示输出的文件格式,我们用 csv,方便用 Excel 等工具打开
# -o 表示输出的文件名,所以执行完会出现一个 apps.csv 的文件

实战:

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
import scrapy

# 定义一个类叫做 TitleSpider 继承自 scrapy.Spider
class TitleSpider(scrapy.Spider):
name = 'title-spider'
# 设定开始爬取的页面
start_urls = ['https://www.appinn.com/category/windows/']
def parse(self, response):
# 找到所有 article 标签
for article in response.css('article'):
# 解析 article 下面 a 标签里的链接和标题
a = article.css('h2.title a')
if a:
result = {
'title': a.attrib['title'],
'url': a.attrib['href'],
}
# 得到结果
yield result

# 解析下一页的链接
next_page = response.css('a.next::attr(href)').get()
if next_page is not None:
# 开始爬下一页,使用 parse 方法解析
yield response.follow(next_page, self.parse)

CSS选择器

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
css选择器
按标签名选择: response.css('标签名')
例如写 response.css('ul') 就可以选择出所有 ul 元素,response.css('div') 可以选择出 div 元素。

class选择:response.css('.class名')
当我们想要选择 class 包含 containerdiv 元素,我们可以写 response.css('div.container')。

id选择: response.css('#id名')
response.css('a#pull')表示我们想选择一个 idpulla 元素

按层级关系选择:用一个空格隔开表示后面为前面所属
空格后面表示的是所有的子元素,不管是处于多少层的子元素。
而如果只想要第一层的子元素则应该用 > 分隔开。这里的 a 元素就是第一层的子元素
所以 h2.title.post-title ah2.title.post-title > a 这两种写法的效果是一样的

取元素中的文本: 使用::text
例如response.css('a::text')表示得到所有a标签下的文本,但得到的并不是纯文本形式
如果需要得到纯文本:
# 取符合条件的第一条数据
response.css('h2.title.post-title a::text').get()
# 取符合条件的所有数据
response.css('h2.title.post-title a::text').getall()

取元素的属性:需要调用attrib方法
如果我们想得到这个 a 元素的 href 属性,需要调用这个元素的 attrib 属性
# 拿到符合选择器条件的第一个 a 标签
a = response.css('h2.title.post-title a')
a.attrib['href']
所有符合 h2.title.post-title a 这个条件的标签的 href 属性,就像下面这样:
for a in response.css('h2.title.post-title a'):
print(a.attrib['href'])
或者另一种写法也可以取到 href 属性,就是加上 ::attr(href):
for href in response.css('h2.title.post-title a::attr(href)').getall():
print(href)

Scrapy互动工具:

scrapy shell “https://www.appinn.com/category/windows/

下面是Scrapy的整个项目配置流程:

启动一个Scrapy项目

首先输入 explorer . 进入指定文件夹

然后输入 scrapy startproject appinn 让Scarpy创建一个名字叫做appinn的项目

然后输入 cd appinn进入指定文件夹

  • items.py
1
2
3
4
5
6
7
8
9
10
11
items.py
items.py记录了我们想要的数据格式的文件,打开items.py
import scrapy
class AppinnItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 在下面输入需要爬取的数据名称,例如
name = scrapy.Field
link = scrapy.Field
pass
#保存并返回

spiders 目录里保存了我们创建的爬虫。

在命令行里执行下面的命令并回车,它会帮我们创建一个叫做 article 的爬虫,

并且只爬取 www.appinn.com 下的网页:

scrapy genspider article www.appinn.com

在spider中打开创建的article.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
import scrapy
from appinn.items import Article

class ArticleSpider(scrapy.Spider):
name = 'article'
allowed_domains = ['www.appinn.com']
start_urls = ['https://www.appinn.com/category/windows/']

def parse_article(self, response):
article = Article()
# 从 response 里提取出标题、时间、作者、分数和内容
# 并将它们都存到 artile 这个 item 里
article['title'] = response.css('').get()
article['time'] = response.css('').get()
article['author'] = response.css('').get()
article['score'] = response.css('').get()
contents = response.css('').getall()
article['content'] = '\n'.join(contents)
# 给出结果
yield article

def parse(self, response):
# 找到所有文章的链接,通知 Scrapy 用 parse_article 方法解析
for article_url in response.css('').getall():
if not article_url:
continue
# 后续请求和解析
yield response.follow(article_url, self.parse_article)

# 找到下一页的链接,通知 Scrapy 用 parse 方法解析
next_page = response.css('').get()
if next_page:
# 后续请求和解析
yield response.follow(next_page, self.parse)
pass

name 是项目中每个爬虫唯一的名字,用来区分不同的爬虫。

allowed_domains 是允许爬取的域名,如果请求的链接不在这个域名下,那么这些请求将会被过滤掉。

start_urls 是初始请求地址的列表,也就是一开始就爬取的页面地址列表。

parse() 方法是默认的解析方法,负责解析返回的响应、提取数据或者生成下一步要处理的请求

首先我们把 start_urls 改成我们需要的开始链接

接着引入我们刚刚写好的 Item

下面是加一个解析文章的方法,用来解析文章详情页,把标题、时间、作者、分数、正文取出来

1
2
3
4
5
6
7
8
9
10
11
12
13
def parse_article(self, response):
article = Article()
# 从 response 里提取出标题、时间、作者、分数和内容
# 并将它们都存到 artile 这个 item 里
article['title'] = response.css('h1.title::text').get()
article['time'] = response.css('span.thetime span::text').get()
article['author'] = response.css('span.theauthor span a::text').get()
article['score'] = response.css('em strong::text').get()
contents = response.css('div.post-single-content *::text').getall()
article['content'] = '\n'.join(contents)
# 给出结果
yield article
response.follow() 方法用于生成下一个请求和数据解析

完整版的 article.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
import scrapy
from appinn.items import Article

class ArticleSpider(scrapy.Spider):
name = 'article'
allowed_domains = ['www.appinn.com']
start_urls = ['https://www.appinn.com/category/windows/']

def parse(self, response):
# 找到所有文章的链接,通知 Scrapy 用 parse_article 方法解析
for article_url in response.css('article h2.title a::attr(href)').getall():
if not article_url:
continue
# 后续请求和解析
yield response.follow(article_url, self.parse_article)

# 找到下一页的链接,通知 Scrapy 用 parse 方法解析
next_page = response.css('a.next::attr(href)').get()
if next_page:
# 后续请求和解析
yield response.follow(next_page, self.parse)

def parse_article(self, response):
article = Article()
# 从 response 里提取出标题、时间、作者、分数和内容
# 并将它们都存到 artile 这个 item 里
article['title'] = response.css('h1.title::text').get()
article['time'] = response.css('span.thetime span::text').get()
article['author'] = response.css('span.theauthor span a::text').get()
article['score'] = response.css('em strong::text').get()
contents = response.css('div.post-single-content *::text').getall()
article['content'] = '\n'.join(contents)
# 给出结果
yield article
  • pipelines.py
  • pipelines.py 文件允许我们在得到结果之后,对结果进行一些处理。即筛选数据

初始化:

1
2
3
4
5
6
7
8
9
10
class AppinnPipeline(object):
def process_item(self, item, spider):
if item.get('想要筛的数据名称'):
if item['score']:
#循环内容,下面的语句意思为去掉我们不想要的item
raise DropItem('')
return item

process_item() 方法就是得到结果之后处理 item 的方法
raise DropItem() 可以去掉我们不想要的 item
  • settings.py
  • settings.py 是整个项目的配置文件这个文件里可以设置爬取并发个数、等待时间、输出格式、默认 header 等等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BOT_NAME = 'appinn'
SPIDER_MODULES = ['appinn.spiders']
NEWSPIDER_MODULE = 'appinn.spiders'

# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'appinn (+http://www.yourdomain.com)'
# Obey robots.txt rules
ROBOTSTXT_OBEY = True

# 上面都是自动生成的,下面开始是我们自己定义的
# 要使用的
pipelineITEM_PIPELINES = { 'appinn.pipelines.AppinnPipeline': 300,
# 300 表示顺序,pipeline 有多个时,数字越小越先执行
}
FEED_FORMAT = 'csv'
# 最后输出的文件格式FEED_URI = 'appin_windows_apps.csv'
# 最后输出的文件名
# 为了避免对被爬网站造成太大的压力,我们启动自动限速,设置最大并发数为 5
AUTOTHROTTLE_ENABLED = TrueAUTOTHROTTLE_TARGET_CONCURRENCY = 5