-
一、scrapy框架的简介和基础使用
-
二、scrapy框架--parse方法解析示例
-
三、scrapy框架--基于终端指令的持久化存储
-
四、scrapy框架--基于管道的持久化存储
-
五、scrapy框架--基于MySQL数据库的持久化存储
-
六、scrapy框架--基于redis的持久化存储
-
七、scrapy框架--将爬取到的数据分别存储在本地磁盘、redis、MySQL中
-
八、scrapy框架--多个url数据爬取(请求手动发送)
-
九、scrapy框架--核心组件
-
十、scrapy框架--发送post请求
-
十一、scrapy框架--session的使用
-
十二、scrapy框架--代理的使用
-
十三、scrapy框架--日志等级
-
十四、scrapy框架--请求传参
-
十五、scrapy框架--CrawlSpider的使用
-
十六、scrapy框架--分布式爬虫RedisCrawlSpider
-
十七、scrapy框架--分布式爬虫RedisSpider
-
十八、scrapy框架--UA池、代理池
-
十九、scrapy框架--综合应用(爬网易新闻)
-
二十、scrapy框架--总结
===============================================
一、scrapy框架的简介和基础使用
a)概念:为了爬取网站数据而编写的一款应用框架,出名,强大。
框架其实就是一个集成了相应功能且具有很强通用性的项目模板。
(高性能的异步下载,解析,持久化...)
b) 安装:
i. linux mac --pip install scrapy
ii. windows
-step1: pip install wheel
-step2: 下载并安装twisted: https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
pip install 下载好的t框架.whl
-step3: pip install pywin32
-step4: pip install scrapy
c) 测试安装是否成功:
命令终端输入“scrapy”后出现scrapy版本号
d) 基础使用:使用流程
i. 创建一个工程,在某目录的命令行:scrapy startproject 工程名称
ii. 在工程目录下创建一个爬虫文件:
-step1: cd 工程名称
-step2: scrapy genspider 爬虫文件名称 起始url
例如:scrapy genspider first www.qiushibaike.com
------------------------------------------------------------------------
附:目录结构说明
spiders 爬虫目录,如:创建文件,编写爬虫解析规则
settings.py 配置文件,如:递归的层数,并发数,延迟下载等
pipelines 数据持久化处理
items.py 设置数据存储模板,用于结构化数据,如Django的Model
scrapy.cfg 项目的主配置信息(真正爬虫相关的配置信息在settings.py文件中)
------------------------------------------------------------------------
iii.对应的文件中编写爬虫程序来完成爬虫的相关操作

# -*- coding: utf-8 -*- import scrapy class FirstSpider(scrapy.Spider): # 爬虫文件的名称:用来定位到某一个爬虫文件,因为一个项目可以有多个爬虫文件 name = 'first' # 允许的域名:存放一系列域名,只能爬取该列表内的域名 # 一般可以注释掉,如果想爬取图片等就可能不在这个域名 allowed_domains = ['www.qiushibaike.com'] # 起始url:就是当前工程想爬取的url页面,必须在允许的域名内 start_urls = ['http://www.qiushibaike.com/'] # 解析方法:对获取的页面数据进行指定内容的解析,建议使用xpath进行解析 # 根据起始url发起请求,response是请求成功后拿到的响应对象 # parse方法返回值必须为迭代器或者None def parse(self, response): print(response.text)
iv. 配置文件settings.py的编写
第22行:ROBOTSTXT_OBEY = False # 不遵守robots协议
第19行:USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
v. 执行:scrapy crawl 爬虫文件的名称 --nolog
如果后面加--nolog不输出日志信息
当前的工程从爬虫文件开始执行,scrapy框架会向起始url列表中的url发起请求,请求成功后获取响应对象,传给parse方法,然后调用parse方法进行解析
二、scrapy框架--parse方法解析示例
--需求:解析糗百中段子的内容和作者
1.创建一个工程:scrapy startproject qiubaiPro
2.创建一个爬虫文件:
cd qiubaiPro/
scrapy genspider qiubai www.qiushibaike.com/text
3.编写代码(爬虫文件):
# -*- coding: utf-8 -*-
import scrapy
class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
# allowed_domains = ['www.qiushibaike.com']
start_urls = ['https://www.qiushibaike.com/text/']
def parse(self, response):
div_list = response.xpath('//div[@id="content-left"]/div')
for div in div_list:
# xpath解析到的内容被存储到了Selector对象中
# 可以用extract()方法获取Selector里的数据
# extract_first()等价于extract()[0]
author = div.xpath('./div/a[2]/h2/text()').extract_first()
content = div.xpath('.//div[@class="content"]/span/text()').extract_first()
4.配置文件的编写:改19、22行
5.执行工程
三、scrapy框架--基于终端指令的持久化存储
i. 保证parse方法返回一个可迭代对象(存储解析到的页面内容)
# -*- coding: utf-8 -*-
import scrapy
class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
start_urls = ['https://www.qiushibaike.com/text/']
def parse(self, response):
div_list = response.xpath('//div[@id="content-left"]/div')
data_list = []
for div in div_list:
author = div.xpath('./div/a[2]/h2/text()').extract_first()
content = div.xpath('.//div[@class="content"]/span/text()').extract_first()
dict = {"author":author, "content":content}
data_list.append(dict)
return data_list
ii. 使用终端指令完成数据存储到指定磁盘文件
scrapy crawl 爬虫文件名称 -o 磁盘文件.csv --nolog
四、scrapy框架--基于管道的持久化存储
i. items.py:存储解析到的页面数据
ii. pipelines:处理持久化存储的相关操作
iii. 代码实现流程:
0. 给items对象加属性
1. 将解析到的页面数据存储到items对象
2. 使用yield关键字将items提交给管道文件进行处理
3. 在管道文件中编写代码完成数据存储的操作
4. 在配置文件中开启管道操作
5. 执行
0:items加属性,取的时候只能item["author"] 不能item.author
import scrapy
class QiubaiproItem(scrapy.Item):
author = scrapy.Field()
content = scrapy.Field()
1. 将解析到的数据(author和content)存储到items对象
# -*- coding: utf-8 -*-
import scrapy
from qiubaiPro.items import QiubaiproItem
class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
start_urls = ['https://www.qiushibaike.com/text/']
def parse(self, response):
div_list = response.xpath('//div[@id="content-left"]/div')
for div in div_list:
author = div.xpath('./div/a[2]/h2/text()').extract_first()
content = div.xpath('.//div[@class="content"]/span/text()').extract_first()
# 1,将解析到的数据(author和content)存储到items对象
item = QiubaiproItem()
item["author"] = author
item["content"] = content
# 2,将item对象提交给管道
yield item
3. 在管道文件中编写代码完成数据存储的操作
class QiubaiproPipeline(object):
fp = None
# 整个爬虫过程中,open方法只会在开始爬虫的时候被调用一次
def open_spider(self, spider):
print('开始爬虫')
self.fp = open('./qiubai_pipe.text','w',encoding='utf8')
# 该方法可以接受爬虫文件中提交过来的item对象,并且对item对象中存储的页面数据进行持久化
# 参数item:接收到的item对象
# 每当爬虫文件yield一次item,该方法就会被执行一次
# 文件'qiubai_pipe.text'在整个过程只打开一次,关闭一次,文件中就不会只有一条记录
def process_item(self, item, spider):
# 取出item对象中的数据
author = item["author"]
content = item["content"]
# 持久化存储
self.fp.write(author+":"+content+"\n\n")
return item
# close方法只会在爬虫结束的时候被调用一次
def close_spider(self,spider):
print('爬虫结束')
self.fp.close()
4. 在配置文件中开启管道操作
第68行:取消注释(数字表示优先级,这里只有一个所以没有谁先谁后)
ITEM_PIPELINES = {
'qiubaiPro.piplines.QiubaiproPipeline':300,
}
5.执行:scrapy crawl qiubai --nolog
五、scrapy框架--基于MySQL数据库的持久化存储
编码流程跟管道一样,只是第3步不同。
0. 给items对象加属性
1. 将解析到的页面数据存储到items对象
2. 使用yield关键字将items提交给管道文件进行处理
3. 在管道文件中编写代码完成数据存储的操作(连接数据库-》执行SQL语句-》提交事务)
4. 在配置文件中开启管道操作
5. 执行
import pymysql
class QiubaiproPipeline(object):
cursor = None
conn = None
def open_spider(self, spider):
print('开始爬虫')
# 1.连接数据库
self.conn = pymysql.Connect(
host='127.0.0.1',
port=3306,
user='root',
password='mysql8',
db='qiubai')
def process_item(self, item, spider):
# 2.执行SQL语句
sql = 'insert into qiubai values("%s","%s")'%(item["author"], item["content"])
self.cursor = self.conn.cursor()
try:
self.cursor.excute(sql)
self.conn.commit()
except Exception as e:
print(e)
self.conn.rollback()
return item
def close_spider(self,spider):
print('爬虫结束')
# 3.关闭连接
self.cursor.close()
self.conn.close()
六、scrapy框架--基于redis的持久化存储
编码流程跟管道一样。只是第3步不同
0. 给items对象加属性
1. 将解析到的页面数据存储到items对象
2. 使用yield关键字将items提交给管道文件进行处理
3. 在管道文件中编写代码完成数据存储的操作(连接redis-》存储数据)
4. 在配置文件中开启管道操作
5. 执行
import redis
class QiubaiproPipeline(object):
conn = None
def open_spider(self, spider):
print('开始爬虫')
# 1.连接redis
self.conn = redis.Redis(host='127.0.0.1', port=6379)
def process_item(self, item, spider):
# 2.存储数据
dict = {'author' : item["author"], 'content' : item["content"] }
self.conn.lpush('data', dict)
return item
七、scrapy框架--将爬取到的数据分别存储在本地磁盘、redis、MySQL中
1. 在管道文件中编写对应平台的管道类
在管道文件中定义三个类。如果下面自定义的两个类也被调用,那也可以写入
2. 在配置文件中对自定义的管道类进行生效操作
ITEM_PIPELINES = {
'qiubaiPro.pipelines.QiubaiproPipeline': 300,
'qiubaiPro.pipelines.QiubaiByFiles': 400,
'qiubaiPro.pipelines.QiubaiByMysql': 500,
}
import redis
#实现将数据存储到redis中
class QiubaiproPipeline(object):
conn = None
def open_spider(self, spider):
print('开始爬虫')
# 1.连接redis
self.conn = redis.Redis(host='127.0.0.1', port=6379)
def process_item(self, item, spider):
# 2.存储数据
dict = {'author' : item["author"], 'content' : item["content"] }
self.conn.lpush('data', dict)
return item
#实现将数据存储到本地磁盘
class QiubaiByFiles(object):
def process_item(self, item, spider):
print('写入到本地磁盘')
return item
#实现将数据存储到mysql数据库
class QiubaiByMysql(object):
def process_item(self, item, spider):
print('写入到mysql')
return item
八、scrapy框架--多个url数据爬取(请求手动发送)
需求:爬取糗百所有页码的作者和文章
跟单个url爬取一样,只是标红部分不同
=========items.py============
import scrapyclass QiubaibypagesItem(scrapy.Item): author = scrapy.Field() content = scrapy.Field()
=========qiubai.py============
import scrapyfrom qiubaiByPages.items import QiubaibypagesItemclass QiubaiSpider(scrapy.Spider): name = 'qiubai' start_urls = ['https://www.qiushibaike.com/text/'] # 设计一个通用的url模板 page_num = 1 url = 'https://www.qiushibaike.com/text/page/%d/' def parse(self, response): div_list = response.xpath('//div[@id="content-left"]/div') for div in div_list: author = div.xpath('./div[@class="author clearfix"]/a[2]/h2//text()').extract_first() content = div.xpath('.//div[@class="content"]/span//text()').extract_first() # 创建一个item对象,将解析到的数据存储到item对象 item = QiubaibypagesItem() item["author"] = author item["content"] = content # 将item对象提交给管道 yield item # 请求的手动发送,13表示最后一页页码 if self.page_num <= 13: print("爬取到了第%d页."%self.page_num) self.page_num += 1 new_url = format(self.url % self.page_num) yield scrapy.Request(url=new_url, callback=self.parse)
=========pipelines.py============
class QiubaibypagesPipeline(object): fp = None def open_spider(self, spider): print('开始爬虫') self.fp = open('./qiubai.txt','w',encoding='utf-8') def process_item(self, item, spider): self.fp.write(item["author"]+":"+item["content"]+"\n\n") return item def close_spider(self,spider): self.fp.close() print('爬虫结束')
=========settings.py============
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'ROBOTSTXT_OBEY = FalseITEM_PIPELINES = { 'qiubaiByPages.pipelines.QiubaibypagesPipeline': 300,}
九、scrapy框架--核心组件
引擎接收到start_url列表中的请求对象,转交给调度器
请求被调度器装到队列里,由调度器调度请求给下载器
下载器从互联网下载后将结果转交给spiders
spiders接收到响应对象后,调度器调用parse方法对页面进行解析
spiders将解析结果封装到items,提交给管道
管道收到items后进行持久化
引擎(Scrapy)
用来处理整个系统的数据流处理, 触发事务(框架核心)
调度器(Scheduler)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
下载器(Downloader)
用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
爬虫(Spiders)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
十、scrapy框架--发送post请求
发送post请求一定要对start_requests方法进行重写。
方式1.Request()方法中给method属性赋值
方式2.FormRequest()进行post请求的发送(推荐)
需求:给百度翻译发送post请求带参数
===========spiders=============
import scrapyclass PostdemoSpider(scrapy.Spider): name = 'postDemo' start_urls = ['https://fanyi.baidu.com/sug'] ''' 之前一直是发的get请求: 覆盖父类的start_requests方法,对start_url列表中的元素进行get请求的发送 def start_requests(self): for url in self.start_urls: yield scrapy.Request(url=url, callback=self.parse) ''' ''' 如何发起post请求? 1.将Request方法中的method参数赋值成post 2.FormRequest()可以发起post请求(推荐) ''' def start_requests(self): data = { 'kw' : 'dog', } for url in self.start_urls: #formdata请求参数对应的字典 yield scrapy.FormRequest(url=url,formdata=data,callback=self.parse) def parse(self, response): print(response.text)
十一、scrapy框架--session的使用
第二次请求的时候,会自动携带第一次请求的cookie,在使用scrapy进行cookie操作的时候,不需要刻意管cookie
需求:爬取豆瓣的个人主页。先登录再查看主页
# -*- coding: utf-8 -*-import scrapyclass DoubanSpider(scrapy.Spider): name = 'douban' allowed_domains = ['www.douban.com'] start_urls = ['https://www.douban.com/accounts/login'] # 登录发送post请求 def start_requests(self): data = { 'source': 'index_nav', 'form_email': '18212345678', 'form_password': '123456' } for url in self.start_urls: yield scrapy.FormRequest(url=url, formdata=data, callback=self.parse) #针对个人主页数据进行解析 def parse_second_page(self, response): fp = open('second.html', 'w', encoding='utf8') fp.write(response.text) fp.close() def parse(self, response): #登录成功后的页面数据进行存储 fp = open('main.html', 'w', encoding='utf8') fp.write(response.text) #获取当前用户的个人主页 url = 'https://www.douban.com/people/18212345678/' yield scrapy.Request(url=url,callback=self.parse_second_page) fp.close()
十二、scrapy框架--代理的使用
原理:
#代理需要下载器的中间件,位于调度器和下载器之间
#拦截调度器发给下载器的请求,进行IP的更换
代码实现流程:
1.下载中间件类的自定义,重写process_request方法,拦截到请求后会调用该方法:
# 自定义一个下载中间件的类,在类中实现process_request方法# 处理中间件拦截到的请求class MyProxy(object): def process_request(self, request, spider): #进行请求IP的更换,IP为代理IP request.meta['proxy'] = "http://120.76.77.152:9999"
2.配置文件中进行代理的开启
DOWNLOADER_MIDDLEWARES = { 'proxyPro.middlewares.MyProxy': 543,}
十三、scrapy框架--日志等级
日志:运行爬虫时,会在控制台打印出一系列的信息
日志等级:
ERROR: 错误
WARNING: 警告
INFO: 一般信息
DEBUG: 调试信息(默认)
默认是输出调试信息。
如果只想让终端打印某个类型的信息,就在settings.py中添加LOG_LEVEL,
如果想将信息输出到文件就添加LOG_FILE:
LOG_LEVEL = 'ERROR'
LOG_FILE = 'log.txt'
十四、scrapy框架--请求传参
请求传参:解决的问题是爬取的数据不在同一个页面中
需求:将id97电影网站中电影详情数据进行爬取(名称,类型,导演,语言,片长)
=============爬虫文件=============
# -*- coding: utf-8 -*-import scrapyfrom moviePro.items import MovieproItemclass MovieSpider(scrapy.Spider): name = 'movie' start_urls = ['http://www.id97.com/movie/'] #用于解析二级子页面中的数据 def parse_by_second_page(self,response): actor = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[1]/td[2]/a/text()').extract_first() language = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[6]/td[2]/a/text()').extract_first() long_time = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[8]/td[2]/a/text()').extract_first() #取出Request方法的meta参数传递过来的字典(response.meta) item = response.meta['item'] item['actor'] = actor item['language'] = language item['long_time'] = long_time #将item提交给管道 yield item def parse(self, response): #名称,类型,导演,语言,片长 div_list = response.xpath('/html/body/div[1]/div[1]/div[2]/div') for div in div_list: name = div.xpath('.//div[@class="meta"]/h1/a/text()').extract_first() kind = div.xpath('.//div[@class="otherinfo"]//text()').extract_first() url = div.xpath('.//div[@class="meta"]/h1/a/@href').extract_first() item = MovieproItem() item['name'] = name item['kind'] = kind #要将其他数据存到item,就要进行请求传参(meta) #需要对url发起请求,获取页面数据,进行指定数据解析,meta把item传给回调函数 #meta参数只能赋值给一个字典(将item对象先封装到字典) yield scrapy.Request(url=url, callback=self.parse_by_second_page,meta={'item': item})
=============items文件============
import scrapyclass MovieproItem(scrapy.Item): name = scrapy.Field() kind = scrapy.Field() actor = scrapy.Field() language = scrapy.Field() long_time = scrapy.Field()
十五、scrapy框架--CrawlSpider的使用
CrawlSpider的作用:
CrawlSpider会根据某一个提取规则提取全站所有链接,并提取链接的链接...一直到全部提取完。
问题:想对某个网站全站的数据进行爬取,例如爬取所有页码的数据
解决方案:
1.手动请求的发送, 递归
2.CrawlSpider(推荐)
CrawlSpider是Spider的一个子类。比Spider功能更强大(链接提取器,规则解析器)
1.创建一个基于CrawlSpider的爬虫文件:scrapy genspider -t crawl chouti dig.chouti.com/
2.使用方法:
==============爬虫文件=============
# -*- coding: utf-8 -*-import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Ruleclass ChoutiSpider(CrawlSpider): name = 'chouti' start_urls = ['https://dig.chouti.com//'] # 实例化一个链接提取器对象 # 链接提取器用来提取指定的链接 # allow参数:赋值一个正则表达式 # 链接提取器可以根据正则表达式在页面中提取指定的链接 # 会提取满足正则表达式的所有,一般用来提取链接,提取到的链接会全部交给规则解析器 link = LinkExtractor(allow=r'/all/hot/recent/\d+') rules = ( #实例化一个规则解析器对象 #规则解析器接收了链接提取器发送的链接后,就会对这些链接发起请求,获取链接对应的页面内容,然后根据指定的规则所有的页面数据进行解析 #callback:指定一个解析规则(方法/函数) #fallow:表示是否将链接提取器继续作用到链接提取器提取出的页面数据中 #如果follow=True会产生大量重复链接,不影响,调度器会进行去重操作 Rule(link, callback='parse_item', follow=False), ) def parse_item(self, response): #链接提取器提取了多少个链接,parse_item方法就执行多少次 print(response)
十六、scrapy框架--分布式爬虫RedisCrawlSpider
分布式爬虫:
1.概念:多台机器上可以执行同一个爬虫程序,实现网站数据的分布式爬取
A机器爬取一部分内容,B机器爬取一部分内容,最后整合
2.原生的scrapy不能实现分布式爬虫,因为--
调度器和管道无法在A、B...多台机器共享,原生scrapy是五大组件配合进行网络数据的爬取
A、B两个机器都有单独的五大组件,这样执行结束后,都下载了单独的一份数据,都存储了单独的一份数据
3.scrapy-redis组件:专门为scrapy开发的一套组件,该组件可以让scrapy实现分布式。
4.分布式爬取流程
4.1*安装: pip install scrapy-redis
4.2*配置redis.conf: 两个位置
bind 127.0.0.1 #注释掉
protected-mode no #yes改为no,关闭保护模式
4.3*启动redis服务: ./redis-server ./redis-conf
4.4*启动redis客户端: ./redis-cli
4.5*创建工程: scrapy startproject redisPro
4.6*创建CrawlSpider: scrapy genspider -t crawl qiubai www.qiushibaike.com/pic
4.7*导入RedisCrawlSpider让爬虫类继承该RedisCrawlSpider
4.8*将start_url修改成redis_key='xxx' #表示调度器队列名称
4.9*编写parse_item
4.10*将管道和调度器配置成基于scrapy-redis组件可以共享的:
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'ROBOTSTXT_OBEY = FalseITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400}# 使用scrapy-redis组件的去重队列DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"# 使用scrapy-redis组件自己的调度器SCHEDULER = "scrapy_redis.scheduler.Scheduler"# 是否允许暂停,如果某台机器宕机,从宕机地方继续爬取SCHEDULER_PERSIST = True#如果redis服务器不在本机,需要进行如下配置REDIS_HOST = '192.168.0.100' #本机的IPREDIS_PORT = 6379REDIS_PARAMS = {'password':'123456'}
4.11*执行爬虫文件,进入爬虫文件所在目录:scrapy runspider qiubai.py
4.12*启动redis客户端,将起始url扔进队列里:
lpush redis-key 起始url
lpush qiubai_spider https://www.qiushibaike.com/pic/
4.13*在其他电脑上同样执行:
scrapy runspider qiubai.py
lpush qiubai_spider https://www.qiushibaike.com/pic/
需求:爬取糗百全站的图片链接保存在redis,https://www.qiushibaike.com/pic/
====爬虫文件=========
# -*- coding: utf-8 -*-import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import Rulefrom scrapy_redis.spiders import RedisCrawlSpiderfrom redisPro.items import RedisproItemclass QiubaiSpider(RedisCrawlSpider): name = 'qiubai' # 表示跟start_url含义一样,表示是调度器队列的名称 redis_key = 'qiubai_spider' link = LinkExtractor(allow=r'/pic/page/\d+') rules = ( Rule(link, callback='parse_item', follow=True), ) def parse_item(self, response): div_list = response.xpath('//div[@id="content-left"]/div') for div in div_list: img_url = "https://"+div.xpath('.//div[@class="thumb"]/a/img/@src').extract_first() item = RedisproItem() item['img_url'] = img_url # 使用分布式的管道 yield item
====items===========
import scrapyclass RedisproItem(scrapy.Item): img_url = scrapy.Field()
====settings==========
# -*- coding: utf-8 -*-BOT_NAME = 'redisPro'SPIDER_MODULES = ['redisPro.spiders']NEWSPIDER_MODULE = 'redisPro.spiders'USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'ROBOTSTXT_OBEY = FalseITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400}# 使用scrapy-redis组件的去重队列DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"# 使用scrapy-redis组件自己的调度器SCHEDULER = "scrapy_redis.scheduler.Scheduler"# 是否允许暂停,如果某台机器宕机,从宕机地方继续爬取SCHEDULER_PERSIST = True#如果redis服务器不在本机,需要进行如下配置REDIS_HOST = '192.168.0.100' #本机的IPREDIS_PORT = 6379
=====pipelines========默认
class RedisproPipeline(object): def process_item(self, item, spider): return item
十七、scrapy框架--分布式爬虫RedisSpider
先写Spider的代码,再修改为RedisSpider
基于RedisSpider实现的分布式爬虫(网易新闻)
a)代码修改(爬虫类)
1.导包:from scrapy_redis.spiders import RedisSpider
2.将爬虫类的父类修改为RedisSpider
3.注释掉start_url,添加一个redis_key属性(调度器队列名称)
b)修改redis数据库配置文件redis.conf
1.#bind 127.0.0.1
2.protected-mode no
c)项目中settings.py进行配置
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400
}
# 使用scrapy-redis组件的去重队列
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 是否允许暂停,如果某台机器宕机,从宕机地方继续爬取
SCHEDULER_PERSIST = True
#如果redis服务器不在本机,需要进行如下配置
REDIS_HOST = '192.168.0.100' #本机的IP
REDIS_PORT = 6379
REDIS_PARAMS = {'password':'123456'}
d)启动redis服务端:
命令:redis-server 配置文件
e)执行爬虫文件:
进入到爬虫文件所在目录执行命令:scrapy runspider wangyi.py
f)向调度器的管道中扔一个起始url:
1.开启redis客户端
2.lpush wangyi https://news.163.com
十七、scrapy框架--selenium如何应用
selenium在scrapy中使用的原理分析:
- 用selenium在中间件中拦截下载器提交给引擎的response对象
- 对其进行篡改,用selenium发请求,将具有动态数据的响应对象替换原响应对象。
selenium在scrapy中的使用流程:
- 重写爬虫文件的构造方法,在该方法中使用selenium实例化一个浏览器对象(因为浏览器对象只需要被实例化一次)
- 重写爬虫文件的closed(self,spider)方法,在其内部关闭浏览器对象。该方法是在爬虫结束时被调用
- 重写下载中间件的process_response方法,让该方法对响应对象进行拦截,并篡改response中存储的页面数据
- 在配置文件中开启下载中间件
-爬虫文件:
# -*- coding: utf-8 -*-import scrapyfrom selenium import webdriverclass WangyiSpider(scrapy.Spider): name = 'wangyi' start_urls = ['https://news.163.com/'] def __init__(self): #实例化浏览器对象(实例化一次) self.bro = webdriver.Chrome(executable_path=r'C:\Users\Administrator\Downloads\chromedriver.exe') def closed(self, spider): #必须在整个爬虫结束后,关闭浏览器 self.bro.quit()
-中间件文件:
from scrapy.http import HtmlResponse #参数介绍: #拦截到响应对象(下载器传递给Spider的响应对象) #request:响应对象对应的请求对象 #response:拦截到的响应对象 #spider:爬虫文件中对应的爬虫类的实例 def process_response(self, request, response, spider): #响应对象中存储页面数据的篡改 if request.url in['http://news.163.com/domestic/','http://news.163.com/world/','http://news.163.com/air/','http://war.163.com/']: spider.bro.get(url=request.url) js = 'window.scrollTo(0,document.body.scrollHeight)' spider.bro.execute_script(js) time.sleep(2) #一定要给与浏览器一定的缓冲加载数据的时间 #页面数据就是包含了动态加载出来的新闻数据对应的页面数据 page_text = spider.bro.page_source #篡改响应对象 return HtmlResponse(url=spider.bro.current_url,body=page_text,encoding='utf-8',request=request) else: return response
-配置文件:
DOWNLOADER_MIDDLEWARES = { 'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,}
十八、scrapy框架--UA池、代理池
原理:
- 使用下载中间件处理请求,对请求设置随机的User-Agent 或者设置随机的代理。
- 目的在于防止爬取网站的反爬虫策略。
操作流程:
- 在下载中间件中拦截请求
- 将拦截到的请求的请求头信息中的UA进行篡改伪装 或者 将拦截到的请求的IP修改成某一代理IP
- 在配置文件中开启下载中间件
代码展示UA池:
#导包from scrapy.downloadermiddlewares.useragent import UserAgentMiddlewareimport random#UA池代码的编写(单独给UA池封装一个下载中间件的一个类)class RandomUserAgent(UserAgentMiddleware): def process_request(self, request, spider): #从列表中随机抽选出一个ua值 ua = random.choice(user_agent_list) #ua值进行当前拦截到请求的ua的写入操作 request.headers.setdefault('User-Agent',ua)user_agent_list = [ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 " "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1", "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 " "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 " "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 " "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 " "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 " "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 " "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 " "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 " "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"]
代码展示代理池:
#批量对拦截到的请求进行ip更换#单独封装下载中间件类class Proxy(object): def process_request(self, request, spider): #对拦截到请求的url进行判断(协议头到底是http还是https) #request.url返回值:http://www.xxx.com h = request.url.split(':')[0] #请求的协议头 if h == 'https': ip = random.choice(PROXY_https) request.meta['proxy'] = 'https://'+ip else: ip = random.choice(PROXY_http) request.meta['proxy'] = 'http://' + ip#可被选用的代理IPPROXY_http = [ '153.180.102.104:80', '195.208.131.189:56055',]PROXY_https = [ '120.83.49.90:9000', '95.189.112.214:35508',]
十九、scrapy框架--综合应用(爬网易新闻)
需求:
- 爬取网易新闻https://news.163.com/基于文字的新闻数据(“国内”、”国际“、”军事“、”航空“四个板块)
用到的知识点:
- 请求的手动发送
- 请求传参
- xpath解析
- UA池,代理池
- selenium在scrapy中的应用
代码展示
--爬虫类:
# -*- coding: utf-8 -*-import scrapyfrom selenium import webdriverfrom wangyiPro.items import WangyiproItemclass WangyiSpider(scrapy.Spider): name = 'wangyi' start_urls = ['https://news.163.com/'] def __init__(self): #实例化浏览器对象 self.bro = webdriver.Chrome(executable_path=r'C:\Users\Administrator\Downloads\chromedriver.exe') def closed(self, spider): self.bro.quit() def parse(self, response): #获取四个板块的a标签href属性 lis = response.xpath('//div[@class="ns_area list"]/ul/li') indexs = [3,4,6,7] li_list = [] #存储的四个板块的li标签对象 for index in indexs: li_list.append(lis[index]) #获取四个板块中的超链接和文字标题 for li in li_list: url = li.xpath('./a/@href').extract_first() title = li.xpath('./a/text()').extract_first() #对每一个板块的url发请求,获取页面数据(标题,缩略图,关键字,发布时间,url) yield scrapy.Request(url=url, callback=self.parse_second, meta={'title': title}) def parse_second(self, response): #这个div_list长度为0,xpath表达式没有问题,是动态加载的数据发送的请求拿不到。 #所以要用selenium去拿数据 div_list = response.xpath('//div[@class="data_row news_article clearfix "]') for div in div_list: #文章标题 head = div.xpath('.//div[@class="news_title"]/h3/a/text()').extract_first() #文章超链接 url = div.xpath('.//div[@class="news_title"]/h3/a/@href').extract_first() #缩略图 img_url = div.xpath('./a/img/@src').extract_first() #关键字 tag_list = div.xpath('.//div[@class="news_tag"]//text()').extract() tags = "".join([tag.strip() for tag in tag_list]) item = WangyiproItem() title = response.meta['title'] item['head'] = head item['url'] = url item['img_url'] = img_url item['tag'] = tags item['title'] = title # 对url发请求,获取页面中存储的新闻内容 yield scrapy.Request(url=url, callback=self.get_content, meta={'item': item}) def get_content(self, response): #获取请求传参的item item = response.meta['item'] #解析出新闻内容 content_list = response.xpath('//div[@class="post_text"]/p/text()').extract() content = "".join(content_list) item['content'] = content yield item
--items:
import scrapyclass WangyiproItem(scrapy.Item): head = scrapy.Field() url = scrapy.Field() img_url = scrapy.Field() tag = scrapy.Field() title = scrapy.Field() content = scrapy.Field()
--管道:
class WangyiproPipeline(object): def process_item(self, item, spider): print("title:"+item['title']+"|content:"+item['content']) return item
--中间件:
# -*- coding: utf-8 -*-from scrapy import signalsimport timefrom scrapy.http import HtmlResponsefrom scrapy.downloadermiddlewares.useragent import UserAgentMiddlewareimport random#单独给UA池封装一个下载中间件的类class RandomUserAgent(UserAgentMiddleware): def process_request(self, request, spider): ua = random.choice(user_agent_list) request.headers.setdefault('User-Agent',ua)#单独给代理池封装一个下载中间件的类class Proxy(object): def process_request(self, request, spider): #判断协议头是http还是https的 h = request.url.split(':')[0] if h=='https': ip = random.choice(proxies_https) request.meta['proxy'] = "https://"+ip else: ip = random.choice(proxies_http) request.meta['proxy'] = "http://" + ipclass WangyiproDownloaderMiddleware(object): def process_request(self, request, spider): return None #拦截响应:拦截下载器传给spider的响应对象 #request:响应对象所对应的请求对象 #response:拦截到的响应对象 #spider:爬虫文件中对应的爬虫类实例 def process_response(self, request, response, spider): #需要篡改response,使其包括动态加载的新闻内容 if request.url in ['http://news.163.com/domestic/','http://news.163.com/world/', 'http://war.163.com/','http://news.163.com/air/']: spider.bro.get(url=request.url) js = 'window.scrollTo(0, document.body.scrollHeight)' spider.bro.execute_script(js) time.sleep(1) page_text = spider.bro.page_source return HtmlResponse(url=spider.bro.current_url, body=page_text, encoding='utf-8', request=request) else: return response def process_exception(self, request, exception, spider): pass def spider_opened(self, spider): spider.logger.info('Spider opened: %s' % spider.name)user_agent_list = [ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 " "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1", "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 " "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 " "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 " "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 " "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 " "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 " "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 " "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 " "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"]proxies_http=[ '173.82.219.113:3128', '92.243.6.37:80',]proxies_https=[ '117.102.96.59:8080', '213.234.28.94:8080', '101.51.123.88:8080', '158.58.131.214:41258',]
--settings:
# -*- coding: utf-8 -*-BOT_NAME = 'wangyiPro'SPIDER_MODULES = ['wangyiPro.spiders']NEWSPIDER_MODULE = 'wangyiPro.spiders'# Obey robots.txt rulesROBOTSTXT_OBEY = FalseDOWNLOADER_MIDDLEWARES = { 'wangyiPro.middlewares.Proxy': 542, #代理池 'wangyiPro.middlewares.RandomUserAgent': 542, #UA池 'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543, #selenium拦截}
二十、scrapy框架--总结
1.接触过几种爬虫模块
- urllib
- requests
2.robots协议是什么
- requests模块没有使用硬性的语法对该协议进行生效
3.如何处理验证码
- 云打码平台
- 打码兔
4.掌握几种数据解析的方式
- 正则
- xpath
- bs4
5.如何爬取动态加载的页面数据
- selenium
- ajax请求
6.接触过哪些反爬机制?如何处理?
- robots协议
- UA
- 封IP
- 验证码
- 动态数据爬取
- 数据加密(挨着试解密方法)
- token(前台页面中查找)
7.在scrapy中接触过几种爬虫的类
- Spider
- CrawlSpider
- RedisCrawlSpider
- RedisSpider
8.如何实现分布式流程
- pip3 install scrapy-redis
- RedisCrawlSpider
- RedisSpider
来源:https://www.cnblogs.com/staff/p/10768497.html