框架:是一个集成了很多功能并且具有很强通用性的一个项目模板
如何学习框架?
scrapy:爬虫中封装好的 一个明星框架。功能:高性能的持久化存储,异步的数据下载,高性能的数据解析,分布式
环境的安装:
mac or linux : pip install scrapy
windows:
安装wheel:pip install wheel
下载twisted
下载地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
安装twisted: pip install Twisted-20.3.0-cp37-cp37m-win_amd64.whl
安装pywin32:pip install pywin32
安装scrapy:pip install scrapy
测试:在终端里输入import scrapy
,没有报错即表示安装成功!
scrapy使用流程
scrapy startproject xxxPro
,即可创建一个工程cd xxxPro
scrapy genspider spiderName www.xxx.com
scrapy crawl spiderName
需求:爬取6jianshi中作者的名称和段子内容
创建工程:scrapy startproject sixjianshiPro
进入到工程目录中:cd sixjianshiPro
在spiders子目录中创建一个爬虫文件:scrapy genspider sixjianshi www.xxx.com
在sixjianshi.py中编写代码
import scrapy
class SixjianshiSpider(scrapy.Spider):
name = 'sixjianshi'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://www.6jianshi.com/']
def parse(self, response):
# 解析:作者的名称+段子内容
# 注意:xpath中不能出现body
div_list = response.xpath('/html//div[3]/div/div/div[1]/div[2]/div[2]/div[@class="art-list"]')
for div in div_list:
# xpath返回的是列表,但是列表元素一定是Selector类型的对象
# extract():可以将Selector对象中的data参数存储的字符串提取出来
author = div.xpath('./div[@class="art-list-user"]/div[2]/a[1]/text()').extract_first()
if author == None:
continue
# 列表调用了extract(),则表示将列表中每一个Selector对象中data对应的字符串提取了出来
content = div.xpath('./div[@class="art-list-content"]/a//text()').extract()
content = ''.join(content)
print(author,":", content)
在settings.py中修改以下内容:
# UA伪装
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
# Obey robots.txt rules
# True:遵从robots协议
ROBOTSTXT_OBEY = False
# 显示指定的类型的日志信息
LOG_LEVEL = 'ERROR'
# 设置编码格式
FEED_EXPORT_ENCODING = 'utf-8-sig'
输入:scrapy crawl sixjianshi
,即可
运行结果如下:
基于终端指令:
指令:scrapy crawl xxx -o filePath
要求:只可以将parse方法的返回值存储到本地的文本文件中
注意:持久化存储对应的文本文件类型只可以为:'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle'
好处:简洁高效便捷
缺点:局限性比较强(数据只可以存储到指定后缀的文本文件中)
案例:
def parse(self, response):
# 解析:作者的名称+段子内容
div_list = response.xpath('/html//div[3]/div/div/div[1]/div[2]/div[2]/div[@class="art-list"]')
all_data = [] # 存储所有解析到的数据
for div in div_list:
# xpath返回的是列表,但是列表元素一定是Selector类型的对象
# extract():可以将Selector对象中的data参数存储的字符串提取出来
author = div.xpath('./div[@class="art-list-user"]/div[2]/a[1]/text()').extract_first()
if author == None:
continue
# 列表调用了extract(),则表示将列表中每一个Selector对象中data对应的字符串提取了出来
content = div.xpath('./div[@class="art-list-content"]/a//text()').extract()
content = ''.join(content)
# print(author,":", content)
dic = {
'author': author,
'content': content
}
all_data.append(dic)
return all_data
然后在终端进入到工程目录中:cd sixjianshiPro
基于管道:
编码流程:
好处:通用性强
案例
在items.py中创建两个属性
import scrapy
class SixjianshiproItem(scrapy.Item):
# define the fields for your item here like:
author = scrapy.Field()
content = scrapy.Field()
修改sixjianshi.py中的parse()方法,将解析数据存储并返回
import scrapy
from sixjianshiPro.items import SixjianshiproItem
def parse(self, response):
# 解析:作者的名称+段子内容
div_list = response.xpath('/html//div[3]/div/div/div[1]/div[2]/div[2]/div[@class="art-list"]')
for div in div_list:
# xpath返回的是列表,但是列表元素一定是Selector类型的对象
# extract():可以将Selector对象中的data参数存储的字符串提取出来
author = div.xpath('./div[@class="art-list-user"]/div[2]/a[1]/text()').extract_first()
if author == None:
continue
# 列表调用了extract(),则表示将列表中每一个Selector对象中data对应的字符串提取了出来
content = div.xpath('./div[@class="art-list-content"]/a//text()').extract()
content = ''.join(content)
# print(author,":", content)
# 实例化item类型的对象,并将解析的数据封装到了item中
item = SixjianshiproItem()
item['author'] = author
item['content'] = content
# 将item提交给了管道
yield item
在pipelines.py中修改SixjianshiproPipeline类
class SixjianshiproPipeline:
fp = None
def open_spider(self, spider):
"""重写父类方法:该方法旨在开始爬虫的时候被调用一次"""
print("开始爬虫......")
self.fp = open('./sixjianshi.txt', 'w', encoding='utf-8')
# 专门用来处理item类型对象
# 该方法可以接收爬虫文件提交过来的item对象
# 该方法每接收一个item就会被调用一次
def process_item(self, item, spider):
author = item['author']
content = item['content']
# 持久化存储
self.fp.write(author + ':' + content + '\n')
return item # 就会传递给下一个即将被执行的管道类
def close_spider(self, spider):
"""重写父类方法:该方法旨在结束爬虫的时候被调用一次"""
print("结束爬虫!!!")
self.fp.close()
在settings.py中开启管道
# 开启管道
ITEM_PIPELINES = {
# 300表示的是优先级,数据越小,优先级越高
'sixjianshiPro.pipelines.SixjianshiproPipeline': 300,
}
在终端中输入:scrapy crawl sixjianshi
,即可
运行结果:
面试题:将爬取到的数据一份存储到本地一份存储到数据库,如何实现?
管道文件中的一个管道类对应的是将数据存储到一种平台
爬虫文件的item只会给管道文件中第一个被执行的管道类接收
process_item 中的return item 表示将item传递给下一个即将被执行的管道类
代码实现
在win10 中开启mysql,我的用户名是root,没有设置密码
在mysql中创建sixjianshi
数据库和sixjianshi
表
-- 新建数据库语句
CREATE DATABASE sixjianshi;
-- 使用数据库
use sixjianshi;
-- 创建数据表
CREATE TABLE `sixjianshi` (
`author` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '作者',
`content` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '内容'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='sixjianshi';c
-- 查看表数据
select * from sixjianshi;
import pymysql
# 管道文件中一个管道类对应将一组数据存储到一个平台或者载体中
class mysqlPipeline:
conn = None
cursor = None
def open_spider(self, spider):
self.conn = pymysql.Connect(host='127.0.0.1',
port=3306,
user='root',
password='123456',
dt='sixjianshi',
charset='utf8')
def process_item(self, item, spider):
# 持久化存储到数据库
self.cursor = self.conn.cursor()
try:
self.cursor.execute('insert into sixjianshi values("%s","%s")' % (item['author'], item['content']))
self.conn.commit() # 数据提交
except Exception as e:
print(e)
self.conn.rollback() # 数据回滚
return item
def close_spider(self, spider):
self.cursor.close()
self.conn.close()
# 开启管道
ITEM_PIPELINES = {
# 300表示的是优先级,数据越小,优先级越高
'sixjianshiPro.pipelines.SixjianshiproPipeline': 300,
'sixjianshiPro.pipelines.mysqlPipeline': 301
}
在终端中输入:scrapy crawl sixjianshi
,即可
在mysql中查看结果,发现表中已经有数据了
大部分的网站展示的数据都进行了分页操作,那么将所有页码对应的页面数据进行爬取就是爬虫中的全站数据爬取。
基于scrapy进行全站数据爬取的实现方式:
需求:爬取站长素材网中的风景板块中的照片名称
代码实现
创建工程:scrapy startproject sc_chinazPro
进入到工程目录中:cd sc_chinazPro
在spiders子目录中创建一个爬虫文件:scrapy genspider sc_chinaz www.xxx.com
在sc_chinaz.py中编写代码
import scrapy
class ScChinazSpider(scrapy.Spider):
name = 'sc_chinaz'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://sc.chinaz.com/tupian/fengjing.html']
# 生成一个通用的url模板(不可变)
url = 'https://sc.chinaz.com/tupian/fengjing_%d.html'
page_num = 2
def parse(self, response):
div_list = response.xpath('//*[@id="container"]/div')
for div in div_list:
img_name = div.xpath('./p/a[1]/text()').extract_first()
print(img_name)
if self.page_num <= 4: # 爬取前4页数据
new_url = format(self.url%self.page_num)
print(new_url)
self.page_num += 1
# 手动请求发送:callback回调函数是专门用作于数据解析
yield scrapy.Request(url=new_url,callback=self.parse)
在终端中输入:scrapy crawl sc_chinaz
,即可
运行结果如下:
scrapy的五大核心组件的工作流程如下:
使用场景:如果爬取解析的数据不在同一张页面中。(深度爬取)
需求:爬取好猎头网站的岗位名称,岗位描述
代码实现
创建工程:scrapy startproject haolietouPro
进入到工程目录中:cd haolietouPro
在spiders子目录中创建一个爬虫文件:scrapy genspider haolietou www.xxx.com
在items.py中创建两个属性
import scrapy
class HaolietouproItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
job_name = scrapy.Field()
job_desc = scrapy.Field()
在settings.py中修改以下内容:
# UA伪装
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
# Obey robots.txt rules
# True:遵从robots协议
ROBOTSTXT_OBEY = False
# 显示指定的类型的日志信息
LOG_LEVEL = 'ERROR'
# 开启管道
ITEM_PIPELINES = {
'haolietouPro.pipelines.HaolietouproPipeline': 300,
}
在haolietou.py中编写代码
import scrapy
from haolietouPro.items import HaolietouproItem
class HaolietouSpider(scrapy.Spider):
name = 'haolietou'
# allowed_domains = ['www.xxx.com']
start_urls = ['http://www.haolietou.com/jobslist?key=建筑师']
# 通用的url
url = 'http://www.haolietou.com/jobs/jobs-list.php?key=建筑师&page=%d'
page_num = 2
# 回调函数接收items
def parse_detail(self, response):
item = response.meta['item']
job_desc = response.xpath('/html/body/div[2]/div[2]/div[1]/div/div[3]/div[2]/p//text()').extract()
job_desc = ''.join(job_desc)
item['job_desc'] = job_desc
# print(job_desc)
yield item
def parse(self, response):
li_list = response.xpath('//*[@id="form1"]/div/div[3]/div[2]/ul/li')
print("li_list length is : ", len(li_list))
for li in li_list:
item = HaolietouproItem()
job_name = li.xpath('./div[1]/p[1]/a/text()').extract_first()
item['job_name'] = job_name
# print(job_name)
detail_url = li.xpath('./div[1]/p[1]/a/@href').extract_first()
# print(detail_url)
# 对详情页发请求获取详情页的页面源码数据
# 手动请求的发送
# 请求传参:meta={},可以将meta字典传递给请求对应的回调函数
yield scrapy.Request(url=detail_url, callback=self.parse_detail, meta={'item': item})
# 分页操作
if self.page_num <= 2:
print("****************{}************".format(self.page_num))
new_url = format(self.url%self.page_num)
print(new_url)
self.page_num += 1
yield scrapy.Request(url=new_url, callback=self.parse)
在pipelines.py中编写代码
class HaolietouproPipeline:
def process_item(self, item, spider):
print(item)
return item
在终端中输入:scrapy crawl haolietou
,即可
需求:爬取站长素材中的高清图片
使用流程:
代码实现
创建工程:scrapy startproject imgsPro
进入到工程目录中:cd imgsPro
在spiders子目录中创建一个爬虫文件:scrapy genspider img www.xxx.com
在img.py中编写代码
import scrapy
from imgsPro.items import ImgsproItem
class ImgSpider(scrapy.Spider):
name = 'img'
# allowed_domains = ['www.xxx.vom']
start_urls = ['https://sc.chinaz.com/tupian/']
def parse(self, response):
div_list = response.xpath('//*[@id="container"]/div')
for div in div_list:
# 注意:使用伪属性
src = 'https:'+div.xpath('./div/a/img/@src2').extract_first()
# print(src)
item = ImgsproItem()
item['src'] = src
yield item
在items.py中创建两个属性
import scrapy
class ImgsproItem(scrapy.Item):
# define the fields for your item here like:
src = scrapy.Field()
在pipelines.py中编写代码
# ImagesPipeline专门用于文件下载的管道类,下载过程支持异步和多线程
from scrapy.pipelines.images import ImagesPipeline
import scrapy
# 新建类
class imgsPipeline(ImagesPipeline):
# 重新父类的三个方法
# 可以根据图片地址,进行图片数据的请求
def get_media_requests(self, item, info):
yield scrapy.Request(item['src'])
# 指定图片存储的路径
def file_path(self, request, response=None, info=None, *, item=None):
imgName = request.url.split('/')[-1] # 获取图片名称
return imgName
def item_completed(self, results, item, info):
return item # 返回给下一个即将执行的管道类
在settings.py中修改以下内容:
# UA伪装
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
# Obey robots.txt rules
# True:遵从robots协议
ROBOTSTXT_OBEY = False
# 显示指定的类型的日志信息
LOG_LEVEL = 'ERROR'
# 开启管道
ITEM_PIPELINES = {
'imgsPro.pipelines.imgsPipeline': 300,
}
# 指定图片存储的目录,如果文件不存在,会自动创建
IMAGES_STORE = './imgs__scChinaz'
在终端中输入:scrapy crawl img
,即可
下载中间件:
篡改响应数据,响应对象
需求:爬取网易新闻中的新闻数据(标题和内容)
代码实现
创建工程:scrapy startproject wangyiPro
进入到工程目录中:cd wangyiPro
在spiders子目录中创建一个爬虫文件:scrapy genspider wangyi www.xxx.com
在items.py中创建两个属性
import scrapy
class WangyiproItem(scrapy.Item):
# define the fields for your item here like:
title = scrapy.Field()
content = scrapy.Field()
在pipelines.py中编写代码
class WangyiproPipeline:
def process_item(self, item, spider):
print(item)
return item
在middlewares.py中编写代码
from scrapy.http import HtmlResponse
from time import sleep
class WangyiproDownloaderMiddleware:
# Not all methods need to be defined. If a method is not defined,
# scrapy acts as if the downloader middleware does not modify the
# passed objects.
def process_request(self, request, spider):
# Called for each request that goes through the downloader
# middleware.
# Must either:
# - return None: continue processing this request
# - or return a Response object
# - or return a Request object
# - or raise IgnoreRequest: process_exception() methods of
# installed downloader middleware will be called
return None
# 该方法拦截四大板块对应的响应对象,进行篡改
def process_response(self, request, response, spider): # spider:爬虫对象
bro = spider.bro # 获取了在爬虫类中定义的浏览器对象
# 挑选出指定的响应对象进行篡改
# 通过url指定request
# 通过request指定response
if request.url in spider.models_urls:
bro.get(request.url) # 获取四大板块对应的url进行请求
sleep(3)
page_text = bro.page_source # 包含了动态加载的新闻数据
# response # 四大板块对应的响应对象
# 针对定位的这些response进行篡改
# 实例化一个新的响应对象(符合需求:包含动态加载出的新闻数据),替代原来旧的响应对象
# 如何获取动态加载出的新闻数据
# 基于selenium便捷的获取动态加载数据
new_response = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request)
return new_response
else:
# response :其他请求对应的响应对象
return response
def process_exception(self, request, exception, spider):
# Called when a download handler or a process_request()
# (from other downloader middleware) raises an exception.
# Must either:
# - return None: continue processing this exception
# - return a Response object: stops process_exception() chain
# - return a Request object: stops process_exception() chain
pass
在settings.py中修改以下内容:
# UA伪装
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
# Obey robots.txt rules
# True:遵从robots协议
ROBOTSTXT_OBEY = False
# 显示指定的类型的日志信息
LOG_LEVEL = 'ERROR'
# 开启下载中间件
DOWNLOADER_MIDDLEWARES = {
'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,
}
# 开启管道
ITEM_PIPELINES = {
'wangyiPro.pipelines.WangyiproPipeline': 300,
}
在wangyi.py中编写代码
import scrapy
from selenium import webdriver
from wangyiPro.items import WangyiproItem
class WangyiSpider(scrapy.Spider):
name = 'wangyi'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://news.163.com/']
models_urls = [] # 存储四个板块对应的详情页的url
def __init__(self):
self.bro = webdriver.Chrome('D:\PythonCode\待整理\爬虫相关\第8章:scrapy框架\chromedriver.exe')
# 解析四大板块对应详情页的url
def parse(self, response):
li_list = response.xpath('//*[@id="index2016_wrap"]/div[3]/div[2]/div[2]/div[2]/div/ul/li')
print("li list length is ",len(li_list))
alist = [2,3,5,6]
for index in alist:
model_url = li_list[index].xpath('./a/@href').extract_first()
self.models_urls.append(model_url)
# 依次对每一个板块对应的页面进行请求
for url in self.models_urls: # 对每一个板块的url进行请求发送
yield scrapy.Request(url, callback=self.parse_model)
# 每个板块对应的新闻标题相关的内容都是动态加载
def parse_model(self, response):
"""解析每一个板块中对应新闻的标题和新闻详情页的url"""
div_list = response.xpath('/html/body/div/div[3]/div[4]/div[1]/div[1]/div/ul/li/div/div')
for div in div_list:
title = div.xpath('./div/div[1]/h3/a/text()').extract_first()
new_detail_url = div.xpath('./div/div[1]/h3/a/@href').extract_first()
item = WangyiproItem()
item['title'] = title
# 对新闻详情页的url发起请求
yield scrapy.Request(url=new_detail_url,callback=self.parse_detail,meta={'item':item})
def parse_detail(self,response): # 解析新闻内容
content = response.xpath('//*[@id="content"]//text()').extract()
content = ''.join(content)
item = response.meta['item']
item['content'] = content
yield item
def closed(self,spider):
self.bro.quit()
在终端中输入:scrapy crawl wangyi
,即可
需求:爬取福州便民热线网站中的编号,新闻标题,及详情页的新闻内容和编号
分析:爬取的数据没有在同一张页面中
让链接提取器提取所有的新闻详情页的链接
代码实现
创建工程:scrapy startproject fuzhouPro
进入到工程目录中:cd fuzhouPro
创建一个爬虫文件:scrapy genspider -t crawl fuzhou www.xxx.com
在fuzhou.py中编写代码
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from fuzhouPro.items import FuzhouproItem, DetailItem
class FuzhouSpider(CrawlSpider):
name = 'fuzhou'
# allowed_domains = ['www.xxx.com']
start_urls = ['http://fz12345.fuzhou.gov.cn/webEntAppealList.jsp?listType=1&pageSize=10&cp=1']
# 链接提取器:根据指定规则(allow='正则')进行指定链接的提取
link = LinkExtractor(allow=r'pageSize=10&cp=\d+')
link_detail = LinkExtractor(allow=r'callId=\S\S\d+&from=webIndex')
rules = (
# 规则提取器:将链接提取器提取到链接进行指定规则(callback)的解析操作
Rule(link, callback='parse_item', follow=True),
# follow=True:可以将链接提取器 继续作用到 链接提取器提取到的链接 所对应的页面中
Rule(link_detail, callback='parse_detail')
)
# 解析新闻编号和新闻的标题
# 如下两个解析方法中是不可以实现请求传参!!!
# 无法将两个解析方法解析的数据存储到同一个item中,可以依次存储到两个item
def parse_item(self, response):
li_list = response.xpath('//*[@id="frame_container"]/div/div[2]/div/div[2]/div[1]/ul/li')
for li in li_list:
new_num = li.xpath('./div/span[1]//text()').extract()
new_num = ''.join(new_num)
new_num = new_num.split(':')[-1].strip()
new_title = li.xpath('./a/b/text()').extract_first()
# print(new_num, new_title)
item = FuzhouproItem()
item['title'] = new_title
item['new_num'] = new_num
yield item
# 解析新闻内容和新闻编号
def parse_detail(self, response):
# 注意:xpath表达式中不可以出现tbody标签,直接删除‘tbody’即可
new_id = response.xpath('//*[@id="appeal-nature-0"]//tr[1]/td[2]/p/text()').extract_first()
new_content = response.xpath('//*[@id="appeal-nature-0"]//tr[3]/td[2]/p//text()').extract()
new_content = ''.join(new_content)
# print(new_id, new_content)
item = DetailItem()
item['new_id'] = new_id
item['content'] = new_content
yield item
在items.py中创建属性
import scrapy
class FuzhouproItem(scrapy.Item):
# define the fields for your item here like:
title = scrapy.Field()
new_num = scrapy.Field()
class DetailItem(scrapy.Item):
new_id = scrapy.Field()
content = scrapy.Field()
在pipelines.py中编写代码
class FuzhouproPipeline:
def process_item(self, item, spider):
# 如何判断item的类型
# 将数据写入数据库时,如何保证数据的一致性:通过new_id和new_num进行关联
if item.__class__.__name__ == 'DetailItem':
print(item['new_id'], item['content'])
else:
print(item['new_num'], item['title'])
return item
在settings.py中修改以下内容:
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
# Obey robots.txt rules
# True:遵从robots协议
ROBOTSTXT_OBEY = False
# 显示指定的类型的日志信息
LOG_LEVEL = 'ERROR'
# 开启管道
ITEM_PIPELINES = {
'fuzhouPro.pipelines.FuzhouproPipeline': 300,
}
在终端中输入:scrapy crawl fuzhou
,即可
概念:需要搭建一个分布式的集群,让其对一组资源进行分布联合爬取
作用:提升爬取数据的效率
如何实现分布式?
安装一个scrapy-Redis的组件:pip install scrapy-redis
原生的scrapy是不可以实现分布式爬虫,必须要让scrapy结合着scrapy-redis组件一起实现分布式爬虫
为什么原生的scrapy不可以实现分布式爬虫?
scrapy-redis组件作用
实现流程
创建一个工程
创建一个基于CrawlSpider的爬虫文件
修改当前的爬虫文件
修改配置文件settings
指定使用可以被共享的管道:
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400,
}
指定调度器:
# 增加一个去重容器类的配置,作用使用Redis的set集合来存储请求的指纹数据,从而实现请求去重的持久化
DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
# 使用scrapy-redis组件自己的调度器
SCHEDULER = 'scrapy_redis.scheduler.Scheduler'
# 配置调度器是否要持久化,也就是当爬虫结束了,要不要清空Redis中请求队列和去重指纹的set。如果是True
SCHEDULER_PERSIST = True
指定redis服务器:
REDIS_HOST = 'redis远程服务器的ip地址'
REDIS_PORT = 6379
redis相关操作配置:
redis.conf
redis.windows.conf
执行工程:
scrapy runspider xxx.py
向调度器的队列中放入一个起始的url:
lpush xxx www.xxx.com
爬取到的数据存在了redis的proName:items这个数据结构中
需求:爬取福州便民热线网站中的编号,新闻标题
代码实现
创建一个工程:scrapy startproject fbsPro
创建一个基于CrawlSpider的爬虫文件:
scrapy genspider -t crawl fbs www.xxx.com
编写爬虫文件fbs.py
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_redis.spiders import RedisCrawlSpider
from fbsPro.items import FbsproItem
class FbsSpider(RedisCrawlSpider):
name = 'fbs'
# allowed_domains = ['www.xxx.com']
# start_urls = ['http://www.xxx.com/']
redis_key = 'fuzhou'
rules = (
Rule(LinkExtractor(allow=r'pageSize=10&cp=\d+'), callback='parse_item', follow=True),
)
def parse_item(self, response):
li_list = response.xpath('//*[@id="frame_container"]/div/div[2]/div/div[2]/div[1]/ul/li')
for li in li_list:
new_num = li.xpath('./div/span[1]//text()').extract()
new_num = ''.join(new_num)
new_num = new_num.split(':')[-1].strip()
new_title = li.xpath('./a/b/text()').extract_first()
item = FbsproItem()
item['title'] = new_title
item['new_num'] = new_num
yield item
在items.py中创建两个属性
import scrapy
class FbsproItem(scrapy.Item):
# define the fields for your item here like:
title = scrapy.Field()
new_num = scrapy.Field()
修改配置文件settings
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
# Obey robots.txt rules
# True:遵从robots协议
ROBOTSTXT_OBEY = False
# 显示指定的类型的日志信息
LOG_LEVEL = 'ERROR'
# 指定管道
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400,
}
# 指定调度器
# 增加一个去重容器类的配置,作用使用Redis的set集合来存储请求的指纹数据,从而实现请求去重的持久化
DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
# 使用scrapy-redis组件自己的调度器
SCHEDULER = 'scrapy_redis.scheduler.Scheduler'
# 配置调度器是否要持久化,也就是当爬虫结束了,要不要清空Redis中请求队列和去重指纹的set。如果是True,则只爬没有爬取的数据,实现增量式爬取
SCHEDULER_PERSIST = True
# 指定redis服务器
REDIS_HOST = '127.0.0.1' # 最好写成redis远程服务器的ip地址
REDIS_PORT = 6379
执行工程:
进入spider文件目录下,输入scrapy runspider fbs.py
向调度器的队列中放入一个起始的url:
在redis客户端中输入lpush fuzhou http://fz12345.fuzhou.gov.cn/webEntAppealList.jsp?listType=1&pageSize=10&cp=1
在redis客户端中查看爬取到的数据情况
爬取的存在了redis的fbs:items
这个数据结构中
redis中相关命令如下: