Scrapy 基础
Scrapy
异步处理框架,可配置和可扩展程度非1 常高,Python中使用最广泛的爬虫框架
安装:
Ubuntu安装
1、安装依赖包
- sudo apt-get install libffi-dev libssl- dev libxml2-dev python3-dev libxslt1-dev zlib1g-dev
- sudo pip3 install -I -U service_identity
2、安装scrapy框架
- sudo pip3 install Scrapy
windows安装
python -m pip install Scrapy
一、概述
1. 五大组件
- 引擎(Engine) :整个框架核心
- 调度器(Scheduler) :维护请求队列
- 下载器(Downloader):获取响应对象
- 爬虫文件(Spider) :数据解析提取
- 项目管道(Pipeline):数据入库处理
下载器中间件(Downloader Middlewares) : 引擎 ->下载器,包装请求(随机代理等)
蜘蛛中间件(Spider Middlewares) : 引擎 -> 爬虫文件,可修改响应对象属性
2. 项目启动
- 由引擎向爬虫程序索要第一个要爬取的URL,交给调度器去入队列
- 调度器处理请求后出队列,通过下载器中间件交给下载器去下载
- 下载器得到响应对象后,通过蜘蛛中间件交给爬虫程序
- 爬虫程序进行数据提取:
- 数据交给管道文件去入库处理
- 对于需要继续跟进的URL,再次交给调度器入队列,依次循环
3. 常用命令
1、创建爬虫项目
scrapy startproject 项目名
2、创建爬虫文件
scrapy genspider 爬虫名 域名
3、运行爬虫
scrapy crawl 爬虫名
4. 项目目录
Baidu # 项目文件夹
├── Baidu # 项目目录
│ ├── items.py # 定义数据结构
│ ├── middlewares.py # 中间件
│ ├── pipelines.py # 数据处理
│ ├── settings.py # 全局配置文件
│ └── spiders
│ ├── baidu.py # 爬虫文件
└── scrapy.cfg # 项目基本配置文件
5. 全局配置文件
1、定义User-Agent
USER_AGENT = 'Mozilla/5.0'
2、是否遵循robots协议,一般设置为False
ROBOTSTXT_OBEY = False
3、最大并发量,默认为16
CONCURRENT_REQUESTS = 32
4、下载延迟时间
DOWNLOAD_DELAY = 1
5、请求头,此处也可以添加User-Agent
DEFAULT_REQUEST_HEADERS={}
6、项目管道
ITEM_PIPELINES={
'项目目录名.pipelines.类名':300
}
6.创建爬虫项目步骤
1、新建项目 :scrapy startproject 项目名
2、cd 项目文件夹
3、新建爬虫文件 :scrapy genspider 文件名 域名
4、明确目标(items.py)
5、写爬虫程序(文件名.py)
6、管道文件(pipelines.py)
7、全局配置(settings.py)
8、运行爬虫 :scrapy crawl 爬虫名
7.运行爬虫
1、创建begin.py(和scrapy.cfg文件同目录)
2、begin.py中内容:
from scrapy import cmdline
cmdline.execute('scrapy crawl maoyan'.split())
Pycharm运行爬虫
- 创建run.py(和scrapy.cfg文件同目录)
- run.py中内容:
from scrapy import cmdline
cmdline.execute('scrapy crawl maoyan'.split())
8. 示例
爬取百度首页的标签--百度一下,你就知道
步骤:
-
创建项目Baidu 和 爬虫文件baidu
scrapy startproject Baidu # 创建项目 cd Baidu scrapy genspider baidu www.baidu.com # 创建爬虫文件
-
编写爬虫文件baidu.py,xpath提取数据
# -*- coding: utf-8 -*- import scrapy class BaiduSpider(scrapy.Spider): name = 'baidu' allowed_domains = ['www.baidu.com'] start_urls = ['http://www.baidu.com/'] def parse(self, response): result = response.xpath('/html/head/title/text()').extract_first() print('*'*50) print(result) print('*'*50)
-
全局配置文件settings.py
USER_AGENT = 'Mozilla/5.0' ROBOTSTXT_OBEY = False DEFAULT_REQUEST_HEADERS = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en', }
-
创建run.py
from scrapy import cmdline cmdline.execute('scrapy crawl baidu'.split())
-
启动爬虫
直接运行
run.py
即可
二、奇淫技巧
-
节点对象.xpath('')
- 列表,元素为选择器 ['
] - 列表.extract() :序列化列表中所有选择器为Unicode字符串 ['A','B','C']
- 列表.extract_first() 或者 get() :获取列表中第1个序列化的元素(字符串)
- 列表,元素为选择器 ['
-
pipelines.py中必须由1个函数叫process_item
def process_item(self,item,spider): return item ( * 此处必须返回 item )
-
日志变量及日志级别(settings.py)
- 日志相关变量
- LOG_LEVEL = ''
- LOG_FILE = '文件名.log'
- 日志级别
- 5 CRITICAL :严重错误
- 4 ERROR :普通错误
- 3 WARNING :警告
- 2 INFO :一般信息
- 1 DEBUG :调试信息
- 日志相关变量
-
管道文件使用
-
在爬虫文件中为items.py中类做实例化,用爬下来的数据给对象赋值
from ..items import MaoyanItem item = MaoyanItem()
-
管道文件(pipelines.py)
-
开启管道(settings.py)
ITEM_PIPELINES = { '项目目录名.pipelines.类名':优先级 }
-
三、猫眼电影
1.创建项目
创建项目文件,爬虫文件
scrapy startproject Maoyan
cd Maoyan
scrapy genspider maoyan https://maoyan.com
创建运行文件
# run.py
from scrapy import cmdline
cmdline.execute('scrapy crawl maoyan'.split())
设置全局设置
# settings.py 只展示需要更改的地方
ROBOTSTXT_OBEY = False
DEFAULT_REQUEST_HEADERS = {
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0'
}
2. 定义数据结构
./Maoyan/items.py
class MaoyanItem(scrapy.Item):
# define the fields for your item here like:
name = scrapy.Field()
star = scrapy.Field()
time = scrapy.Field()
3. 定义spider
./Maoyan/Spiders/maoyan.py
# -*- coding: utf-8 -*-
import scrapy
from ..items import MaoyanItem
class MaoyanSpider(scrapy.Spider):
name = 'maoyan'
allowed_domains = ['maoyan.com']
# 起始的URL地址,即第一页地址
start_urls = ['https://maoyan.com/board/4?offset=0']
def parse(self, response):
# 基准的xpath
dd_list = response.xpath('//dl[@class="board-wrapper"]/dd')
# 依次遍历
for dd in dd_list:
# 创建item对象(./Maoyan/items.py)
item = MaoyanItem()
# 电影名称
item['name'] = dd.xpath('./a/@title').extract_first().strip()
# 电影主演
item['star'] = dd.xpath('.//p[@class="star"]/text()').extract_first().strip()
# 电影上映时间
item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').extract_first().strip()
# 把爬取的数据交给管道文件piplines处理
yield item
self.offset += 10
if self.offset <= 90:
url = 'https://maoyan.com/board/4?offset={}'.format(self.offset) # 拼接下一页的地址
# 交给调度器入队列
yield scrapy.Request(url = url, callback=self.parse)
当一个函数中出现了
yield
关键字时,则python将该函数当做生成器使用,也就是说,函数执行到yield
时,将数据传递给piplines
,并且函数暂停到此处,等待下一次的调用,当再次调用的时候,则会从上一次的地方继续开始,直到调用结束,scrapy
也会捕获这个异常
4. 定义管道文件
./Maoyan/pipelines.py
class MaoyanPipeline(object):
def process_item(self, item, spider):
print(item['name'])
print(item['star'])
print(item['time'])
return item
开启管道需要设置settings.py
ITEM_PIPELINES = {
'Maoyan.pipelines.MaoyanPipeline': 300,
}
300代表优先级,优先级的范围是1-1000,数字越小,优先级越高
5. 小结
以上方法已经足以爬取需要的数据,但是,在运行的过程中,URL在入队列后又出队列,到了yield之后又交给调度器,如此往复循环,并没有发挥scrapy的多线程优势,届时,需要修改代码来充分的使用多线程技术。
5.1 法一
修改maoyan.py
原理:将所有的URL全部交给调度器
# -*- coding: utf-8 -*-
import scrapy
from ..items import MaoyanItem
class MaoyanSpider(scrapy.Spider):
name = 'maoyan'
allowed_domains = ['maoyan.com']
# 起始的URL地址,即第一页地址
start_urls = ['https://maoyan.com/board/4?offset=0']
offset = 0
def parse(self, response):
for offset in range(0, 91, 10):
url = 'https://maoyan.com/board/4?offset={}'.format(str(offset))
# 把地址交给调度器入队列
yield scrapy.Request(url=url,callback=self.parse_html)
def parse_html(self, response):
# 基准xpath,匹配每个电影信息节点对象列表
dd_list = response.xpath('//dl[@class="board-wrapper"]/dd')
# dd_list : [<element dd at xxx>,<...>]
for dd in dd_list:
# 创建item对象
item = MaoyanItem()
# [<selector xpath='' data='霸王别姬'>]
# dd.xpath('')结果为[选择器1,选择器2]
# .extract() 把[选择器1,选择器2]所有选择器序列化为unicode字符串
# .extract_first() : 取第一个字符串
item['name'] = dd.xpath('./a/@title').extract_first().strip()
item['star'] = dd.xpath('.//p[@class="star"]/text()').extract()[0].strip()
item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').extract()[0]
yield item
这里有个问题,parse函数用来解析,但是在这里却是交付URL,物未尽其用,所以再次修改
5.2 法二
既然离不开start_url
,那么就剖其源码可以得到,该变量应用于start_requests
函数
以下为start_requests()
源码:
def start_requests(self):
cls = self.__class__
if method_is_overridden(cls, Spider, 'make_requests_from_url'):
warnings.warn(
"Spider.make_requests_from_url method is deprecated; it "
"won't be called in future Scrapy releases. Please "
"override Spider.start_requests method instead (see %s.%s)." % (
cls.__module__, cls.__name__
),
)
for url in self.start_urls:
yield self.make_requests_from_url(url)
else:
for url in self.start_urls:
yield Request(url, dont_filter=True)
那么就可以重写该方法,只需要再次定义start_requests
函数即可,根据优先级则先执行本文件的start_requests
方法
def start_requests(self):
for offset in range(0, 91, 10):
url = 'https://maoyan.com/board/4?offset={}'.format(str(offset))
# 交给调度器
yield scrapy.Request(url=url, callback=self.parse_html)
完整maoyan.py
代码
# -*- coding: utf-8 -*-
import scrapy
from ..items import MaoyanItem
class MaoyanSpider(scrapy.Spider):
name = 'maoyan'
allowed_domains = ['maoyan.com']
# 重写start_requests方法,提高效率
def start_requests(self):
for offset in range(0, 91, 10):
url = 'https://maoyan.com/board/4?offset={}'.format(str(offset))
# 交给调度器
yield scrapy.Request(url=url, callback=self.parse_html)
def parse_html(self, response):
# 基准xpath,匹配每个电影信息节点对象列表
dd_list = response.xpath('//dl[@class="board-wrapper"]/dd')
# dd_list : [<element dd at xxx>,<...>]
for dd in dd_list:
# 创建item对象
item = MaoyanItem()
# [<selector xpath='' data='霸王别姬'>]
# dd.xpath('')结果为[选择器1,选择器2]
# .extract() 把[选择器1,选择器2]所有选择器序列化为unicode字符串
# .extract_first() : 取第一个字符串
item['name'] = dd.xpath('./a/@title').extract_first().strip()
item['star'] = dd.xpath('.//p[@class="star"]/text()').extract()[0].strip()
item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').extract()[0]
yield item