chapter_Content = “”.join(“”.join(text.xpath(“.//dd[@id=‘contents’]//*/text()”)))

pattern = re.compile(‘dd id=“contents”.?>(.?)’)

match = pattern.search(ht)

chapter_Content = “”.join(match.group(1).replace(" “,”").split()) if match else “爬取错误”

‘’’

result,number = re.subn(“<.*?>”,“”,str(soup.find(“dd”,id=‘contents’)))

chapter_Content = “”.join(result.split())

print(len(chapter_Content))

novel_ID = response.url.split(“/”)[-2]

return ChapterItem(

chapter_Url = response.url,

_id=int(response.url.split(“/”)[-1].split(“.”)[0]),

novel_Name=novel_Name,

chapter_Name=chapter_Name,

chapter_Content= chapter_Content,

novel_ID = novel_ID,

is_Error = len(chapter_Content) < 3000

)

3.scrapy中实现增量式爬取的几种方式
1.缓存

通过开启缓存,将每个请求缓存至本地,下次爬取时,scrapy会优先从本地缓存中获得response,这种模式下,再次请求已爬取的网页不用从网络中获得响应,所以不受带宽影响,对服务器也不会造成额外的压力,但是无法获取网页变化的内容,速度也没有第二种方式快,而且缓存的文件会占用比较大的内存,在setting.py的以下注释用于设置缓存

#HTTPCACHE_ENABLED = True

#HTTPCACHE_EXPIRATION_SECS = 0

#HTTPCACHE_DIR = ‘httpcache’

#HTTPCACHE_IGNORE_HTTP_CODES = []

#HTTPCACHE_STORAGE = ‘scrapy.extensions.httpcache.FilesystemCacheStorage’

这种方式比较适合内存比较大的主机使用,我的阿里云是最低配的,在爬取半个晚上接近27W个章节信息后,内存就用完了

2.对item实现去重

本文开头的第一种方式,实现方法是在pipelines.py中进行设置,即在持久化数据之前判断数据是否已经存在,这里我用的是mongodb持久化数据,逻辑如下

#处理书信息

def process_BookItem(self,item):

bookItemDick = dict(item)

try:

self.bookColl.insert(bookItemDick)

print(“插入小说《%s》的所有信息”%item[“novel_Name”])

except Exception:

print(“小说《%s》已经存在”%item[“novel_Name”])

#处理每个章节

def process_ChapterItem(self,item):

try:

self.contentColl.insert(dict(item))

print(‘插入小说《%s》的章节"%s"’%(item[‘novel_Name’],item[‘chapter_Name’]))

except Exception:

print(“%s存在了,跳过”%item[“chapter_Name”])

def process_item(self, item, spider):

‘’’

if isinstance(item,ChaptersItem):

self.process_ChaptersItem(item)

‘’’

if isinstance(item,BookItem):

self.process_BookItem(item)

if isinstance(item,ChapterItem):

self.process_ChapterItem(item)

return item

两种方法判断mongodb中是否存在已有的数据,一是先查询后插入,二是先设置唯一索引或者主键再直接插入,由于mongodb的特点是插入块,查询慢,所以这里直接插入,需要将唯一信息设置为”_id”列,或者设置为唯一索引,在mongodb中设置方法如下

db.集合名.ensureIndex({“要设置索引的列名”:1},{“unique”:1})

需要用什么信息实现去重,就将什么信息设置为唯一索引即可(小说章节信息由于数据量比较大,用于查询的列最好设置索引,要不然会非常慢),这种方法对于服务器的压力太大,而且速度比较慢,我用的是第二种方法,即对已爬取的url进行去重

3.对url实现去重

对我而言,这种方法是最好的方法,因为速度快,对网站服务器的压力也比较小,不过网上的资料比较少,后来在文档中发现scrapy可以自定义下载中间件,才解决了这个问题

文档原文如下

class scrapy.downloadermiddlewares.DownloaderMiddleware

process_request(request, spider) 当每个request通过下载中间件时,该方法被调用。

process_request() 必须返回其中之一: 返回 None 、返回一个 Response 对象、返回一个 Request

对象或raise IgnoreRequest 。

如果其返回 None ,Scrapy将继续处理该request,执行其他的中间件的相应方法,直到合适的下载器处理函数(download

handler)被调用, 该request被执行(其response被下载)。

如果其返回 Response 对象,Scrapy将不会调用 任何 其他的 process_request() 或

process_exception() 方法,或相应地下载函数; 其将返回该response。 已安装的中间件的

process_response() 方法则会在每个response返回时被调用。

如果其返回 Request 对象,Scrapy则停止调用

process_request方法并重新调度返回的request。当新返回的request被执行后,

相应地中间件链将会根据下载的response被调用。

如果其raise一个 IgnoreRequest 异常,则安装的下载中间件的 process_exception()

方法会被调用。如果没有任何一个方法处理该异常,

则request的errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常,

则该异常被忽略且不记录(不同于其他异常那样)。

所以只需要在process_request中实现去重的逻辑就可以了,代码如下

class UrlFilter(object):

#初始化过滤器(使用mongodb过滤)

def init(self):

self.settings = get_project_settings()

self.client = pymongo.MongoClient(

host = self.settings[‘MONGO_HOST’],

port = self.settings[‘MONGO_PORT’])

self.db = self.client[self.settings[‘MONGO_DB’]]

self.bookColl = self.db[self.settings[‘MONGO_BOOK_COLL’]]

#self.chapterColl = self.db[self.settings[‘MONGO_CHAPTER_COLL’]]

self.contentColl = self.db[self.settings[‘MONGO_CONTENT_COLL’]]

def process_request(self,request,spider):

if (self.bookColl.count({“novel_Url”:request.url}) > 0) or (self.contentColl.count({“chapter_Url”:request.url}) > 0):

return http.Response(url=request.url,body=None)

但是又会有一个问题,就是有可能下次开启时,种子url已经被爬取过了,爬虫会直接关闭,后来想到一个笨方法解决了这个问题,即在pipeline.py里的open_spider方法中再爬虫开启时删除对种子url的缓存

def open_spider(self,spider):

self.bookColl.remove({“novel_Url”:“http://www.23us.so/xiaoshuo/414.html”})

3.结果

小说信息

这里写图片描述

最后

🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Python爬虫全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:python)
img

程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

因此收集整理了一份《2024年Python爬虫全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:python)
[外链图片转存中…(img-6cyQFj6R-1711069160161)]

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐