学习视频推荐:【达内】2021年Python爬虫全套课程(爬虫快速上手)_哔哩哔哩_bilibili
目录
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()
- 1.writer.writerow(['', ''....]) 参数为列表,列表中每个元素为csv文件中单元格的数据
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()
- 如何给URL地址进行md5加密生成指纹?
- 创建库以及指纹表
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版权协议,转载请附上原文出处链接和本声明。