爬虫数据存储(11. 文件存储)

内容概括

保存文件有多种形式,主要包括XML文件、CSV文件、JSON文件
主要内容:
1、操作文件的基础方式
2、使用Fileput对象读取文件
3、读写XML文件
4、读写JSON文件
5、XML数据、JSON数据和Python对象之间的相互转换
6、读写CSV文件

11.1 write_file 写入文件

open函数和使用方法

open函数用于打开文件,第1个参数指明文件路径(绝对相对都可以),第2个参数指明文件操作模式

 文件模式                 描述
    r                 读模式(默认)
    w                 写模式                              
    x                 排他的写模式(只能用户自己写)   
    a                 追加模式   
    b                 二进制模式(可添加到其他模式中使用)   
    t                 文本模式(默认值,可添加到其他模式中使用)   
    +                 读写模式(必须与其他文件模式一起使用)  
# wirte(string):向文件写入内容,该方法返回写入文件的字节数
# read[n]:读取文件的内容,n是一个整数,表示从文件指针指定位置读取n个字节,如不指定,该方法会读取从当前往后的所有字节
# seek(n):重新设置指针,也就是改变文件的当前位置(偏移量)
# close():关闭文件     

实操案例:

# 一些模式打开test1.txt文件
f = open('./files/test1.txt','w')
# 向test1.txt写入"i love",运行结果:7
print(f.write('I love '))
# 向test1.txt写入"python",运行结果:6
print(f.write('python'))
f.close()

# 以读的模式打开test1.txt
f = open('./files/test1.txt','r')
# 从test1.txt文件中读取7个字节的数据,运行结果I love
print(f.read(7))
# 从当前位置开始读取6个字节的数据,运行结果python
print(f.read(6))
f.close()

try:
    # 如果test2.txt文件不存在,会抛出异常
    f = open('./files/test2.txt','r+')
except Exception as e:
    print(e)
# 用追加可读写模式打开test2.txt文件
f = open('./files/test2.txt','a+')
# 向test2.txt文件写入hello
print(f.write('hello'))
f.close()

f = open('./files/test2.txt','a+')
# 读取test2.txt文件,由于目前文件指针已经在文件的结尾,所以什么都不会读出来
print(f.read())
# 将文件指针设置到指针开始的位置
f.seek(0)
# 读取全部文件内容,hello
print(f.read())
f.close()

try:
    # 用可读写的方式打开test2.txt文件,将文件内容会清空
    f = open('./files/test2.txt','w+')
    # 读取文件的全部内容,什么都没读出来
    print(f.read())
    # 向文件写入“How are you?”
    f.write('How are you?')
    # 重置文件指针到文件的开始位置
    f.seek(0)
    # 读取文件的全部内容,运行结果How are you?
    print(f.read())
finally:
    f.close()

11.2 read_write_lines文件读写行

操作方法:

通过readline方法、readlines方法、writelines方法对urls.txt文件进行读写操作

  1. readline读取文件中一行的数据
  2. readlines读取文件中所有行的数据
  3. writelines写入文件多行数据

实操案例:

import os
f = open('./files/urls.txt','r+')
url = ''
while True:
    # 从urls.txt读一行文本
    url = f.readline()
    # 将最后的行结束符去掉
    url = url.rstrip()
    # 当读上来的是空串就结束循环
    if url == '':
        break
    else:
        print(url)
print('_____________________________________')
f.seek(0)
# 读urls中所有的行
print(f.readlines())
# 向urls.txt文件中添加一个新行
f.write('https://jiketiku.com' + os.linesep)
f.close()

# 使用'a+'模式再次打开urls.txt文件
f = open('./files/urls.txt','a+')
# 定义一个要写入的urls.txt列表
urlList = ['https://geekori.com' + os.linesep, 'https://www.google.com' + os.linesep]
# 将urlList写入urls.txt文件
f.writelines(urlList)
# 关闭urls.txt文件
f.close()

11.3 fileput_demo另一种文件操作方法

操作方法:

  1. 使用fileinput.input方法读取urls.txt文件,通过for循环获取每一行值
  2. 同时调用fileinpout.filename方法
  3. fileinput.lineno方法分别获取正在读取的文件名和当前的行号

实操案例:

import fileinput
# 使用input方法打开urls.txt文件
fileobj = fileinput.input('./files/urls.txt')
# 输出fileobj类型
print(type(fileobj))
# 读取urls.txt文件第1行
print(fileobj.readline().rstrip())
# 通过for循环输出urls.txt文件的其他行
for line in fileobj:
    line = line.rstrip()
    # 如果file不等于空串,输出当前行号和内容
    if line != '':
        print(fileobj.lineno(),':',line)
    else:
        print(fileobj.filename())

11.4 read_search_xml读取XML文件

操作方法:

读取XMl文件需要导入xml.etree.ElementTree,并通过该模块的parse函数读取XML文件

实操案例:

products.xml文件

<!-- products.xml -->
<root>
    <products>
    	 <product uuid='1234'>
            <id>10000</id>
            <name>iPhone9</name>
            <price>9999</price>
        </product>
        <product uuid='4321'>
            <id>20000</id>
            <name>特斯拉</name>
            <price>800000</price>
        </product>
        <product uuid='5678'>
            <id>30000</id>
            <name>Mac Pro</name>
            <price>40000</price>
        </product>
    </products>
</root>
from xml.etree.ElementTree import parse
# 开始分析products.xml文件,files/products.xml是要读取的XML文件的名字
doc = parse('files/products.xml')
# 通过Xpath搜索子节点集合,然后对这个子节点集合进行迭代
for item in doc.iterfind('products/product'):
    # 读取product节点的id子节点的值
    id = item.findtext('id')
    # 读取product节点的name子节点的值
    name = item.findtext('name')
    # 读取product节点的price子节点的值
    price = item.findtext('price')
    # 读取product节点的uuid子节点的值
    print('uuid=',item.get('uuid'))
    print('id=',id)
    print('name=',name)
    print('price=',price)
    print('__________________________________________________')

11.5 dict to xml 字典转xml文件方法

操作方法:

  1. 将一个字典类型的变量转换为XMl字符串
  2. 然后使用parseString函数解析整个XML字符串
  3. 并用带缩进格式的形式将XMl字符串写入persons.xml文件

实操案例;

import dicttoxml
from xml.dom.minidom import parseString
import os
# 定义一个字典
d = [20,'names',
     {'name':'Bill','age':30,'salary':2000},
     {'name':'王军','age':34,'salary':3000},
     {'name':'John','age':25,'salary':2500}]
# 将字典转换为XML格式(bytes形式)
bxml = dicttoxml.dicttoxml(d, custom_root = 'persons')
# 将bytes形式的XML数据按utf-8编码格式解码成XMl字符串
xml = bxml.decode('utf-8')
print(xml)
# 解析XML字符串
dom = parseString(xml)
# 生成带缩进格式的XML字符串
prettyxml = dom.toprettyxml(indent='    ')
# 创建files目录
os.makedirs('files',exist_ok=True)
# 以只写和utf-8编码格式的方式打开persons.xml文件
f = open('./files/persons.xml','w',encoding='utf-8')
# 将格式化的XML字符写入persons.xml文件
f.write(prettyxml)
f.close()

生产的persons.xml文件:

<?xml version="1.0" ?>
<persons>
    <item type="int">20</item>
    <item type="str">names</item>
    <item type="dict">
        <name type="str">Bill</name>
        <age type="int">30</age>
        <salary type="int">2000</salary>
    </item>
    <item type="dict">
        <name type="str">王军</name>
        <age type="int">34</age>
        <salary type="int">3000</salary>
    </item>
    <item type="dict">
        <name type="str">John</name>
        <age type="int">25</age>
        <salary type="int">2500</salary>
    </item>
</persons>

11.6 xml to dict xml文件转字典类型

操作方法:

  1. 从products.xml文件中读取一个XMl字符串
  2. 并使用xmltodict模块的parse模块来解析XML字符串
  3. 如果格式正确,parse函数可以返回字典对象

实操案例:

import xmltodict
# 打开products.xml
f = open('files/products.xml','rt',encoding="utf-8")
xml = f.read()
# 分析XMl字符串,并转化为字典
d = xmltodict.parse(xml)
print(d)
print('_________________________________________________')
f.close()

# 使用pprint模块可读性更好
import pprint
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(d)

11.7 json to dict JSON字符串转字典

操作方法:

  1. 将data的字典转换为JSON字符串
  2. 然后将JSON字符串s通过eval函数转换为字典
  3. 最后从products.json文件中读取SJON字符串,并使用loads函数和eval函数两种方法将JSON字符串转换为字典

实操案例:

products.json文件内容:

[
    {
    "name":"iPhone9",
    "price":9999,
    "count":3000},

	{"name":"特斯拉",
	"price":800000,
	"count":122}
]

import json
data = {
    'name':'Bill',
    'company':'Microsoft',
    'age':34
}
# 将字典转换为JSON字符串
jsonStr = json.dumps(data)
print(type(jsonStr))
print(jsonStr)
# 将JSON字符串转换为字典
data = json.loads(jsonStr)
print(type(data))
print(data)


# 定义一个JSON字符串
s = '''
{
    'name' : 'Bill',
    'company' : 'Microsoft',
    'age' : 34
}
'''
# 使用eval函数将JSON字符串转换为字典
data = eval(s)
print(type(data))
print(data)
# 输出字典中key为company的值
print(data['company'])
# 打开products.json文件中的所有内容
f = open('./files/products.json','r',encoding='utf-8')
jsonStr = f.read()
# 使用eval函数将JSON字符串转换为字典
json1 = eval(jsonStr)
# 使用loads函数将JSON字符串转换为字典
json2 = json.loads(jsonStr)
print(json1)
print(json2)
print(json2[0]['name'])
f.close()

11.8 json to class JSON字符串转换成CLASS类

操作使用:

  1. 指定类:loads函数会自动创建指定类的实例,并将由JSON字符串转换成的字典通过类的构造方法传入类实例
  2. 指定回调函数:loads函数会调用回调函数返回类实例,并将有JSON字符串转换成的字典传入回调函数,必须有一个参数可以接收字典

实操案例:

从product.json文件读取JSON字符串,然后分别通过指定类(Product)和指定回调函数(json2Product)的方式将JSON字符串转换为Product对象

  1. product.json文件内容:
{"name":"iPhone9",
"price":9999,
"count":3000}
  1. json2class.PY文件内容:
import json
class Product:
    # d参数式要传入的字典
    def __init__(self, d):
        self.__dict__ = d
# 打开product.json文件
f = open('files/product.json','r')
# 从product.json文件中读取JSON字符串
jsonStr = f.read()
my1 = json.loads(jsonStr, object_hook=Product)
# 下面3行代码输出Product对象中相应属性的值
print(type(my1))
print('name=',my1.name)
print('price=',my1.price)
print('count',my1.count)
print('________________________________________________________________')


# 定义用于将字典转换为Product对象的函数
def json2Product(d):
    return Product(d)
# 通过指定类回调函数的方式将JSON字符串转换为Product对象
my2 = json.loads(jsonStr, object_hook=json2Product)
# 下面3行代码输出Product对象中相应属性的值
print(type(my2))
print('name=',my2.name)
print('price=',my2.price)
print('count',my2.count)
f.close()

11.9 class to json CLASS类转JSON字符串

操作方法:

  1. dumps函数不仅可以将字典转换为JSON字符串,还可以将类实例转换为JSON字符串
  2. dumps函数需要通过default关键字参数指定一个回调函数,在转换的过程中,dumps函数会向这个回调函数传入类实例

操作实例:

创建一个类和构造函数,将其实例化,再调用dumps方法将对象转换成JSON字符串

import json
class Product:
    # 通过类的构造方法初始化3个属性
    def __init__(self,name,price,count):
        self.name = name
        self.price = price
        self.count = count

# 用于将Product类的实例转换为字典的函数
def product2Dict(obj):
    return {
        'name':obj.name,
        'price':obj.price,
        'count':obj.count
    }
# 创建Product类的实例
product = Product('特斯拉',10000000,20)
# 将Product类的实例转换为JSON字符串
jsonStr = json.dumps(product,default=product2Dict,ensure_ascii=False)
print(type(jsonStr))
print(jsonStr)

11.10 classlist to json 对象列表和JSON字符串转换

操作方法:

  1. 从products.json文件读取JSON字符串
  2. 并通过loads函数将其转换为Product对象列表,
  3. 然后再通过dumps函数将Product对象列表转换为JSON字符串

实操案例:

products.json文件与前面的一样

import json
class Product:
    def __init__(self,d):
        self.__dict__ = d

f = open('files/products.json','r',encoding='utf-8')
jsonStr = f.read()
# 将JSON字符串转换为Product对象列表
products = json.loads(jsonStr,object_hook=Product)
# 输出Product对象列表中所有Product对象的相关属性值
for product in products:
    print(type(product))
    print('name=', product.name)
    print('price=', product.price)
    print('count=', product.count)
f.close()
# 定义将Product对象转换为字典的函数
def product2Dict(product):
    return {
        'name':product.name,
        'price':product.price,
        'count':product.count
    }
# 将Product对象列表转换为JSOn字符串
jsonStr = json.dumps(products, default=product2Dict,ensure_ascii=False)
print(jsonStr)

11.11 json to xml JSON字符串转XML文件

操作方法:

  1. 从products.json文件中读取json字符串
  2. 将json字符串转换成字典类型
  3. 将字典类型转换成XML文件

实操案例:

import json
import dicttoxml
f = open('files/products.json','r',encoding='utf-8')
jsonStr = f.read()
# 将JSON字符串转换为字典
d = json.loads(jsonStr)
print(d)
# 将字典转换为XML字符串
xmlStr = dicttoxml.dicttoxml(d).decode('utf-8')
print(xmlStr)
f.close()

11.12 write_csv 写入CSV文件

操作方法:

读写方法和读写文件类似,使用open方法打开csv文件,操作符用 w 写模式

实操案例:

import csv
# 打开files/data.csv文件,如果不存在,会重新创建一个data.csv
with open('files/data.csv','w',encoding='utf-8') as f:
    writer = csv.writer(f)

    # 写入数据
    writer.writerow(['产品ID','产品名称','生产企业','价格'])
    writer.writerow(['0001','iPhone9','Apple',9999])
    writer.writerow(['0002','特斯拉','特斯拉',12345])
    writer.writerow(['0003','荣耀手机','华为',3456])

# 修改字段分隔符
with open('files/data1.csv', 'w', encoding='utf-8') as f:
    writer = csv.writer(f, delimiter=';')
    writer.writerow(['产品ID', '产品名称', '生产企业', '价格'])
    writer.writerow(['0001', 'iPhone9', 'Apple', 9999])
    writer.writerow(['0002', '特斯拉', '特斯拉', 12345])
    writer.writerow(['0003', '荣耀手机', '华为', 3456])

# 一次性写入多行
with open('files/data2.csv','w',encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['产品ID','产品名称','生产企业','价格'])
    writer.writerows([['0001','iPhone9','Apple',9999],
                      ['0002', '特斯拉', '特斯拉', 12345],
                      ['0003', '荣耀手机', '华为', 3456]])

# 写入字典形式的数据
with open('files/data3.csv','w',encoding='utf-8') as f:
    fieldnames = ['产品ID','产品名称','生产企业','价格']
    writer = csv.DictWriter(f,fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({'产品ID': '0001', '产品名称': 'iPhone9', '生产企业': 'Apple', '价格': 9999})
    writer.writerow({'产品ID': '0002', '产品名称': '特斯拉', '生产企业': '特斯拉', '价格': 12345})
    writer.writerow({'产品ID': '0003', '产品名称': '荣耀手机', '生产企业': '华为', '价格': 3456})

# 追加数据
with open('files/data.csv','a',encoding='utf-8') as f:
    fieldnames = ['产品ID','产品名称','生产企业','价格']
    writer = csv.DictWriter(f,fieldnames=fieldnames)
    writer.writerow({'产品ID': '0004', '产品名称': '量子战衣', '生产企业': '斯塔克工业', '价格': 99999999999})

11.13 read_csv 读取CSV文件

操作方法:

读写方法和读写文件类似,使用open方法打开csv文件,操作符用 r 写模式

实操案例:

读取11.12案例中的data.csv文件,对其进行操作

import csv
# 打开要读取的CSV文件
with open('files/data.csv','r',encoding='utf-8') as f:
    # 创建reader对象
    reader = csv.reader(f)
    # 获取每一行的数据
    for row in reader:
        print(row)
# 导入pandas模块
import pandas as pd
# 读取data.csv文件的数据
df = pd.read_csv('files/data.csv')
print(df)

11.14 实战案例:抓取豆瓣音乐排行榜写入CSV文件

操作方法:

  1. 使用requests网络库请求页面
  2. 使用Beautiful Soup解析库分析内容
  3. 解析出需要的内容
  4. 将内容写入CSV文件

实操案例:

import requests
from bs4 import BeautifulSoup
import re
import csv
import time

headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
}

def get_url_music(url):
    html = requests.get(url,headers=headers)

    soup = BeautifulSoup(html.text, 'lxml')

    aTags = soup.find_all("a",attrs={"class": "nbg"})
    for aTag in aTags:
        get_music_info(aTag['href'])

def save_csv(filename,info):
    with open(filename, 'a', encoding='utf-8') as f:
        fieldnames = ['name', 'author', 'style', 'time','publisher','score']
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writerow(info)
        
def get_music_info(url):
    html = requests.get(url,headers=headers)
    soup = BeautifulSoup(html.text, 'lxml')
    name = soup.find (attrs={'id':'wrapper'}).h1.span.text
    author = soup.find(attrs={'id':'info'}).find('a').text
    styles = re.findall('<span class="pl">流派:</span>&nbsp;(.*?)<br />', html.text, re.S)
    if len(styles) == 0:
        style = '未知'
    else:
        style = styles[0].strip()

    time = re.findall('发行时间:</span>&nbsp;(.*?)<br />', html.text, re.S)[0].strip()
    publishers = re.findall('<span class="pl">出版者:</span>&nbsp;(.*?)<br />', html.text, re.S)
    if len(publishers) == 0:
        publisher = '未知'
    else:
        publisher = publishers[0].strip()


    score = soup.find(class_='ll rating_num').text
    info = {
        'name': name,
        'author': author,
        'style': style,
        'time': time,
        'publisher': publisher,
        'score': score
    }
    print(info)
    save_csv(filename,info)

if __name__ == '__main__':
    urls = ['https://music.douban.com/top250?start={}'.format(str(i)) for i in range(0,250,25)]
    filename = 'music.csv'
    with open(filename, 'w', encoding='utf-8') as f:
        fieldnames = ['name', 'author', 'style', 'time', 'publisher', 'score']
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()

    for url in urls:
        get_url_music(url)
        time.sleep(1)

结果截图:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值