增量式爬虫详细讲解,附案例分析 您所在的位置:网站首页 网络爬虫采用的是哪种算法技术 增量式爬虫详细讲解,附案例分析

增量式爬虫详细讲解,附案例分析

2024-07-13 13:47| 来源: 网络整理| 查看: 265

爬虫之增量式爬虫 一:什么是增量式爬虫

爬虫策略:

广度优先

比如我们平时通过分页爬取的方式

深度优先

对于深度优先来说,必须使用增量爬虫

增量的含义就是不断的增加,它通过我们提供的一个入口,不断的去爬取数据,从而达到使数据不断增加的目的。

在我们平时的爬取过程中,会遇到一些问题:

页面内容发生变更

有些数据需要我们持久的慢慢的进行爬取

如果我们的爬虫页面发生了变化,也能够持续稳定的将变化的数据更新到数据库中,同时又能保证爬虫再执行的过程中,数据量也在不停的增加,这样的爬虫就叫增量爬虫。

增量爬虫的核心:去重,如果不进行去重会进入死链状态,详细可参考下面的案例

增量爬虫如何去重:

在请求发起之前,判断此url是否爬取过在解析内容之后,判断该内容是否被爬取过在保存数据时,判断将要写入的内容是否存在于存储介质中 二:案例演示

案例目标:猫眼电影-影人抓取,爬取尽可能多的演员信息

案例介绍:此案例的爬虫策略和之前博客中发布的爬虫案例的都不同,这个案例并没有分类页(列表页),没有一个完整的爬虫入口,只能够请求一个或多个演员,然后找到相关影人,从而尽可能多的获取。

编写调度器即定义一个队列进行存取 from lxml import etree def main(): # 取出队列中的url,进行请求 url = scheduler.get() # 定义downloader下载器处理取出的请求 response = downloader(url) html = etree.HTML(response) if __name__ == "__main__": # 初始化任务池,尽可能多的放进一些url,这样搜索面才会大 start_urls = [ "https://maoyan.com/films/celebrity/789", "https://maoyan.com/films/celebrity/29480", "https://maoyan.com/films/celebrity/29481", "https://maoyan.com/films/celebrity/28625", "https://maoyan.com/films/celebrity/5392", ] # 定义一个调度器队列 scheduler = Queue() # 将任务池中的url放入到调度器的队列中 for url in start_urls: scheduler.put(url) # 定义一个main函数用来执行其它组件的功能 main() 编写下载器即downloader函数

作用:将从调度器拿到的url进行请求,并将请求后的响应返回给爬虫组件spider

def downloader(url): response = requests.get(url) return response.text 编写爬虫组件spider函数

作用:处理下载器发送过来的响应,获取数据

def spider(html): # 创建一个item字典 item = {} # 获取影人中文名字 zh_name = html.xpath('//p[@class="china-name cele-name"]/text()')[0] # 获取影人英文名字 en_name = html.xpath('//p[@class="eng-name cele-name"]/text()')[0] # 获取影人职业 profession = html.xpath('//span[@class="profession"]/text()')[0] # 获取影人生日 birthday = html.xpath('//span[@class="birthday"]/text()')[0] # 获取影人身高 height = html.xpath('//span[@class="height"]/text()')[0] # 获取影人作品 display = html.xpath('//p[@class="movie-name"]/text()') # 保存数据至item item["zh_name"] = zh_name item["en_name"] = en_name item["profession"] = profession item["birthday"] = birthday item["height"] = height item["display"] = display

这里出现了一个问题:如果影人没有英文名或是缺少了其它一些信息,那么我们就会得到一个空列表,之后用索引取值时就会报错,为了解决这个问题,我们再编写一个提取数据的函数,使得如果得到一个空列表则返回一个None。

编写extract_first提取函数

def extract_first(text): if text: return text[0] return None

修改spider函数

def spider(html): # 创建一个item字典 item = {} # ------------------------------------------------------------------------------------- # 获取影人中文名字 zh_name = extract_first(html.xpath('//p[@class="china-name cele-name"]/text()')) # 获取影人英文名字 en_name = extract_first(html.xpath('//p[@class="eng-name cele-name"]/text()')) # 获取影人职业 profession = extract_first(html.xpath('//span[@class="profession"]/text()')) # 获取影人生日 birthday = extract_first(html.xpath('//span[@class="birthday"]/text()')) # 获取影人身高 height = extract_first(html.xpath('//span[@class="height"]/text()')) # 获取影人作品 display = html.xpath('//p[@class="movie-name"]/text()') # ------------------------------------------------------------------------------------- # 保存数据至item item["zh_name"] = zh_name item["en_name"] = en_name item["profession"] = profession item["birthday"] = birthday item["height"] = height item["display"] = display return item 编写管道组件pipeline函数

作用:将获取到的数据保存到mongodb数据库中

def pipeline(item,url): # 链接mongodb client = pymongo.Client(host="127.0.0.1",port=27017) # 进入数据库 db = client["first_text"] # 进入集合 col = db["actor"] # 插入数据,需进行判重 # 判重思路:一般都是通过url进行判重,在这里我们可以将主键id替换为我们 加密过的请求url,然后进行判重,如果此url已请求过则不再添加它的返回值 item["_id"] = encryption(url) col.update({"_id":url},{"$set":item},True)

编写加密函数

def encryption(url): # 初始化md5对象 md5 = hashlib.md5() # 加密 md5.update(url.encode("utf-8")) # 返回加密后的数据 return md5.hexdigest()

获取相关影人的url,并将获取到的url放入到队列中,不断进行请求,直到队列为空为止

对main函数进行修改

def main(): # --------------------------------------------------------------------------------- # 使用死循环一直从队列中取出URL,在队列为空时退出循环 while True: if scheduler.empty(): break # ---------------------------------------------------------------------------------- # 取出队列中的url,进行请求 url = scheduler.get() # 定义downloader下载器处理取出的请求 response = downloader(url) html = etree.HTML(response) # 定义spider爬虫组件接受下载器的请求并处理数据 item = spider(html) # 定义pipeline管道对数据进行保存 pipeline(item,url) # 获取相关影人的url related_url_list = html.xpath('//div[@class="rel-item"]/a/@href') for related_url in related_url_list: full_related_url = "https://maoyan.com" + related_url scheduler.put(full_related_url)

​ 这里又出现了一个问题,假设某演员的相关影人中有他的朋友,当我们进入到他朋友的页面中后,如果他朋友的相关影人也有他那么就会不断的进行请求,就会形成一个死循环,所以我们要进行队列的去重操作。

定义去重函数url_seen,相当于Scrapy-Redis的去重器

去重器原理:将每一次取出的url放入到redis数据库中集合中,以放入时返回的值为判断条件,如果返回值为0则说明有重复的url,我们就不再进行请求,如果返回值为1,说明这个url没有被请求过,我们就可以对这个url发起请求

def url_seen(url): """去重函数""" # 链接Redis数据库 r = redis.Redis(host="127.0.0.1",port=6379) # 判重 res = r.sadd("maoyan:actor_url",encryption(url)) return res == 0 修改main函数,对队列中的url进行去重处理 def main(): # 使用死循环一直从队列中取出URL,在队列为空时退出循环 while True: if scheduler.empty(): break # 取出队列中的url,进行请求 url = scheduler.get() # 在此处判断,即每一次从队列中取出url时,先去判断此url是否已经存在 # ----------------------------------------------------------- if not url_seen(url): # ----------------------------------------------------------- # 定义downloader下载器处理取出的请求 response = downloader(url) html = etree.HTML(response) # 定义spider爬虫组件接受下载器的请求并处理数据 item = spider(html) # 定义pipeline管道对数据进行保存 pipeline(item,url) # 获取相关影人的url related_url_list = html.xpath('//div[@class="rel-item"]/a/@href') for related_url in related_url_list: full_related_url = "https://maoyan.com" + related_url # 将获取到的url放入到队列中 scheduler.put(full_related_url)

至此增量式爬虫编写完毕,在此提醒:运行文件的时候可别忘了打开mongodb和redis数据库的服务器,不然会报请求被拒绝的错误哦!

下面附完整无注释代码:

import redis import hashlib import pymongo import requests from lxml import etree from queue import Queue def extract_first(text): if text: return text[0] return None def encryption(url): md5 = hashlib.md5() md5.update(url.encode("utf-8")) return md5.hexdigest() def pipeline(item,url): client = pymongo.MongoClient(host="127.0.0.1",port=27017) db = client["first_text"] col = db["actor"] item["_id"] = encryption(url) col.update_many({"_id":item["_id"]},{"$set":item},True) def spider(html): item = {} zh_name = extract_first(html.xpath('//p[@class="china-name cele-name"]/text()')) en_name = extract_first(html.xpath('//p[@class="eng-name cele-name"]/text()')) profession = extract_first(html.xpath('//span[@class="profession"]/text()')) birthday = extract_first(html.xpath('//span[@class="birthday"]/text()')) height = extract_first(html.xpath('//span[@class="height"]/text()')) display = html.xpath('//p[@class="movie-name"]/text()') item["zh_name"] = zh_name item["en_name"] = en_name item["profession"] = profession item["birthday"] = birthday item["height"] = height item["display"] = display return item def downloader(url): response = requests.get(url) return response.text def url_seen(url): r = redis.Redis(host="127.0.0.1",port=6379) res = r.sadd("maoyan:actor_url",encryption(url)) return res == 0 def main(): while True: if scheduler.empty(): break url = scheduler.get() if not url_seen(url): response = downloader(url) html = etree.HTML(response) item = spider(html) pipeline(item,url) related_url_list = html.xpath('//div[@class="rel-item"]/a/@href') for related_url in related_url_list: full_related_url = "https://maoyan.com" + related_url scheduler.put(full_related_url) if __name__ == "__main__": start_urls = [ "https://maoyan.com/films/celebrity/789", "https://maoyan.com/films/celebrity/29480", "https://maoyan.com/films/celebrity/29481", "https://maoyan.com/films/celebrity/28625", "https://maoyan.com/films/celebrity/5392", ] scheduler = Queue() for url in start_urls: scheduler.put(url) main()


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有