scrapy 是为了爬取数据,提取结构性数据的应用框架。
安装
> pip install scrapy
# 如果报超时错误,则切换源
> pip install scrapy -i https://pypi.douban.com/simple
创建项目与项目简介
在命令行中进入项目存储目录下,执行以下命令:
> scrapy startproject <项目名称>
创建之后,进入项目目录,可看到项目目录:
╠═╗ 项目名
╠═╗ 项目名
╠═╗ spiders --爬虫核心功能的存储文件夹
╠═ __init__.py
╠═ __init__.py
╠═ items.py --定义数据结构的地方,是一个继承自scrapy.Item的类
╠═ middlewares.py --中间件
╠═ pipelines.py --管道文件,用于处理下载数据的后续处理
╠═ settings.py --配置文件
╠═ scrapy.cfg
创建爬虫与爬虫文件介绍
在项目目录下,在命令行中执行以下命令:
> scrapy genspider <爬虫名> <网址(不包含http)>
执行命令后,在项目目录/项目同名目录/spiders
目录下,会发现项目创建了一个文件。打开文件,文件初始化内容如下:
import scrapy
class <爬虫名>Spider(scrapy.Spider):
name = '<爬虫名>' # 爬虫的名字
allowed_domains = ['<网址>'] # 限定允许访问的域名
start_urls = ['http://<网址>/'] # 起始的url地址,第一次要访问的url
def parse(self, response):
'''执行起始url地址后执行的代码
args:
response: 执行起始url后,得到的结果(对象,与requests执行后的结果一样)
'''
pass
如果在命令行中输入的命令中,爬虫名是中文,那么项目会在创建的文件名前添加a
字符,在类名前加A
字符,name
属性名不变。
运行爬虫
在项目目录下,在命令行中执行以下命令:
> scrapy crawl <爬虫名>
如果为爬虫名是中文(上节说项目自动修改了文件名和类名),依旧使用中文,不需要添加 a
和 A
字符。
工作原理
- 引擎向spiders获取url;
- 引擎将获取的url发送给调度器;
- 调度器将url生成一个请求对象返回给引擎,存放到引擎的请求队列中;
- 引擎将请求队列中的请求对象分配给下载器;
- 下载器从互联网上下载数据并返回给引擎;
- 引擎将数据返还给spiders,spiders通过数据解析将解析结果发送给引擎;
- 引擎会判断解析结果是url还是数据,如果是url将会从第2步重复,如果是数据将会发送给管道,管道负责将数据保存到文件或数据库等存储单元中。
╔═══════════════╗
║ 调度器 ║ ╔═══════════════╗
╚═══════════════╝ ║ 互联网 ║
↑ ↑ ╎ ╚═══════════════╝
url url 请求 ↑ ╎
╎ ╎ ↓ 从互联网上下载数据
╔═══════════════╗ ╎ ↓
╔══════════╗ ║ ║ --请 求-->╔═══════════════╗
║ 管 道 ║<--数 据--║ 引擎 ║ ║ 下载器 ║
╚══════════╝ ║ ║ <--数 据--╚═══════════════╝
(存储到文件/数据库中) ╚═══════════════╝
↑ ↑ ╎
解析结果 url 数据
╎ ╎ ↓
╔═══════════════════╗
║ spiders ║
╚═══════════════════╝
(通过xpath解析数据)
response中可用的属性与方法
response.text 获取html代码/接口数据 (字符串)
response.body 二进制数据
response.xpath() 列表,元素为 seletor对象,参数为xpath语法
seletor对象.extract() 获取seletor对象的data属性值,数据类型为字符串
元素为seletor对象的列表.extract_first()
提取列表(元素为seletor对象)中的第一个数据,不存在则返回None
seletor对象.xpath() seletor对象也可以调用xpath
response.meta 字典,上一步中传递的数据
需要获取多个url页面数据
在爬虫文件中,可以使用 scrapy.Request(url='', callback=self.parse, meta={'<key>': 'value'})
主动调用网页爬虫,其中,参数url
是要爬取的网页,callback
是获取数据后处理的函数。
如果多个url
的数据处理一样,如分页查询。可以直接调用自身的处理函数。如果是其他url
,如子页面数据获取,可以模仿 parse()
方法自定义一个处理函数。
若有多个数据处理函数,在 Request()
函数中使用 meta
参数传递字典,将这一步骤得到的数据传递到下一步骤。需要的数据全部获取完成时,统一加入数据存储类中。
函数数据存储文件 — items.py
在数据存储文件中需要定义我们需要的数据:
import scrapy
class <爬虫名>Item(scrapy.Item):
<数据变量名> = scrapy.Field() # 以定义类属性的形式定义数据变量名
定义之后如何使用呢?
在 spiders 目录的爬虫文件中,先导入数据存储类,然后实例化数据存储类时将数据当作参数传入:
# 导入
from <项目名>.items import <数据存储文件的类名>
# 使用,在parse() 爬虫信息处理函数中
<自定义变量> = <数据存储文件的类名>(<数据变量名>='<数据值>')
# 例:
# items.py
class ScrapyStudyItem(scrapy.Item):
name = scrapy.Field()
# 爬虫文件.py
def parse(self, response):
# 省去了数据处理步骤
it = ScrapyStudyItem(name="aaa")
print(it) # 结果为: {'name': 'aaa'}
管道文件 — pipelines.py
在爬虫文件中,处理完数据并使用数据存储对象将数据实例化后,使用 yield 生成器,可自动传递到管道文件中。
在管道文件中,方法 process_item(self, item, spider)
随时监听程序,生成器传递一条,此方法就会执行一次。其中 item
就是生成器传递的数据。
# 爬虫文件.py
def parse(self, response):
# 省去了数据处理步骤
it = ScrapyStudyItem(name="aaa")
yield it # 通过生成器传递数据
# pipelines.py
def process_item(self, item, spider):
print(item) # 结果为: {'name': 'aaa'}
# 通常,在这里都是写将数据保存到文件,保存到数据库的代码,这里为了明了直接打印了数据
return item
管道文件的类定义中,还可以定义两个方法 open_spider(self, spider)
和close_spider(self, spider)
,分别表示爬虫文件执行的前后所对应的前置和后置执行函数。通常用于文件的开启关闭,数据库连接等初始化和结束操作。
如果需要添加新的管道,则模仿着自定义一个管道类。定义完成后,在配置文件中将这个管道类添加到 ITEM_PIPELINES
配置中。
post请求
post请求一般是需要传递参数的,但是爬虫文件中的 parse() 方法是自动执行的,无法传递参数。这个方法无效了。需要创建一个名字叫做 start_requests(self)
的方法来获取post方式请求的url的数据。
start_requests(self)
方法在项目执行时也是自动执行,但是不同的是,这个方法没有指定要获取的 url,需要在方法内自定义。下面例子可以表示使用方法:
class BaiduSpider(scrapy.Spider):
name = 'baidu'
allowed_domains = ['fanyi.baidu.com']
# start_urls = ['http://fanyi.baidu.com/']
# def parse(self, response):
# pass
def start_requests(self):
url = "http://fanyi.baidu.com/sug"
data = {"kw": 'blue'}
# url是要访问的url,formdata是参数,callback是数据处理函数
yield scrapy.FromRequest(url=url, formdata=data, callback=self.parse_second)
def parse_second(self, response):
# 数据处理
print(response.text)
配置文件 — settings.py
ROBOTSTXT_OBEY = True -- 是否遵循网站的robots协议,默认为True。
ITEM_PIPELINES = { -- 将 # 符号去除,表示开启管道。此字典中包含定义的所有管道
'<项目名>.pipelines.<项目名>Pipeline': 300, -- 定义的一条管道,值为优先级
}
LOG_LEVEL = "" -- 日志打印的等级,默认为DEBUG,可选项:
CRITICAL, ERROR, WARNING, INFO, DEBUG
LOG_FILE = "xxx.log" -- 日志保存到文件中,而不打印