爬虫快速上手之数据持久化存储

学习视频推荐:【达内】2021年Python爬虫全套课程(爬虫快速上手)_哔哩哔哩_bilibili

目录

1.数据持久化存储-csv

2.数据持久化存储-MySQL

3.数据持久化存储-MongoDB

4.多级页面数据抓取案例

5.增量爬虫-MySQL实现


1.数据持久化存储-csv

        csv模块

  • 模块
    • csv Python标准库模块
  • 作用
    • 将爬取的数据存放到本地的csv文件中
  • 使用流程
    • 打开csv文件        f = open('test.csv', 'w', encoding = 'utf8')
    • 初始化写入对象        writer = csv.writer(f)
    • 写入数据(参数为列表)        writer.writerow(['', '']) 或者 writer.writerows([('', ''), (), ()...])
  • 代码示例
    • 1.writer.writerow(['', ''....])        参数为列表,列表中每个元素为csv文件中单元格的数据
      • import csv
        
        data_list = ['Jack', 18]
        # 打开csv文件
        file = open('writerow.csv', 'a', newline='')
        # 初始化写入对象
        writer = csv.writer(file)
        # 一行行写入数据
        writer.writerow(data_list)
        writer.writerow(data_list)
        writer.writerow(data_list)
        writer.writerow(data_list)
        
        # 关闭文件
        file.close()
    • 2.writer.writerows([('', '', ''....),(),...])        其中每个元组为一行数据,元组中的元素为一行中csv单元格中的数据第三方
      • import csv
        
        data = [('Jastin', 20), ('Bob', 11), ('Athena', 21), ('Backy', 35)]
        
        # 打开文件
        file = open('writerows.csv', 'a', newline='')
        # 初始化写入对象
        writer = csv.writer(file)
        # 一次写入多行数据
        writer.writerows(data)
        # 关闭文件
        file.close()

2.数据持久化存储-MySQL

        pymysql回顾

  • pymysql模块与MySQL数据库交互流程
    • 创建数据库连接对象        db = pymysql.connect(host='localhost', user = 'root', password  = '123456', database = 'xxx', charset = 'utf8')
    • 创建游标对象        cursor = db.cursor()
    • 执行SQL命令       
      • cursor.excute(sql语句, [xx, xx...]) 一次插入1条表记录
      • cursor.excutemany(sql语句, [(xx, xx...), (), ()...])一次插入多条表记录
      • 代码示例
      • import pymysql
        
        # 1. 创建数据库连接
        db = pymysql.connect(host='localhost', user='root', passwd='123456', database='maoyandb', charset='utf8')
        # 2. 创建游标对象
        cursor = db.cursor()
        # 3. 执行sql语句,将电影信息写入数据库表中
        # execute()一次写入一行数据
        cursor.execute('insert into maoyan VALUES (%s, %s, %s)', ['长安如故', '任嘉伦,白鹿', '2021.8.20'])
        # executemany()一次写入多行数据
        data_list = [('花千骨', '赵丽颖,霍建华', '2017.1.1'), ('机智的上半场', '章若楠,翟子路', '2021.9.1'), ('你是我的荣耀', '杨洋,迪丽热巴', '2021.7.1')]
        cursor.executemany('insert into maoyan VALUES (%s, %s, %s)', data_list)
        # 4. 提交到数据库执行
        db.commit()
        # 5. 关闭游标
        cursor.close()
        # 6. 关闭数据库连接
        db.close()
    • 提交到数据库执行        db.commit()
    • 关闭游标        cursor.close()
    • 断开数据库连接       db.close()

3.数据持久化存储-MongoDB

        MongoDB回顾

  •  进入MongoDB命令行:mongo
  • 常用命令
    • show dbs        查看所有库
    • use 库名       切换到指定库
    • show collections        查看当前库中的所有集合
    • db.集合名.find().pretty()        查看当前库中文档
    • db.集合名.count()        统计集合中文档的数量
    • db.集合名.drop()        删除集合
    • db.dropDatabase()        删除当前库
  • 概念
    • MongoDB为非关系型数据库,数据以键值对方式存储
    • MongoDB基于磁盘存储
    • MongoDB数据类型单一,值为JSON文档,而Redis基于内存
    • MongoDB:库->集合->文档
    • MySQL:库->表->表记录
  • pymongo模块使用流程
    • 安装:sudo pip3 install pymongo
    • 使用流程
      • 创建连接对象
        • conn = pymongo.MongoClient('localhost', 27017)
      • 创建库对象
        • db = conn['库名']
      • 创建集合对象
        • myset = db['集合名']
      • 在集合中插入文档
        • myset.insert_one({})
        • myset.insert_many([{}, {}...])
      • 注意:MongoDB无需提前建库建集合,直接操作即可,会自动建库建集合

4.多级页面数据抓取案例

        汽车之家 多级页面的数据抓取

  • 百度搜索汽车之家->二手车->价格从低到高
  • 爬取目标: 车的型号,行驶里程,上牌时间,档位,排量,车辆所在地,价格 分析: 一级页面所抓数据:汽车详情页链接 二级页面所抓数据:车的标题,行驶里程,上牌时间,档位,排量,所在地,价格
  • 分析
    • 抓取汽车详情页的链接----查看一级页面网页源码是否有二级页面的链接
    • 抓取详情页所需的汽车详细信息----查看二级页面网页源码是否有所需要的汽车详细信息
    • 一级页面分页链接分析
      • url地址
      • 第一页:https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp1exx0/
      • 第二页:https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp2exx0/
      • 第三页:https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp3exx0/
      • ......
    • 写代码
  • 代码示例
    • from urllib import request
      import re
      import time
      import random
      
      class CarSpider(object):
          def __init__(self):
              self.url = 'https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp{}exx0/'
              self.headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36'}
              self.count = 0 # 记录抓取多少条数据
      
          # 多级页面数据抓取,需要请求多次
          def get_html(self, url):
              """
              功能函数1:获取响应内容
              :param url:
              :return:
              """
              # 重构请求头
              req = request.Request(url=url, headers=self.headers)
              # 发起请求获取响应对象
              res = request.urlopen(req)
              # 获取响应对象内容
              # 知识点1:ignore:decode()时遇到不能识别的字符,则忽略掉
              # 知识点2:decode()时,如果出现乱码,则去查看网页的字符编码是什么
              html = res.read().decode('gb2312', 'ignore') # 经过报错调试,需要加ignore,且返回的页面为乱码,此时查看网页源码得知页面编码charset='gb2312
              return html
      
          # 解析数据 (多级页面,需要解析不止一个网页的数据)
          def re_func(self, regex, html):
              """
              功能函数2:解析提取数据
              :return:
              """
              pattern = re.compile(regex, re.S)
              r_list = pattern.findall(html)
              return r_list
      
          def parse_html(self, first_url):
              """
              数据抓取函数:从一级页面解析开始
              :return:
              """
              first_html = self.get_html(first_url)
              regex = r'<li class="cards-li list-photo-li " .*?<a href="(.*?)".*?</li>'
              href_list = self.re_func(regex, first_html)
              # print(href_list)
              for href in href_list:
                  # 拼接汽车详情页的链接
                  car_url = 'https://www.che168.com' + href
                  # 解析详情页数据
                  car_info = self.get_data(car_url)
                  print(car_info)
                  time.sleep(random.randint(1, 8)) # 请求一次详情页,随机休眠时长
      
          def get_data(self, car_url):
              """
              数据抓取函数:解析二级页面(汽车详情页)
              :param car_url:
              :return:
              """
              car_html = self.get_html(car_url)
              # print(car_html)
              # print(car_url)
              car_regex = r'<div class="car-box">.*?<h3 class="car-brand-name">(.*?)</h3>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)<i class="ico-explain">.*?<span class="price" id="overlayPrice">.*?(\d+\.{,1}\d+)<b>'
              car_list = self.re_func(car_regex, car_html)
              # print(car_list)
              try:
                  car_info = {}
                  car_info['name'] = car_list[0][0].strip()
                  car_info['kilometre'] = car_list[0][1].strip()
                  car_info['time'] = car_list[0][2].strip()
                  car_info['gear'] = car_list[0][3].split('/')[0].strip()
                  car_info['output'] = car_list[0][3].split('/')[-1].strip()
                  car_info['address'] = car_list[0][4].strip()
                  car_info['migrate'] = car_list[0][5].strip()
                  car_info['price'] = car_list[0][6].strip()
              except:
                  print('当前正则表达式未获取到某一种数据(价格)而存在异常!')
              return car_info
      
          def run(self):
              """
              入口函数
              :return:
              """
              for page in range(1, 2):
                  first_url = self.url.format(page)
                  self.parse_html(first_url)
                  time.sleep(random.uniform(1, 8))
      
      if __name__ == '__main__':
          car = CarSpider()
          car.run()

5.增量爬虫-MySQL实现

        增量爬虫MySQL

  • 定义
    • 每次爬取只爬取新更新的链接,之前抓取过的链接不会再继续抓取
  • 实现思路 - MySQL
    • MySQL中新建指纹表,用来存储所有爬取过的链接的指纹
    • 在爬取任何链接之前,先判断该指纹是否存在于指纹表,如果已存在则不再进行爬取
  • 注意
    • 对于常规网站来说,新更新的数据一般会在前面。比如说新闻类网站(新更新的新闻会在页面顶部)、电商类网站、房产类网站等
    • 所以,一旦检测到有链接已经爬取过,则无需再检测其之后的链接,终止程序即可
  • 以上面爬取汽车之家为例实现增量爬虫
    • 准备
      • 如何给URL地址进行md5加密生成指纹?
        • from hashlib import md5
          
          # 创建md5对象
          s = md5()
          # 对url进行加密
          s.update(url.encode()) # 注意需要对url进行字节编码
          # 获取加密后的十六进制url
          finger = s.hexdigest() # 需要存储在指纹表里的链接指纹
      • pymysql模块如何获取执行查询语句后的结果?
        • import pymysql
          
          # 创建数据库连接对象
          db = pymysql.connect(host = 'localhost', user = 'root', password = '123456', database = 'cardb', charset = 'utf8')
          # 创建游标对象
          cursor = db.cursor()
          
          # 执行查询语句
          sql = 'select * from request_finger where finger = %s'
          cursor.excuter(sql, [finger])
          # 获取查询到的值
          result = cursor.fetchall() # 返回值为元组
          
          # 提交数据库执行命令
          db.commit()
          # 关闭游标
          cursor.close()
          # 断开数据库连接
          db.close()
    • 创建库以及指纹表
      • create database cardb charset=utf8;
        
        use cardb;
        
        create table request_finger(
            finger varchar(32)
        )charset=utf8;
    • 创建存储所抓取具体数据的表
      • create table cartable(
            name varchar(100),
            kilometre varchar(50),
            time varchar(50),
            gear varchar(50),
            output varchar(50),
            address varchar(50),
            migrate varchar(50),
            price varchar(50)
        )charset=utf8;
  • 最终代码示例
    • """
      百度搜索汽车之家->二手车->价格从低到高
          url地址:
              第一页:https://www.che168.com/beijing/a0_0msdgscncgpi1lto1cspexx0/
              第二页:https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp2exx0/
              第三页:https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp3exx0/
              ......
      
      爬取目标:
          车的型号,行驶里程,上牌时间,档位,排量,车辆所在地,价格
      
      分析:
          一级页面所抓数据:汽车详情页链接
          二级页面所抓数据:车的标题,行驶里程,上牌时间,档位,排量,所在地,价格
      
      """
      from urllib import request
      import re
      import time
      import random
      import pymysql
      from hashlib import md5
      import sys
      
      class CarSpider(object):
          def __init__(self):
              self.url = 'https://www.che168.com/beijing/a0_0msdgscncgpi1lto1csp{}exx0/'
              self.headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36'}
              self.count = 0 # 记录抓取多少条数据
              self.db = pymysql.connect(host='localhost', user='root', passwd='123456', database='cardb', charset='utf8')
              self.cursor = self.db.cursor()
              self.md5 = md5() # 创建md5对象
      
          # 多级页面数据抓取,需要请求多次
          def get_html(self, url):
              """
              功能函数1:获取响应内容
              :param url:
              :return:
              """
              # 重构请求头
              req = request.Request(url=url, headers=self.headers)
              # 发起请求获取响应对象
              res = request.urlopen(req)
              # 获取响应对象内容
              # 知识点1:ignore:decode()时遇到不能识别的字符,则忽略掉
              # 知识点2:decode()时,如果出现乱码,则去查看网页的字符编码是什么
              html = res.read().decode('gb2312', 'ignore') # 经过报错调试,需要加ignore,且返回的页面为乱码,此时查看网页源码得知页面编码charset='gb2312
              return html
      
          # 解析数据 (多级页面,需要解析不止一个网页的数据)
          def re_func(self, regex, html):
              """
              功能函数2:解析提取数据
              :return:
              """
              pattern = re.compile(regex, re.S)
              r_list = pattern.findall(html)
              return r_list
      
          def parse_html(self, first_url):
              """
              数据抓取函数:从一级页面解析开始
              :return:
              """
              first_html = self.get_html(first_url)
              regex = r'<li class="cards-li list-photo-li " .*?<a href="(.*?)".*?</li>'
              href_list = self.re_func(regex, first_html)
              # print(href_list)
              for href in href_list:
                  # 拼接汽车详情页的链接
                  car_url = 'https://www.che168.com' + href
      
                  # 对汽车详情页链接进行md5加密
                  self.md5.update(car_url.encode())
                  # 获取汽车详情链接的十六进制md5密码
                  hex_url = self.md5.hexdigest()
      
                  # 判断该汽车详情链接是否在数据库中
                  # 从数据库中读取该指纹是否可以取出
                  sql_select = 'select * from request_finger where finger = %s'
                  self.cursor.execute(sql_select, [hex_url])
                  car_finger = self.cursor.fetchall() # 结果为一个元组
                  if car_finger: # 该链接存在
                      print('数据采集完毕!')
                      sys.exit(1)
                  else: # 该链接不存在
                      # 添加该链接的十六进制MD5至数据库request_finger表中
                      sql_insert = 'insert into request_finger VALUES (%s)'
                      self.cursor.execute(sql_insert, [hex_url])
                      self.db.commit()
                      # 解析详情页数据
                      car_info = self.get_data(car_url)
                      print(car_info)
                      time.sleep(random.randint(1, 8)) # 请求一次详情页,随机休眠时长
      
      
          def get_data(self, car_url):
              """
              数据抓取函数:解析二级页面(汽车详情页)
              :param car_url:
              :return:
              """
              car_html = self.get_html(car_url)
              # print(car_html)
              # print(car_url)
              car_regex = r'<div class="car-box">.*?<h3 class="car-brand-name">(.*?)</h3>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)</h4>.*?<h4>(.*?)<i class="ico-explain">.*?<span class="price" id="overlayPrice">.*?(\d+\.{,1}\d+)<b>'
              car_list = self.re_func(car_regex, car_html)
              # print(car_list)
              try:
                  car_info = {}
                  car_info['name'] = car_list[0][0].strip()
                  car_info['kilometre'] = car_list[0][1].strip()
                  car_info['time'] = car_list[0][2].strip()
                  car_info['gear'] = car_list[0][3].split('/')[0].strip()
                  car_info['output'] = car_list[0][3].split('/')[-1].strip()
                  car_info['address'] = car_list[0][4].strip()
                  car_info['migrate'] = car_list[0][5].strip()
                  car_info['price'] = car_list[0][6].strip()
                  # 将数据存入数据库
                  sql_save = 'insert into cartable VALUES (%s, %s, %s, %s, %s, %s, %s, %s)'
                  self.cursor.execute(sql_save, [car_info['name'], car_info['kilometre'], car_info['time'], car_info['gear'], car_info['output'], car_info['address'], car_info['migrate'], car_info['price']])
                  self.db.commit()
              except:
                  print('当前正则表达式未获取到某一种数据(价格)而存在异常!')
              return car_info
      
          def run(self):
              """
              入口函数
              :return:
              """
              for page in range(1, 2):
                  first_url = self.url.format(page)
                  self.parse_html(first_url)
                  time.sleep(random.uniform(1, 8))
      
              # 关闭游标
              self.cursor.close()
              # 关闭数据库连接
              self.connect.close()
      
      if __name__ == '__main__':
          car = CarSpider()
          car.run()
      
      


版权声明:本文为weixin_52384854原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。