今天我们实现增量爬虫~,先来了解一下啥是增量爬虫??
增量爬虫: 通过爬虫程序监测某网站数据更新的情况,以便可以爬取到该网站更新出的新数据
通俗来讲:就是当你在爬取一个网站的数据的时候,反反复复在爬取,比如现在有一个项目,需要你用爬虫爬取某网站的数据
但是这个爬虫不能每次都运行,都是从头到尾爬取数据吧,这也不利于高效率开发啊,而且,就算每次都爬取,那爬取的数据是不是有很多重合的
你昨天爬过一次,今天再爬,是不是昨天爬过的数据又是在重新爬取了?这样就会造成数据的冗余,所以就有了增量爬虫的到来
对他的定义我觉得就是懒爬取,因为就是我们在第一爬去数据的时候,爬取完毕后,再次启动爬虫程序,只要是那个网站没有跟新的数据,他就不会爬,一旦有了更新,他也只是爬取更新的数据,之前爬过的数据一律不再爬取,这样就节省了工作效率,更不会造成数据冗余
说了这么多,希望大家可以明白增量爬虫的意义所在,好了,我们接下来准备一下环境吧
项目环境
1、建立自己的User-Agent池
这个有必要说明一下,以前呢,我们是自己去网上搜User-Agent,或者用浏览器的,
我们本次爬虫就用这个,这个是随机的User-Agent,只需要导入包就可以用了,简单快捷说一下怎么安装:
1)pip3 install fake_useragent #安装
2)from fake_useragent import UserAgent #导入
UserAgent().random # 使用
2、pymongo
这个是MongoDB数据库(可有可无),不存数据库的可以不用了,这个不影响增量爬虫
3、redis
这个是实现增量爬虫的必须品,redis数据库,这个属于缓存型数据库,他是建立在内存中的数据库
4、hashlib
Python里面的hashlib模块提供了很多加密的算法,我们这次用hashlib的md5算法加密数据
5、requests
pip install requests #安装
requests是python实现的简单易用的HTTP库,使用起来比urllib简洁很多
好了,所需环境就这么多,就下来我们看一下怎么做了:
汽车之家二手车数据抓取
一、分析:
1. 一级页面: 每辆汽车详情页的链接
2. 二级页面: 每辆汽车具体的数据
二、建立自己的User-Agent池:
2. from fake_useragent import UserAgent
UserAgent().random
三、使用redis中集合实现增量爬虫
原理: 根据sadd()的返回值来确定之前是否抓取过
返回值为1: 说明之前没有抓取过
返回值为0: 说明之前已经抓取过,程序结束
接下来就是代码展示了:
导入各种会用到的包
import requests
import re
import time
import random
from fake_useragent import UserAgent
import pymongo
import redis # 增量就靠它
from hashlib import md5 #加密算法库
import sys
总体的思路还是:定义功能函数,减少重复代码
class CarSpider:
def __init__(self):
self.url = 'https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp{}exx0/?pvareaid=102179#currengpostion'
# mongodb3个对象
# 连接mongodb数据库
self.conn = pymongo.MongoClient('localhost', 27017)
self.db = self.conn['cardb']
self.myset = self.db['carset']
# 连接到redis
self.r = redis.Redis(host='localhost', port=6379, db=0)
功能函数1: 请求功能函数
’User-Agent’: UserAgent().random 这个就是创建随机的User-Agent池
def get_html(self, url):
headers = {'User-Agent': UserAgent().random}
# ignore参数: 解码时遇到不识别的字符直接忽略掉
html = requests.get(url=url, headers=headers).content.decode('gb2312', 'ignore')
return html
功能函数2: 解析功能函数,获取到的数据,进行用正则解析出来
def re_func(self, regex, html):
pattern = re.compile(regex, re.S)
r_list = pattern.findall(html)
return r_list
"功能函数: 对url进行md5加密,加密的作用就是对每个数据加密,避免重复数据
def md5_url(self, url):
s = md5()
s.update(url.encode())
return s.hexdigest()
爬虫逻辑函数
def parse_html(self, one_url):
"""爬虫逻辑函数"""
one_html = self.get_html(url=one_url)
one_regex = '<li class="cards-li list-photo-li".*?<a href="(.*?)".*?</li>'
# href_list: ['/declear/xxx.html', '', '', '', ...]
href_list = self.re_func(one_regex, one_html)
for href in href_list:
finger = self.md5_url(href)
# 返回值1:之前没抓过
if self.r.sadd('car:spiders', finger) == 1:
# 拼接完整URL地址,发请求提取具体汽车信息
self.get_one_car_info(href)
time.sleep(random.randint(1, 2))
else:
# 一旦发现之前抓过的,则彻底终止程序
sys.exit('更新完成')
接下来就是提取一辆汽车的具体信息,并存入了Redis、MongoDB数据库
def get_one_car_info(self, href):
"""提取一辆汽车的具体信息"""
two_url = 'https://www.che168.com' + href
two_html = self.get_html(url=two_url)
two_regex = '<div class="car-box">.*?<h3 class="car-brand-name">(.*?)</h3>.*?<ul class="brand-unit-item fn-clear">.*?<li>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<span.*?(?:"overlayPrice">|"price">)(.*?)(?:<b>|</span>)'
# car_info_list:
# [('宝马','12万公里','2004年','自动/2.5L','北京','4.20')]
car_info_list = self.re_func(two_regex, two_html)
item = {}
item['name'] = car_info_list[0][0].strip()
item['km'] = car_info_list[0][1].strip()
item['time'] = car_info_list[0][2].strip()
item['type'] = car_info_list[0][3].split('/')[0].strip()
item['displace'] = car_info_list[0][3].split('/')[1].strip()
item['address'] = car_info_list[0][4].strip()
item['price'] = car_info_list[0][5].strip()
print(item)
# 数据存入到MongoDB数据库
self.myset.insert_one(item)
最后程序入口函数,用来控制整体逻辑
def run(self):
for page in range(1, 5):
page_url = self.url.format(page)
self.parse_html(page_url)
if __name__ == '__main__':
spider = CarSpider()
spider.run()
我们先来看一下redis数据库有没有存入进去
先连接进去:redis-cli -h 你电脑的IP地址 -p 密码
然后:ping一下
能看到是pong就行
再:keys *
接着看到有这个car:spiders
输入:SMEMBERS car:spiders
就可以看到有很多数据了
注意:
我们再次运行代码的话不会再增加数据了,因为是redis我们避免了重复数据,所以,再次运行代码就会显示我们设置的这样:
而且再次查看redis数据库里页不会有新增的,可以自己看一下
mongodb数据的话自己看一下吧,因为这个不重要,不在本文中做详细介绍,可以看这篇关于mongodb的博客:
https://blog.csdn.net/Yxh666/article/details/111239743
最后奉上全部代码:
import requests
import re
import time
import random
from fake_useragent import UserAgent
import pymongo
import redis
from hashlib import md5
import sys
class CarSpider:
def __init__(self):
self.url = 'https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp{}exx0/'
# mongodb的3个对象
# 连接到mongodb
self.conn = pymongo.MongoClient('localhost', 27017)
self.db = self.conn['cardb']
self.myset = self.db['carset']
# 连接到redis
self.r = redis.Redis(host='localhost', port=6379, db=0)
def get_html(self, url):
headers = {'User-Agent': UserAgent().random}
# ignore参数: 解码时遇到不识别的字符直接忽略掉
html = requests.get(url=url, headers=headers).content.decode('gb2312', 'ignore')
return html
def re_func(self, regex, html):
pattern = re.compile(regex, re.S)
r_list = pattern.findall(html)
return r_list
def md5_url(self, url):
s = md5()
s.update(url.encode())
return s.hexdigest()
def parse_html(self, one_url):
one_html = self.get_html(url=one_url)
one_regex = '<li class="cards-li list-photo-li".*?<a href="(.*?)".*?</li>'
# href_list: ['/declear/xxx.html', '', '', '', ...]
href_list = self.re_func(one_regex, one_html)
for href in href_list:
finger = self.md5_url(href)
# 返回值1:之前没抓过
if self.r.sadd('car:spiders', finger) == 1:
# 拼接完整URL地址,发请求提取具体汽车信息
self.get_one_car_info(href)
time.sleep(random.randint(1, 2))
else:
# 一旦发现之前抓过的,则彻底终止程序
sys.exit('更新完成')
def get_one_car_info(self, href):
two_url = 'https://www.che168.com' + href
two_html = self.get_html(url=two_url)
two_regex = '<div class="car-box">.*?<h3 class="car-brand-name">(.*?)</h3>.*?<ul class="brand-unit-item fn-clear">.*?<li>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<span.*?(?:"overlayPrice">|"price">)(.*?)(?:<b>|</span>)'
# car_info_list:
# [('宝马','12万公里','2004年','自动/2.5L','北京','4.20')]
car_info_list = self.re_func(two_regex, two_html)
item = {}
item['name'] = car_info_list[0][0].strip()
item['km'] = car_info_list[0][1].strip()
item['time'] = car_info_list[0][2].strip()
item['type'] = car_info_list[0][3].split('/')[0].strip()
item['displace'] = car_info_list[0][3].split('/')[1].strip()
item['address'] = car_info_list[0][4].strip()
item['price'] = car_info_list[0][5].strip()
print(item)
# 数据存入到MongoDB数据库
self.myset.insert_one(item)
def run(self):
for page in range(1, 5):
page_url = self.url.format(page)
self.parse_html(page_url)
if __name__ == '__main__':
spider = CarSpider()
spider.run()