文章目录
如何安装创建启动项目等基础操作我在之前的一篇博客中已经完整的介绍过了,如果你还不会上述操作可以查看我上篇博客爬虫框架(Scrapy)简介(https://xunmi.blog.csdn.net/article/details/105408249)
Scrapy的爬虫流程
从上图可以看出,使用Scrapy的一个大致流程,
- 指定第一个URL放入调度器
- 下载器会从调取器中读取URL并自动访问下载,传入爬虫
- 爬虫将提取的URL在此传入调度器,提取的数据传入管道进行保存
- 重复上述操作,直到完成调度器中所有的URL
本片博客主要介绍Spider部分和Irem部分
spiders(爬虫包)
创建项目
我们创建项目后就能在项目文件夹中看见一个自动创建的spiders文件夹,而这个文件夹中默认有一个__init__.py
文件,也就是此文件夹被定义为一个包,这个包就是用来存放我们的爬虫文件的。
我们在命令行中执行scrapy genspider 爬虫名 "运行爬取的域名"
命令即可生成一个爬虫文件
此命令会根据爬取的域名自动生成一个允许的域(allowed_domains)和开始爬取的URL(start_urls)。
下面我使用阳光问政平台作为我们的测试项目进行爬取
阳光问政(wz.sun0769.com)
创建爬虫的时候http或https协议建议不带(因为wz.sun0769.com的范围比http://wz.sun0769.com更大,不过这个带不带在大部分网站中都没有影响)
scrapy genspider 阳光问政 "wz.sun0769.com"
创建项目后,自动生成的开始URL可能并不是我们真正开始爬取的URL,这里我们可以手动修改一下,比如说我这里想爬取阳光问政的最新问政页面(http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1)
class A阳光问政Spider(scrapy.Spider):
name = '阳光问政'
allowed_domains = ['wz.sun0769.com']
start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1']
response方法
创建的爬虫中带一个def parse(self, response):
方法,从上图中我们可以看出,调度器会自动将我们首个url(start_urls)传入下载器,并下载其内容,而首个URL中的下载内容这会传入parse方法中的response属性中,也就是我们调用此属性即可实现首个页面的内容读取。
如果想看response的源码可以在from scrapy.http.response import Response
中查看
response方法 | 作用 |
---|---|
.xpath() |
筛选规则,不会用xpath的点击这里, 非常常用 |
.css() |
筛选规则 |
.text |
直接获取相应(response)文本 |
.copy() |
输出此相应(response)的副本 |
.replace() |
根据给定值创建一个新的相应(response),给定的值可以为’url’, ‘status’, ‘headers’, ‘body’, ‘request’, ‘flags’, ‘certificate’ |
.urljoin(url) |
将此相应的url与给定的url连接起来。 |
.follow() |
像调度器中存放URL,在此需要请求的网站,相当于scrapy.Request() 。非常重要,下面会着重介绍scrapy.Request() 的作用 |
.follow_all() |
像调度器中放入多个URL,源码层面上就是多次调用.follow() |
我们在parse方法中对首个URL的相应(response)可以直接使用xpath提取数据
def parse(self, response):
tr_data = response.xpath('//div[@class="width-12"]/ul[@class="title-state-ul"]/li/span[3]/a')
for tr in tr_data:
data['标题'] = tr.xpath('./text()').extract_first()
data['详情URL'] = tr.xpath('./@href').extract_first()
data['详情URL'] = "http://wz.sun0769.com" + data['详情URL']
print(data)
当前一个简单的页面爬取就已经完成了。
response.follow()与scrapy.Request()方法
.follow()
是scrapy在较新的版本中推出的方法,相比于scrapy.Request()
,我跟推荐大家使用response.follow()
对于初学者来说这两个方法效果是相同的,他们接收的属性,和执行的效果都完全一样,不太相同的是response.follow()
方法接受的URL可以是相对URL或“scrapy.link.link”对象。
上述的爬取中,我还获取了详情URL的地址,那么我如何将详情URL传入调度器,这里就要用到.follow()
了。我们先看一下.follow()中常用的有哪些属性。
.follow() 常用属性 |
作用 |
---|---|
url |
必填,像调度器中传入的新URL |
callback=None |
满足此条件的url回调的函数(默认: 空) |
method=GET |
请求时使用的方法(默认:GET) |
headers=None |
请求头 |
cookies=None |
请求携带的cookies |
meta=None |
此属性一般用来传递变量(在一个方法中提取的数据传递进入另一个方法使用,必须已字典的形式传入) |
encoding='utf-8' |
请求时的字符编码 |
需要注意的是,在scrapy中我们结束一个方法用的大多数都是yield
让其变成一个生成器,而不是return
直接结束这个方法。
下面我们就来爬取一下 阳光问政 这个网站的所有最新问政
# -*- coding: utf-8 -*-
import scrapy
from scrapy.http.response import Response
class A阳光问政Spider(scrapy.Spider):
name = '阳光问政'
allowed_domains = ['wz.sun0769.com']
start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1']
def parse(self, response):
tr_data = response.xpath('//div[@class="width-12"]/ul[@class="title-state-ul"]/li/span[3]/a')
for tr in tr_data:
data = {}
data['标题'] = tr.xpath('./text()').extract_first()
data['详情URL'] = tr.xpath('./@href').extract_first()
data['详情URL'] = "http://wz.sun0769.com" + data['详情URL']
yield response.follow(
url=data['详情URL'],
callback=self.details_parse,
meta={'data': data}
)
next_url ="http://wz.sun0769.com" + response.xpath('//div[@class="width-12"]/div[3]/a[2]/@href').extract_first()
yield response.follow(
url=next_url,
callback=self.parse
)
def details_parse(self, response):
data = response.meta.get('data')
data['内容'] = response.xpath('//div[@class="details-box"]/pre/text()').extract()
img = response.xpath('//div[@class="mr-three"]/div[3]/img/@src').extract()
if img:
data['配图'] = img
print(data)
items.py(项目)
除了爬虫外items.py是用来规范提取的数据的一个文件,他用于指定提取数据的键名。
打开items.py后我们会发现里面有个一个继承与scrapy.Item
的类,其中包含键名 = scrapy.Field()
这样一个属性,这个数据就是用于定义键名的。
首先我们可以创建一些键名.然后在爬虫中可以进行引用并赋值给我们创建用于存储网站数据的变量。这时候我们的变量必须使用items.py中定义过的键名,否则会出现报错。
items.py的存在使项目变得规范,而非强制使用。
pipelines(管道)
管道用于进行数据处理,我们在爬虫(spiders)中已经将需要的数据提取出来了,但提取出来的数据如何存储,存储为什么格式,存储在哪个地方,这都是在管道中进行的。使用管道前,我们需要先在设置(setting.py)中启动管道。
启动管道
打开设置(setting.py)后我们需要先将一下代码的注释删除
ITEM_PIPELINES = {
'scrapy_text.pipelines.ScrapyTextPipeline': 300,
}
其中ScrapyTextPipeline
是管道(pipelines.py)中的的类名,这里可以同时存在多个管道,而后面的数字着表示为优先级,数字越小,优先级越高,数据会优先传入优先级高的管道
使用管道
配置好后,我们为了验证是否能将数据传入管道,我们可以直接将提取的数据进行输出(print()),
打开管道设置后,我们只需在爬虫中将数据使用yield方法传入即可,管道的item变量会自动接收爬虫传出的数据。
保存数据这里就不细讲了,之后我会专门写一篇如何将scrapy爬取的数据保存进入数据库的博客。
常用设置(settings.py)
我想将我汉化注释后的设置放出来,其中原设置没有的属性只有LOG_LEVEL = 'WARNING'
,这个设置是用来设置输出的log的等级的,如果不这么设置,我们输出的时候会携带一大堆当前爬虫的配置信息,这里大多数信息对我们来说都是没有太大用处的,所以我这里设置了只有出现警告(WARNING)及以上的信息才会输出。
# -*- coding: utf-8 -*-
# Scrapy 的 scrapy_text 项目的设置
#
# 为简单起见,此文件仅包含被认为重要或常用的设置。您可以在参考文档中找到更多设置:
#
# https://docs.scrapy.org/en/latest/topics/settings.html
# https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html
BOT_NAME = 'scrapy_text'
SPIDER_MODULES = ['scrapy_text.spiders']
NEWSPIDER_MODULE = 'scrapy_text.spiders'
# 命令行出入的log等级,(默认为全部输出)
LOG_LEVEL = 'WARNING'
# 通过在用户代理上标识您自己(和您的网站)负责任地爬行
#USER_AGENT = 'scrapy_text (+http://www.yourdomain.com)'
# 当前是否遵循爬虫协议
ROBOTSTXT_OBEY = False
# 配置Scrapy执行的最大并发请求数(默认值:16)
#CONCURRENT_REQUESTS = 32
# 为同一网站的请求配置延迟(默认值:0)
# 见 https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# 参见 autothrottle 设置和文档
#DOWNLOAD_DELAY = 3
# 下载延迟设置将仅支持以下选项之一:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16
# 禁用Cookie(默认情况下已启用)
#COOKIES_ENABLED = False
# 禁用Telnet控制台(默认启用)
#TELNETCONSOLE_ENABLED = False
# 重写默认请求头:
#DEFAULT_REQUEST_HEADERS = {
# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# 'Accept-Language': 'en',
#}
# 启用或禁用爬虫中间件(middleware)
# 见 https://docs.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
# 'scrapy_text.middlewares.ScrapyTextSpiderMiddleware': 543,
#}
# 启用或禁用下载器中间件(middleware)
# 见 https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
# 'scrapy_text.middlewares.ScrapyTextDownloaderMiddleware': 543,
#}
# 启用或禁用扩展
# 见 https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
# 'scrapy.extensions.telnet.TelnetConsole': None,
#}
# 配置项管道(pipelines)
# 见 https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'scrapy_text.pipelines.ScrapyTextPipeline': 300,
}
# 启用和配置自动限速(AutoThrottle)扩展(默认禁用) 此选项配置根据网站负载进行自动限速
# 看 https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# 初始下载延迟
#AUTOTHROTTLE_START_DELAY = 5
# 在高延迟情况下设置的最大下载延迟
#AUTOTHROTTLE_MAX_DELAY = 60
# Scrapy应该并行发送的请求的平均数量
# 每个远程服务器
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# 启用显示节流的每一个响应收到的统计:
#AUTOTHROTTLE_DEBUG = False
# 启用和配置HTTP缓存(默认禁用)
# 看 https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
调用自定义设置
上述的设置都是scrapy自带的,除此之外设置的主要作用是存放一些公共的变量(比如数据库的地址,账号密码等),方便自己和别人修改。需要注意的是,设置中的变量名都是大写的(并非强制,但是一种规范)。
假设我们当前在设置中存储了数据库的地址,我们如何去调用它呢。
大部分人最先想到的方法可能都是引用该文件
from scrapy_text.settings import SQL_HOST
,然后调用即可
上述方法虽然没错,达成了我们想要的目的,但其实scrapy给我们提供了更加便利的方法已供我们使用。在爬虫中self.settings.get('SQL_HOST')
就能提取出我们设置中的内容,无需引入设置。self.settings
会已字典的形式将设置文件读取出来。
在管道中,我们需要读取配置文件就需要用到管道中携带的spider
属性,而非self了,
spider.settings.get('SQL_HOST')