目录
目标网址:http://www.daomubiji.com/
1.Python中os.mkdir()与os.makedirs()的区别及用法
目标网址:http://www.daomubiji.com/
数据内容:
- 书的名称
- 章节名称
- 文本内容
scrapy项目
- 创建项目
- 创建爬虫
- 定义item
- 编写spider
- 定义初始请求
- 解析响应内容
- 定义管道
- 运行项目
实现分布式的步骤
- 安装:pip install scrapy-redis.
- 将爬虫主文件中继承自Scrapy中的scrapy.Spider或CrawlSpider替换成Scrapy-Redis的RedisSpider或 RedisCrawlSpider。
- 初始的start_urls改为redis_kev。
- 在settings.py文件中修改Scrapy自带的调度器类和去重类为Scrapy-Redis提供的类。
# 调度器 使用scrapy-redis SCHEDULER = "scrapy redis.scheduler.Scheduler" # 去重的 使用scrapy-redis DUPEFILTER_CLASS = "scrapy_redis.dupeŨ lter.RFPDupeFilter" # 断点续爬 请求持久化 SCHEDULER_PERSIST = True
- setting里打开管道
ITEM_PIPELINES = { 'dmbj.pipelines.DmbjPipeline': 300, # 数据保存到redis 'scrapy_redis.pipelines.RedisPipeline': 400, }
代码的实现
Spider.py
import scrapy
from dmbj.items import DmbjItem
import re, os
from copy import deepcopy
# redis要导入的内容
from scrapy_redis.spiders import RedisSpider
class SpiderSpider(RedisSpider):
name = 'spider'
# allowed_domains = ['daomubiji.com']
# start_urls = ['http://daomubiji.com/']
# redis要写成这个
redis_key = 'dmbj'
def parse(self, response):
"""一级爬取"""
text = response.text
html = response.xpath('//ul[@class="sub-menu"]//a')
for i in html:
item = DmbjItem()
# 书名
item['book_name'] = i.xpath('./text()').get()
# 连接
book_link = i.xpath('./@href').get()
# 书名记得替换特殊字符,预防引发爬取时变成转义字符
book_path = re.sub(r'[\\\/\:\*\?\"\<\>\|]', '_', item['book_name'])
# 书本存放路径
dirpath = f'./noval/{book_path}'
# 每本书一个文件见存放
if not os.path.exists(dirpath):
os.makedirs(dirpath)
# 发送请求到下一级爬取 记得用深拷贝
yield scrapy.Request(url=book_link, callback=self.second_parse, meta={'item': deepcopy(item)})
def second_parse(self, response):
"""二级爬取"""
# 接受上一个参数的传参
item = response.meta.get('item')
article_list = response.xpath('//div[@class="excerpts"]//article')
for i in article_list:
# 章节名字
item['chapter_title'] = i.xpath('./a/text()').get()
# 章节链接
content_link = i.xpath('./a/@href').get()
yield scrapy.Request(url=content_link, callback=self.third_parse, meta={'item': deepcopy(item)})
def third_parse(self,response):
"""三级爬取"""
item = response.meta.get('item')
content = response.xpath('//article[@class="article-content"]/p/text()').getall()
# 每个段落用换行代替
item['content'] = '\n'.join(content)
yield item
设计知识点:
1.Python中os.mkdir()与os.makedirs()的区别及用法
首先说os.mkdir(path),他的功能是一级一级的创建目录,前提是前面的目录已存在,如果不存在会报异常。
import os
os.mkdir('d:\hello') # 正常
os.mkdir('d:\hello\hi') # 正常
# 如果d:\hello目录不存在
# 则os.mkdir('d:\hello\hi')执行失败
然后是os.makedirs(path),可以一次创建多级目录,中间目录不存在也能正常的创建。
import os
os.makedirs('d:\hello') # 正常
os.makedirs('d:\hello\hi') # 正常
# 如果d:\hello目录不存在
# 则os.makedirs('d:\hello\hi') # 仍然正常
2.copy与deepcopy的区别与用法
参考这篇博客:copy与deepcopy的区别
3.meta传参
上一级爬虫发送
yield scrapy.Request(url=content_link, callback=self.third_parse, meta={'item': deepcopy(item)})
下级爬虫接受
item = response.meta.get('item')
4.列表合并
每个段落用换行合并
# 每个段落用换行代替
item['content'] = '\n'.join(content)
5.正则表达式替换
# 书名记得替换特殊字符,预防引发爬取时变成转义字符
book_path = re.sub(r'[\\\/\:\*\?\"\<\>\|]', '_', item['book_name'])
items.py
import scrapy
class DmbjItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 书名
book_name = scrapy.Field()
# 章节名
chapter_title = scrapy.Field()
# 章节内容
content = scrapy.Field()
管道保存pipeline.py
import re
class DmbjPipeline:
def open_spider(self,spider):
pass
def process_item(self, item, spider):
# 替换字符
self.book_name = re.sub(r'[\\\/\:\*\?\"\<\>\|]', '_', item['book_name'])
self.chapter_title = re.sub(r'[\\\/\:\*\?\"\<\>\|]', '_', item['chapter_title'])
# 创建文件
self.f = open(f'./noval/{self.book_name}/{self.chapter_title}.txt','w',encoding='utf-8')
# 写入
self.f.write(item["content"])
return item
def close_spider(self,spider):
self.f.close()
settings.py
# 调度器 使用scrapy_redis
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 去重 使用scrapy_redis
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 断点续爬 请求持久化
SCHEDULER_PERSIST = True
ITEM_PIPELINES = {
'dmbj.pipelines.DmbjPipeline': 300,
# 数据保存到redis
'scrapy_redis.pipelines.RedisPipeline': 400,
}
启动
需要打开redis.py,而且创建redis列表,和起始地址
然后开启多个终端模拟多台电脑运行即可
注意事项:
python3.10之后要做出的修改,在spiders.py的源代码里面
from collections import Iterable 为 from collections.abc import Iterable