Scrapy介绍
Scrapy是⼀个为了爬取⽹站数据,提取结构性数据⽽编写的应⽤框架,让爬虫更快,更强大。用更少代码,实现更多灵活的功能。内部封装了Twisted(内部源码采用了闭包)异步网络框架。
框架和模块的区别
框架是软件框架是项目软件开发过程中提取特定领域软件的共性部分形成的体系结构,不同领域的软件项目有着不同的框架类型。框架不是现成可用的应用系统,而是一个半成品,是一个提供了诸多服务,供开发人员进行二次开发,实现具体功能的应用系统。
模块是指的一段能够实现某个有价值目标的的成员代码段,这样的东西,我们还有另一个称呼:例程,而例程有两种,即函数和过程。定义模块的原则应该是:高内聚和低耦合。
源安装
临时使用:
可以在使用pip的时候,加上参数-i和镜像地址(如
https://pypi.tuna.tsinghua.edu.cn/simple),
例如:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas,这样就会从清华镜像安装pandas库。
pip install 包名 -i http://pypi.douban.com/simple
比较常用的国内镜像包括:
名称 | 地址 |
---|---|
清华大学 | https://pypi.tuna.tsinghua.edu.cn/simple/ |
阿里云 | http://mirrors.aliyun.com/pypi/simple/ |
豆瓣 | http://pypi.douban.com/simple/ |
科学技术大学 | http://pypi.mirrors.ustc.edu.cn/simple/ |
永久修改:
- Linux操作系统
修改 ~/.pip/pip.conf (没有就创建一个文件夹及文件。文件夹要加“.”,表示是隐藏文件夹)
内容如下:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
[install]
trusted-host = https://pypi.tuna.tsinghua.edu.cn
- windows操作系统
直接在user目录中创建一个pip目录,如:C:\Users\xx\pip,然后新建文件pip.ini,即 %HOMEPATH%\pip\pip.ini,在pip.ini文件中输入以下内容(以豆瓣镜像为例):
[global]
index-url = http://pypi.douban.com/simple
[install]
trusted-host = pypi.douban.com
创建pip.ini文件可以用文本编辑器,例如 sublime text3。
srcapy的安装
pip install scrapy -i https://pypi.tuna.tsinghua.edu.cn/simple
srcapy的工作流程
- 一般爬虫的工作流程
markdown流程图使用
TB - 顶部到底部
BT - 底部到顶部
RL - 由右至左
LR - 由左至右
TD - 与 TB 相同
模块名称 | 功能 | 手码情况 |
---|---|---|
Scrapy engine(引擎) | 总指挥:负责数据和信号的在不同模块间的传递 | scrapy已经实现 |
Scheduler(调度器) | ⼀个队列,存放引擎发过来的request请求 | scrapy已经实现 |
Downloader(下载器) | 下载把引擎发过来的requests请求,并返回给引擎 | scrapy已经实现 |
Spider(爬⾍) | 处理引擎发来的response,提取数据,提取url,并交给引擎 | 需要⼿写 |
Item Pipline(管道) | 处理引擎传过来的数据,比如存储 | 需要⼿写 |
Downloader | 可以⾃定义的下载扩展,比如设置代理 | ⼀般不⽤⼿写 |
Spider Middlewares(中间件) | 可以⾃定义requests请求和进⾏response过滤 | ⼀般不⽤⼿写 |
scrapy快速入门
创建scrapy项目
(venv) D:\Python_study\new_python\venv\scrapy_space>scrapy startproject my_first_scrapy
New Scrapy project 'my_first_scrapy', using template directory 'd:\python_study\new_python\venv\lib\site-packages\scrapy\templates\proje
ct', created in:
D:\Python_study\new_python\venv\scrapy_space\my_first_scrapy
You can start your first spider with:
cd my_first_scrapy
scrapy genspider example example.com
创建一个爬虫
(venv) D:\Python_study\new_python\venv\scrapy_space>scrapy genspider db douban.com
scrapy genspider 【爬虫名字】 [爬虫网站]
注意爬虫网站不要https://www
执行如下:
(venv) D:\Python_study\new_python\venv\scrapy_space\my_first_scrapy>scrapy genspider douban douban.com
Created spider 'douban' using template 'basic' in module:
my_first_scrapy.spiders.douban
查看项目目录就会找到,为我们自动生成的爬虫文件douban,py
目录结构分析
#Don’t forget to add your pipeline to the ITEM_PIPELINES setting
不要忘记把管道添加设置中
爬虫代码编写
如douban.py
# -*- coding: utf-8 -*-
import scrapy
class DoubanSpider(scrapy.Spider):
name = 'douban'
allowed_domains = ['douban.com']
start_urls = ['http://douban.com/']
def parse(self, response):
#pass
print('*'*40)
print(response)
print(type(response))
print('='*40)
注意在运行代码前要把/spider/settings.py文件的头代理打开
#Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
#'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#'Accept-Language': 'en',
#}
改为:
DEFAULT_REQUEST_HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6788.400 QQBrowser/10.3.2816.400',
'Accept-Language': 'en'
}
注释掉ROBOTSTXT协议
# Obey robots.txt rules
#ROBOTSTXT_OBEY = True
2020-06-22 14:00:01 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (301) to <GET https://www.douban.com/> from <GET http://w
ww.douban.com/>
2020-06-22 14:00:02 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.douban.com/> (referer: None)
****************************************
<200 https://www.douban.com/>
<class 'scrapy.http.response.html.HtmlResponse'>
========================================
2020-06-22 14:00:02 [scrapy.core.engine] INFO: Closing spider (finished)
2020-06-22 14:00:02 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 773,
'downloader/request_count': 3,
'downloader/request_method_count/GET': 3,
'downloader/response_bytes': 18493,
'downloader/response_count': 3,
'downloader/response_status_count/200': 1,
'downloader/response_status_count/301': 2,
'elapsed_time_seconds': 0.584632,
'finish_reason': 'finished',
'finish_time': datetime.datetime(2020, 6, 22, 6, 0, 2, 294357),
'log_count/DEBUG': 3,
'log_count/INFO': 10,
'response_received_count': 1,
'scheduler/dequeued': 3,
'scheduler/dequeued/memory': 3,
'scheduler/enqueued': 3,
'scheduler/enqueued/memory': 3,
'start_time': datetime.datetime(2020, 6, 22, 6, 0, 1, 709725)}
2020-06-22 14:00:02 [scrapy.core.engine] INFO: Spider closed (finished)
settings.py设置日志级别屏蔽,冗余日志
LOG_LEVEL = 'WARNING'
获取这个列表的内容
douban.py
# -*- coding: utf-8 -*-
import scrapy
class DoubanSpider(scrapy.Spider):
name = 'douban'
allowed_domains = ['douban.com']
start_urls = ['http://movie.douban.com/']
# 从xpath中获取数据selector对象之后,要提取数据
# 方法1: 用get()/getall()
# 如<a>文字</a>
# 方法2: extract_first() -》这是一个文字
def parse(self, response):
#pass
# print('*'*40)
# print(response)
# print(type(response))
print('*' * 40)
list_koubei = response.xpath("//div[@class='billboard-bd']//tr")
#print(list_koubei)
item = {}
for koubei in list_koubei:
#print(koubei)
#print(koubei.xpath('td[@class="title"]/a/text()'))
item['name'] = koubei.xpath('td[@class="title"]/a/text()').extract_first()
print(item)
yield item
print('=' * 40)
运行爬虫
方法一用命令运行
scrapy crawl [爬虫名称]
接着再用命令运行douban.py
(venv) D:\Python_study\new_python\venv\scrapy_space\my_first_scrapy>scrapy crawl douban
方法二建立py文件运行爬虫
#!/user/bin/env python
#-*-coding utf-8-*-
#@Time : 2020/6/2215:46
#@Author : GodSpeed
#@File : start.py.py
#@Software : PyCharm
from scrapy import cmdline
#cmdline.execute("scrapy crawl douban".split()) #方式1
cmdline.execute(['scrapy','crawl','douban']) #方式2
运行的结果:
D:\Python_study\new_python\venv\Scripts\python.exe D:/Python_study/new_python/venv/scrapy_space/my_first_scrapy/my_first_scrapy/spiders/start.py
****************************************
{'name': '默片解说员'}
{'name': '数电影的人'}
{'name': '马茹娜的非凡旅程'}
{'name': '二十世纪'}
{'name': '金色荣耀'}
{'name': '鲁邦三世 The First'}
{'name': '知晓天空之蓝的人啊'}
{'name': '上帝存在,她叫佩特鲁尼娅'}
{'name': '最后与最初的人类'}
{'name': '双郎'}
========================================
Process finished with exit code 0
爬虫数据保存
打开settings的管道设置
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
#ITEM_PIPELINES = {
# 'my_first_scrapy.pipelines.MyFirstScrapyPipeline': 300,
#}
打开
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
#300表示优先级
'my_first_scrapy.pipelines.MyFirstScrapyPipeline': 300,
}
pipelines.py文件
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
class MyFirstScrapyPipeline:
def process_item(self, item, spider):
print('pipelines:MyFirstScrapyPipeline process_item start'+'-'*5)
print(item)
print('pipelines:MyFirstScrapyPipeline process_item end'+'='*5)
return item
迭代器和生成器
# 迭代器是一个可以记住便利位置的对象 iter() next()
# 只能向前不能后退
# 迭代器对象从序列中第一个元素开始,直到元素全部访问完后结束
# lst = ['蜘蛛侠','钢铁侠','李小龙','灭霸','唐吉可德']
# myiter = iter(lst)
#
# # print(next(myiter))
# # print(next(myiter))
# # print(next(myiter))
# # print(next(myiter))
# # print(next(myiter))
#
# for it in myiter:
# print(it)
# 生成器:在Python中使用了yield这个函数称为生成器
# 生成器是一个返回迭代器的函数
# 在调用生成器运行的过程中,每次约到yield函数会暂停,并保存
# 当前运行的信息并返回yield的值,并在下一次调用next()函数时,
# 从当前位置继续进行
def fn1():
print(1)
print(2)
print(3)
print(4)
print(5)
def fn2():
print(111)
yield 1
print(222)
yield 2
print(333)
yield 3
print(444)
yield 4
print(555)
yield 5
if __name__ == '__main__':
#print(type(fn1)) # <class 'function'>
#print(type(fn2)) # <class 'function'>
#f1 = fn1()
#print(type(f1)) # <class 'NoneType'>
f2 = fn2() # 没有打印任何东西
#print(type(f2)) # <class 'generator'>
#next(f2) #one by one的执行
#next(f2)
#next(f2)
#next(f2)
#next(f2)
'''
111
222
333
444
555
'''
print('return:',next(f2))
print('return:', next(f2))
print('return:', next(f2))
# 111
# return: 1
# 222
# return: 2
# 333
# return: 3
数据的处理
pipelines.py文件
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
import json
class MyFirstScrapyPipeline:
def __init__(self):
# 写入保存的逻辑
self.f = open('tudou.json','w',encoding='utf-8')
# 爬虫打开函数
def open_spider(self,item):
print('爬虫开始了')
def process_item(self, item, spider):
print('pipelines:MyFirstScrapyPipeline process_item start'+'-'*5)
print(item)
# json.dumps()可以将自定义形式的数据转换为字符串
# ensure_ascii=Flase 在转换的时候默认是ascii字符,防止出现乱码
item_json = json.dumps(item,ensure_ascii=False)
self.f.write(item_json)
print('pipelines:MyFirstScrapyPipeline process_item end'+'='*5)
return item
def close_spider(self,item ):
print('爬虫结束了')
self.f.close()
小结:
- scrapy模块的安装
pip install scrapy -i https://pypi.douban.com/simple - 创建scrapy项目 scrapy startproject myspider
- 生成一个爬虫 scrapy genspider demo xxx.com
如:scrapy genspider douban douban.com - 用xpath提取数据
- piplines中保存数据
- 运行爬虫项目
scrapy crawl 爬虫名字 - pycharm中运行爬虫项目
1.先创建一个py文件
2.导入一个模块 from srcapy import cmdline
3.cmdline.execute(“scrapy crawl douban”.split()) #方式1
cmdline.execute([‘scrapy’,‘crawl’,‘douban’]) #方式2
Scrapy CrawlSpider说明
CrawlSpider是爬取那些具有一定规则网站的常用的爬虫,它基于Spider并有一些独特属性
rules: 是Rule对象的集合,用于匹配目标网站并排除干扰
parse_start_url: 用于爬取起始响应,必须要返回Item,Request中的一个。
因为rules是Rule对象的集合,所以这里也要介绍一下Rule。它有几个参数:link_extractor、callback=None、cb_kwargs=None、follow=None、process_links=None、process_request=None
其中的link_extractor既可以自己定义,也可以使用已有LinkExtractor类,主要参数为:
allow:满足括号中“正则表达式”的值会被提取,如果为空,则全部匹配。
deny:与这个正则表达式(或正则表达式列表)不匹配的URL一定不提取。
allow_domains:会被提取的链接的domains。
deny_domains:一定不会被提取链接的domains。
restrict_xpaths:使用xpath表达式,和allow共同作用过滤链接。还有一个类似的restrict_css
CrawlSpider创建
scrapy genspider -t crawl 爬⾍名字 域名
CrawlSpider实例
创建爬虫项目
cd crawspider_yg
scrapy genspider -t crawl craw_spider_yg wz.sun0769.com
自动生成了craw_spider_yg.py
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class CrawSpiderYgSpider(CrawlSpider):
name = 'craw_spider_yg'
allowed_domains = ['wz.sun0769.com']
start_urls = ['http://wz.sun0769.com/']
rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
)
def parse_item(self, response):
item = {}
#item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
#item['name'] = response.xpath('//div[@id="name"]').get()
#item['description'] = response.xpath('//div[@id="description"]').get()
return item
settings.py文件需要修改的地方
LOG_LEVEL = 'WARNING'
# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
}
# Obey robots.txt rules
#ROBOTSTXT_OBEY = True
craw_spider_yg.py文件
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class CrawSpiderYgSpider(CrawlSpider):
name = 'craw_spider_yg'
allowed_domains = ['wz.sun0769.com']
start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1']
# 定义提前url地址的规则
rules = (
# LinkExtractor链接提取器
# callback 提取url地址的response 会交给回调函数处理
# follow 是否连续提取新的url地址
#Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
# 列表页
Rule(LinkExtractor(allow=r'http://wz.sun0769.com/political/index/politicsNewest\?id=1&page=\d+'), follow=True),
# 详情页
Rule(LinkExtractor(allow=r'http://wz.sun0769.com/political/politics/index\?id=\d+'), callback='parse_item', follow=False),
)
def parse_item(self, response):
item = {}
#item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
#item['name'] = response.xpath('//div[@id="name"]').get()
#item['description'] = response.xpath('//div[@id="description"]').get()
item['content'] = response.xpath('//div[@class="details-box"]/pre/text()').extract_first()
print(item['content'])
return item
start.py
from scrapy import cmdline
cmdline.execute(['scrapy','crawl','craw_spider_yg'])
Crawl spider小结
- 使用条件:目标信息必须在网页源码上才可以使用CrawlSpider;
- 创建CrawlSpider 创建方法 scrapy genspider -t crawl 爬虫的名字 域名;
- CrawlSpider需要定义回调函数的到时候,不要以parse命名,以避免混淆;
- 注意Rule对象中的follow callback的使用条件,什么时候用,什么时候不用;
- 注意Rule中的allow的正则表达式必须要?等特殊字符前面要加\进行转义处理,否则会导致爬取不到数据。