【6】网络与网页-2-爬虫--scrapy
一、简介
scrapy爬虫框架是实现爬虫的一个软件结构和功能组件集合
是一个半成品,能够帮助用户实现专业网络爬虫
‘5+2’ 结构看结构: 分布式
看流程: 数据流
数据流的路径:
- Engine从Spider处获得爬取请求(Request)
- Engine将爬取请求转发给Scheduler,用于调度
- Engine从Scheduler处获得下一个要爬取的请求
- Engine将爬取请求通过中间件发送给Downloader
- 爬取网页后,Downloader形成响应(Response) 通过中间件发给Engine
- Engine将收到的响应通过中间件发送给Spider处理
- Spider处理响应后产生爬取项(scraped Item) 和新的爬取请求(Requests)给Engine
- Engine将爬取项发送给Item Pipeline(框架出口)
- Engine将爬取请求发送给Scheduler
Engine控制各模块数据流,不间断从Scheduler处 获得爬取请求,直至请求为空
框架入口:Spider的初始爬取请求
框架出口:Item Pipeline
Engine
* 控制所有模块之间的数据流
* 根据条件触发事件
* 不需要用户修改
Downloader
* 根据请求下载网页
* 不需要用户修改
Scheduler
- 对所有爬取请求进行调度管理
- 不需要用户修改
Downloader Middleware
- 实施Engine、Scheduler和Downloader 之间进行用户可配置的控制
- 修改、丢弃、新增请求或响应
- 用户可以编写配置代码
Spider
- 解析Downloader返回的响应(Response)
- 产生爬取项(scraped item)
- 产生额外的爬取请求(Request)
- 需要用户编写配置代码
Item Pipelines
- 以流水线方式处理Spider产生的爬取项
- 由一组操作顺序组成,类似流水线,每个操作是一个Item Pipeline类型
- 可能操作包括:清理、检验和查重爬取项中的HTML数据、将数据存储到数据库
- 需要用户编写配置代码
Spider Middleware
- 对请求和爬取项的再处理
- 修改、丢弃、新增请求或爬取项
- “5+2”结构 Spider Middleware
安装:
pip install scrapy
查看是否安装成功
scapy -h
二、requests库和库的比较
相同点
- 两者都可以进行页面请求和爬取,Python爬虫的两个重要技术路线
- 两者可用性都好,文档丰富,入门简单
- 两者都没有处理js、提交表单、应对验证码等功能(可扩展)
requests | scrapy |
---|---|
页面级爬虫 | 网站级爬虫 |
功能库 | 框架 |
并发性考虑不足,性能较差 | 并发性好,性能较高 |
重点在于页面下载 | 重点在于爬虫结构 |
定制灵活 | 一般定制灵活,深度定制困难 |
上手十分简单 | 入门稍难 |
非常小的需求,requests库
不太小的需求,Scrapy框架
定制程度很高的需求(不考虑规模),自搭框架,requests > Scrapy
三、scrapy爬虫的常用命令
为什么Scrapy采用命令行创建和运行爬虫?
命令行(不是图形界面)更容易自动化,适合脚本控制 本质上,Scrapy是给程序员用的,功能(而不是界面)更重要
四、scrapy项目的建立
1.建立一个Scrapy爬虫工程
scrapy startproject python123demo
2.在工程中产生一个Scrapy爬虫
cd python123demo/
scrapy genspider demo python123.io
该命令作用:
(1) 生成一个名称为demo的spider
(2) 在spiders目录下增加代码文件demo.py
该命令仅用于生成demo.py,该文件也可以手工生成
# -*- coding: utf-8 -*-
import scrapy
class DemoSpider(scrapy.Spider):
name = "demo"
allowed_domains = ["python123.io"]
start_urls = ['http://python123.io/']
def parse(self, response):
pass
3.配置产生的spider爬虫
(1)初始URL地址 (2)获取页面后的解析方式
# -*- coding: utf-8 -*-
import scrapy
class DemoSpider(scrapy.Spider):
name = "demo"
#allowed_domains = ["python123.io"]
start_urls = ['http://python123.io/ws/demo.html']
def parse(self, response):
fname = response.url.split('/')[-1]
with open(fname, 'wb') as f:
f.write(response.body)
self.log('Saved file %s.' % name)
demo.py的完整写法
# -*- coding: utf-8 -*-
import scrapy
class DemoSpider(scrapy.Spider):
name = "demo"
#allowed_domains = ["python123.io"]
def start_requests(self):
urls = ['http://python123.io/ws/demo.html]
for url in urls:
yield scrapy.Request(url=url,callback=self.parse)
def parse(self, response):
fname = response.url.split('/')[-1]
with open(fname, 'wb') as f:
f.write(response.body)
self.log('Saved file %s.' % name)
4.运行爬虫,获取网页
scrapy crawl demo
五、yield
包含yield语句的函数是一个生成器
生成器每次产生一个值(yield语句),函数被冻结,被唤醒后再产生一个值
生成器是一个不断产生值的函数
def gen(n):
for i in range(n):
yield i**2
for i in gen(5):
print i
生成器每调用一次在yield位置产生一个值,直到函数执行结束
生成器相比一次列出所有内容的优势
- 更节省存储空间
- 响应更迅速
- 使用更灵活
六、scrapy爬虫使用的步骤
- 创建一个工程和Spider模板
- 编写Spider
- 编写Item Pipeline
- 优化配置策略
scrapy爬虫的数据类型
Request类:
class scrapy.http.Request()
Request对象表示一个HTTP请求,由Spider生成,由Downloader执行
属性或方法 说明
.url Request对应的请求URL地址
.method 对应的请求方法,'GET' 'POST'等
.headers 字典类型风格的请求头
.body 请求内容主体,字符串类型
.meta 用户添加的扩展信息,在Scrapy内部模块间传递信息使用 .copy() 复制该请求
Response类:
class scrapy.http.Response()
Response对象表示一个HTTP响应
由Downloader生成,由Spider处理
属性或方法 说明
.url Response对应的URL地址
.status HTTP状态码,默认是200
.headers Response对应的头部信息
.body Response对应的内容信息,字符串类型
.flags 一组标记
.request 产生Response类型对应的Request对象
.copy() 复制该响应
Item类
class scrapy.item.Item()
Item对象表示一个从HTML页面中提取的信息内容
由Spider生成,由Item Pipeline处理
Item类似字典类型,可以按照字典类型操作
Scrapy爬虫支持多种HTML信息提取方法:
- Beautiful Soup
- lxml
- re
- XPath Selector
- CSS Selector
CSS Selector
<HTML>.css('a::attr(href)').extract()
标签名称 标签属性
CSS Selector由W3C组织维护并规范
七、案例分析
1.爬取股票信息
1.建立工程
scrapy startproject BaiduStocks
cd BaiduStocks/
scrapy genspider stocks baidu.com
2.进一步修改spiders中的stocks.py
# -*- coding: utf-8 -*-
import scrapy
import re
class StocksSpider(scrapy.Spider):
name = "stocks"
start_urls = ['http://quote.eastmoney.com/stocklist.html']
def parse(self, response):
for href in response.css('a::attr(href)').extract():
try:
stock = re.findall(r"[s][hz]\d{6}", href)[0]
url = 'https://gupiao.baidu.com/stock/' + stock + '.html'
yield scrapy.Request(url, callback=self.parse_stock)
except:
continue
def parse_stock(self, response):
infoDict = {}
stockInfo = response.css('.stock-bets')
name = stockInfo.css('.bets-name').extract()[0]
keyList = stockInfo.css('dt').extract()
valueList = stockInfo.css('dd').extract()
for i in range(len(keyList)):
key = re.findall(r'>.*</dt>', keyList[i])[0][1:-5]
try:
val = re.findall(r'\d+\.?.*</dd>', valueList[i])[0][0:-5]
except:
val = '--'
infoDict[key]=val
infoDict.update(
{'股票名称': re.findall('\s.*\(',name)[0].split()[0] + \
re.findall('\>.*\<', name)[0][1:-1]})
yield infoDict
3.编写Pipelines
配置pipelines.py的文件
定义对爬取项(scraped item的)的处理类
下面是pipelines.py文件源代码:
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
class BaidustocksPipeline(object):
def process_item(self, item, spider):
return item
class BaidustocksInfoPipeline(object):
def open_spider(self, spider):
self.f = open('BaiduStockInfo.txt', 'w')
def close_spider(self, spider):
self.f.close()
def process_item(self, item, spider):
try:
line = str(dict(item)) + '\n'
self.f.write(line)
except:
pass
return item
配置ITEM_PIPELINES选项
下面是settings.py文件中被修改的区域:
# Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'BaiduStocks.pipelines.BaidustocksInfoPipeline': 300,
}
4.启动程序
scrapy crawl stocks
5.优化速度
settings.py文件
CONCURRENT_REQUESTS Downloader最大并发请求下载数量,默认32
CONCURRENT_ITEMS Item Pipeline最大并发ITEM处理数量,默认100
CONCURRENT_REQUESTS_PER_DOMAIN 每个目标域名最大的并发请求数量,默认8
CONCURRENT_REQUESTS_PER_IP 每个目标IP最大的并发请求数量,默认0,非0有效
八、我的案例
参考资料:
北京理工大学 嵩天老师的课件
个人公众号,比较懒,很少更新,可以在上面提问题,如果回复不及时,可发邮件给我: tiehan@sina.cn