爬虫基础(9)数据存储之文件存储

news/2024/7/19 11:49:03 标签: python, 爬虫

文章目录

    • 一. TXT 文本存储
        • 1. 基本实例
        • 2. 打开方式
    • 二. JSON 文件存储
        • 1. 基本实例
        • 2. JSON 基础知识
        • 3. 写入 JSON
        • 4. 读取 JSON
    • 三. CSV 文件存储
        • 1. 基本实例
        • 2. 写入 CSV
        • 3. 读取 CSV

一. TXT 文本存储

1. 基本实例

首先,可以用 requests 将网页源代码获取下来,然后使用 BeautifulSoup 解析库解析,完整代码如下:

python">import requests
from bs4 import BeautifulSoup


def getHtml(url):
    # 获取网页数据
    html = requests.get(url)
    htmlCode = html.text
    # 解析网页
    soup = BeautifulSoup(htmlCode,'html.parser')
    # 返回解析后的页面内容
    return soup

def getTitle(url):
    soup = getHtml(url)
    title = soup.find('div',class_="book_info").find('h1').string
    return title

# 获取各章节所在的网页链接
def getCatalogList(url):
    soup = getHtml(url)
    # 查找所有章节的链接
    listBox = soup.find('div', class_="book_list").find_all('a')
    # 新建列表用来储存链接列表中的URL
    bookLists = []
    for i in listBox:
        listUrl = i['href']
        bookLists.append(listUrl)
    return bookLists

# 获取各章节正文
def getNovelContent(url):
    soup = getHtml(url)
    # 获得需要的正文内容
    content = soup.find('div', class_="contentbox").text
    content = content.strip()
    contentCut = content.replace("本章未完,点击[ 下一页 ]继续阅读-->>","").replace("本章有错误,我要提交上一章 返回目录 下一章","").replace("小提示:按 回车[Enter]键 返回书目,按 ←键 返回上一页, 按 →键 进入下一页。","")
    return contentCut

# 保存文件到本地
def saveNovel(url):
    bookLists = getList(url)
    title = getTitle(url)
    num = 1
    with open('%s.txt'%title, 'a' ,encoding='utf-8') as f:
        for listUrl in bookLists:
            # 拼接完整的每个章节的链接地址
            chapterUrl = url + listUrl
            chapterContent = getNovelContent(chapterUrl)
            f.write(chapterContent)
            print('***第{}章下载完成***'.format(num))
            num += 1
        f.close()

if __name__ == '__main__':
    url='http://www.moyanxsw.com/binbianbushihaitanghong/'
    saveNovel(url)

上述代码实现小说《鬓边不是海棠红》各章节内容爬取的思路是:

  • 首先可以通过目录页面获得小说所有章节的链接地址
  • 然后通过第一步获得的链接地址,爬取每一章节的正文内容
  • 最后把爬取到的正文保存在本地的 txt 文档中

下面我们分别来说说上面各个函数的功能。

**(1)getHtml():获取网站数据并解析网页 **

先用 requests.get() 方法获取一个HTTP状态码,然后用.text 方法来获取这个结果里的文本信息;最后用 BeautifulSoup4 将复杂的 HTML 文档转换成一个复杂的树形结构(每个节点都是Python对象,之后就可以利用 soup 加标签名轻松地获取这些标签的内容了),并将结果返回。

**(2)getTitle():获得小说标题 **

首先调用getHtml()获得解析后的 HTML,然后再获取小说标题所在的标签。标题是放在<div class="book_info">中的<h1 >标签里,所以直接用 soup.find('div',class_="book_info").find('h1').string就可以拿出来了。

(3)getCatalogList():获取章节目录

首先调用getHtml()获得解析后的 HTML,然后再获取各章节目录的链接所在的标签。之前我们分析过,在目录页面中,所有的目录链接都放在 < div class="book_list">中的<a>标签里,所以我们可以用 soup.find('div', class_="book_list").find_all('a') 来获得所有符合条件的 <a> 标签了;通过上述代码可以获得一个包含所有 <a> 标签的列表,遍历所有的列表元素,获取各个 <a> 标签的 href 属性值,然后保存在 bookLists 中并返回。

**(4)getNovelContent():获取各章节正文 **

首先调用getHtml()获得各章节内容所在页面解析后的 HTML。需要注意的是,此时调用该函数时传递的 url参数是拼接后的各章节正文内容所在的网页链接。由于正文部分是放在各个章节对应的正文页面的 < div class="contentbox">中的,直接用 soup.find('div', class_="contentbox").text就可以获得对应 div 中的所有小说正文内容,其中已过滤 HTML 的标签代码。在提取了各章节正文内容之后,用.strip() 方法去掉内容最后不属于小说正文的部分(因为正文底部的控件和文字也都包含在了 < div class="contentbox">中)。

**(5)saveNovel():爬取小说正文后保存到本地 **

首先调用 getCatalogList(url)getTitle(url) 函数,获得相应的返回内容;然后以追加模式打开一个 TXT 文件,遍历访问每一个 bookLists 中的元素所指向的小说各章节的链接,获取小说各章节的内容,然后将文本写入 TXT 文件中;最后关闭文件。

2. 打开方式

在刚才的实例中, open() 方法的第二个参数设置成了 a ,这样在每次写入文本时不会清空源文件,而是在文件末尾写入新的内容,这是一种文件打开方式。关于文件的打开方式,其实还有其他几种,这里简要介绍一下:
在这里插入图片描述
在这里插入图片描述

二. JSON 文件存储

JSON ,全称为 JavaScript Object Notation 也就 JavaScript 对象标记,它通过对象和数组的组合来表示数据,构造简洁但是结构化程度非常高,是一种轻量级的数据交换格式。JSON 格式数据的本质就是字符串。下面我们就来了解如何利用 Python 保存数据到 JSON 文件。

1. 基本实例

我们先来看看下面这个实例中将爬取数据保存为 JSON 数据。

例1:爬取猫眼电影网站上排名前100的电影信息

python">import re
import requests
import json
from requests.exceptions import RequestException

def get_open_page(url):
    try:
        headers = {
            'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
        }
        proxies = {
            'http': 'http://127.0.0.1'    # 使用localhost作为代理服务器
        }
        response = requests.get(url, headers=headers, timeout=3, proxies=proxies)
        response.encoding = 'utf-8'
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        return None

# 爬取排名前100的电影
def main():
    List = []
    for i in range(0, 10):
        url = 'https://maoyan.com/board/4?offset='
        url = url + str(i * 10)
        html = get_open_page(url)
        pattern = re.compile(
            '<dd>.*?board-index.*?>(.*?)</i>.*?name.*?a.*?>(.*?)</a>.*?star.*?>\s+(.*?)\s+.*?</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>',
            re.S)
        result = re.findall(pattern, html)
        for items in result:
            dic = {
                'top:': items[0],
                'name:': items[1],
                'stars:': items[2],
                'releasetime:': items[3],
                'score': items[4] + items[5]
            }
            List.append(dic)
    with open('maoyan_movie.json', 'w', encoding="utf-8") as file:
        file.write(json.dumps(List, indent=2, ensure_ascii=False))

main()

运行结果如下:

[
  {
    "top:": "1",
    "name:": "我不是药神",
    "stars:": "主演:徐峥,周一围,王传君",
    "releasetime:": "上映时间:2018-07-05",
    "score": "9.6"
  },
  {
    "top:": "2",
    "name:": "肖申克的救赎",
    "stars:": "主演:蒂姆·罗宾斯,摩根·弗里曼,鲍勃·冈顿",
    "releasetime:": "上映时间:1994-09-10(加拿大)",
    "score": "9.5"
  },
  {
    "top:": "3",
    "name:": "绿皮书",
    "stars:": "主演:维果·莫腾森,马赫沙拉·阿里,琳达·卡德里尼",
    "releasetime:": "上映时间:2019-03-01",
    "score": "9.5"
  },
  ......
]

上述案例爬虫代码封装在两个函数中,get_open_page() 函数主要功能是:接收到**main()**传递过来的参数 URL ,然后调用 request库 中的get 方法发送请求,并接受响应,将响应内容返回;main() 函数的主要功能是:拼接 URL 并调用 get_open_page() 得到响应内容,利用正则表达式解析响应得到的内容,遍历解析后的列表元素(每一个列表元素都是包含6个元素的元组,这六个元素的含义分别是电影排名、电影名称、电影主演、上映时间和电影评分,最后两个元素分别是电影评分的整数位和小数位的数字,两者拼接后就是电影的评分),并将每个列表元素(元组)的内容保存在字典中,然后将每个字典作为一个列表元素保存在 List 列表;最后将 List 列表转换成 JSON 数据格式,保存在 maoyan_movie.json 文件中。

例2:爬取豆瓣网站上排名前250的电影信息

python">import urllib.request
from bs4 import BeautifulSoup
import os
import codecs
import json
from requests.exceptions import RequestException


def get_open_page(url):
    try:
        header = {
            'User-Agent': "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11"
        }
        ret = urllib.request.Request(url=url, headers=header)
        res = urllib.request.urlopen(ret)
        return res
    except RequestException:
        return None

def getDatas():
    List = []
    for i in range(0,10):
        url = "https://movie.douban.com/top250?start="
        url1 = url + str(i * 25) + "&filter="
        res = get_open_page(url1)
        response = BeautifulSoup(res, 'html.parser')
        datas = response.find_all('div', {'class': 'item'})
        folder_name = "output"
        if not os.path.exists(folder_name):
            os.mkdir(folder_name)
        file_name = "douban" + ".json"
        file_path = folder_name + "/" + file_name
        for item in datas:
            dic = {}
            dic['rank'] = item.find('div', {'class': 'pic'}).find('em').get_text()
            dic['title'] = item.find('div', {'class': 'info'}).find('div', {'class': 'hd'}).find('a').find('span', {
                'class': 'title'}).get_text()
            dic['picUrl'] = item.find('div', {'class': 'pic'}).find('a').find('img').get('src')
            List.append(dic)
        with codecs.open(file_path, 'w', encoding="utf-8") as fp:
            fp.write(json.dumps(List, indent=2, ensure_ascii=False))

getDatas()

上述案例的爬虫思路跟例1一样,此处不再赘述。

2. JSON 基础知识

Python 数据类型与 json 数据类型的映射关系如下:

PythonJSON
dictobject
list, tuplearray
str, unicodestring
int, long, floatnumber
Truetrue
Falsefalse
Nonenull

JSON 中常用的方法:

方法描述
json.dumps()将 Python 对象编码成 JSON 字符串
json.loads()将已编码的 JSON 字符串解码为 Python 对象
json.dump()将Python内置类型序列化为json对象后写入文件
json.load()读取文件中json形式的字符串元素转化为Python类型

3. 写入 JSON

调用 dump() 方法将 JSON 对象转化为字符串,然后再调用文件的 write() 方法写入文本,例如:

python">import json

data = [{
        'name': 'Bob',
        'gender': 'male',
        'birthday': '1992-10-18'
    },
    {
        'name': '王伟',
        'gender': '男',
        'birthday': '1996-4-15'
    }
]
with open('json_data.json', 'w', encoding='utf-8') as file:
    file.write(json.dumps(data, indent=2, ensure_ascii=False))

写入 json_data.json 文件的内容如下:

[
  {
    "name": "Bob",
    "gender": "male",
    "birthday": "1992-10-18"
  },
  {
    "name": "王伟",
    "gender": "男",
    "birthday": "1996-4-15"
  }
]

参数encoding=‘utf-8’ 规定文件输出的编码,参数 indent 代表缩进字符个数,参数 ensure_ascii=False 设置 JSON 文本内容中文的正常显示。

4. 读取 JSON

我们调用 JSON 库的 loads() 方法可以将 JSON 文本字符串转换为 JSON 对象。例如,将下面 JSON 形式的字符串用 Python 将其转换为可操作的数据结构,如列表或字典:

python">str = '''
[
  {
    "name": "Bob",
    "gender": "male",
    "birthday": "1992-10-18"
  },
  {
    "name": "王伟",
    "gender": "男",
    "birthday": "1996-4-15"
  }
]
'''
print(type(str))
data = json.loads(str)
print(data)
print(type(data))

上述代码运行结果如下:

<class 'str'>
[{'name': 'Bob', 'gender': 'male', 'birthday': '1992-10-18'}, {'name': '王伟', 'gender': '男', 'birthday': '1996-4-15'}]
<class 'list'>

这里使用 loads() 方法将字符串转为 JSON 对象。由于最外层是中括号,所以最终的类型是列表。对转换成的列表元素,我们可以通过索引进行访问,而对每个列表元素(字典)又可以以访问字典元素的方式进行获取。

如果 JSON 数据存放在文件中,我们可以直接读取文件内容,再调用 loads() 方法转换成可操作的数据格式。上述代码我们用 with ... as ... 改写如下:

python">with open('json_data.json', 'r') as file:
    str = file.read()
    data = json.loads(str)
    print(data)
    print(type(data))

三. CSV 文件存储

CSV ,全称为 Comma-Separated Values ,中文可以叫作逗号分隔值或字符分隔值,其文件以纯文本形式存储表格数据。该文件是一个字符序列,可以由任意数目的记录组成,记录间以某种换行符分隔。每条记录由字段组成,字段间的分隔符是其他字符或字符串,最常见的是逗号或制表符。不过所有记录都有完全相同的字段序列 ,相当于一个结构化表的纯文本形式。它比 Excel 文件更加简介,XLS 文本是电子表格,它包含了文本、数值、公式和格式等内容,而 CSV 中不包含这些内容,就是特定字符分隔的纯文本,结构简单清晰。

1. 基本实例

我们先来看看下面这个实例中将爬取数据保存为 CSV 数据。

例1:从“大学生必备网”上面爬取2021QS世界大学排名1000强

python">import requests
from bs4 import BeautifulSoup
import csv


# 发送请求与接收响应
def get_soup(url, encode):
    header = {
        'User-Agent': 'python-requests/2.22.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'
    }
    response = requests.get(url=url, headers=header)
    response.encoding=encode
    return BeautifulSoup(response.text, "lxml")

# 解析网页
def get_university(soup):
    un_list = soup.find("table").find_all("tr")
    td_list = []
    for un_row in un_list:
        td_row=""
        for un_td in un_row.find_all("td"):
            td_row = td_row + un_td.text + "\t"
        td_list.append(td_row)
    return td_list

# 保存数据
def write_csv(data_list):
    csv_file = open("university.csv", "w+", encoding="utf-8")
    try:
        csv_writer = csv.writer(csv_file)
        for i in data_list:
            csv_writer.writerow(i.split('\t')[:-1])
    finally:
        csv_file.close()

if __name__ == '__main__':
    url = r"https://www.dxsbb.com/news/16131.html"
    soup = get_soup(url,"gb2312")
    list = get_university(soup)
    write_csv(list)

上述代码保存的文件内容如下:

排名,学校名称,国家/地区
1,麻省理工学院,美国
2,斯坦福大学,美国
3,哈佛大学,美国
4,加州理工学院,美国
5,牛津大学,英国
6,苏黎世联邦理工学院,瑞士
7,剑桥大学,英国
8,帝国理工学院,英国
9,芝加哥大学,美国
10,伦敦大学学院,英国
......

2. 写入 CSV

在Python中处理 CSV 格式的数据可以导入 CSV 模块,其中包含了向文件写入 CSV 数据的方法。例如,下面示例向 csv_data.csv 文件写入三位学生的信息:

python">import csv

with open('csv_data.csv', 'w') as csvfile:
    writer = csv.writer(csvfile, delimiter=' ')
    writer.writerow(['id', 'name', 'age'])
    writer.writerow(['10001', 'Mike', 20])
    writer.writerow(['10002', 'Bob', 22])
    writer.writerow(['10003', 'Jordan', 21])

首先,打开 csv_data.csv 文件,然后指定打开的模式为 w ,获得文件句柄,随后调用 csv 库中的 writer() 方法初始化写人对象,传入该句柄,然后调用 writerow() 方法传入每行的数据即可完成写入。在程序运行结束时,可以生成一个 csv_data.csv 文件,其中包含三条数据,内容如下:

id name age
10001 Mike 20
10002 Bob 22
10003 Jordan 21

需要注意的是,参数 delimiter=' ' 将列与列之间的分隔符设置为 ' ' ,即空格。如果不设置该参数,则默认以 ',' 作为列与列之间的分隔符。

另外,我们可以调用 writerows() 方法同时写入多行数据,此时传入的参数就是一个二维列表,例如下面代码示例:

python">with open('csv_data.csv', mode='a', encoding='utf-8') as csvfile:
    writer = csv.writer(csvfile, delimiter=' ')
    writer.writerows([['10004', 'Durant', 25],
                      ['10005', '凯文', 23]])

通过设置 mode='a' 将文件读写方式设置为追加。如果写入文件的数据中包含中文字符,需要给 open() 参数指定编码格式,否则会发生编码错误。

除了上述以列表格式向文件写入数据,我们一般会用字典格式。在 csv 库中也提供了字典的写入方式,示例如下:

python">with open('csv_data.csv', 'w') as csvfile:
    field_names = ['id', 'name', 'age']
    writer = csv.DictWriter(csvfile, fieldnames=field_names)
    writer.writeheader()
    writer.writerow({'id':'10001', 'name':'Mike', 'age': 20})
    writer.writerow({'id':'10002', 'name':'Bob', 'age': 22})
    writer.writerow({'id':'10003', 'name':'Jordan', 'age': 21})
    writer.writerows([{'id':'10004', 'name':'Durant', 'age': 25},
                      {'id': '10005', 'name': '凯文', 'age': 223}])

这里先定义3个字段,用 field_names 表示,然后将其传给 DictWriter 来初始化一个字典写入对象,接着可以调用 writeheader() 方法先写入头信息,然后再调用 writerow() 方法传入相应字典即可。程序运行结束后,生成的 csv_data.csv 文件内容如下:

id,name,age
10001,Mike,20
10002,Bob,22
10003,Jordan,21
10004,Durant,25
10005,凯文,223

3. 读取 CSV

我们同样可以使用 CSV 库来读取 CSV 文件。例如,将刚才写入的文件内容读取出来,相关代码如下:

python">import csv

with open ('csv_data.csv', mode='r', encoding='utf-8') as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        print(row)

上述代码输出的文件内容如下:

['id', 'name', 'age']
['10001', 'Mike', '20']
['10002', 'Bob', '22']
['10003', 'Jordan', '21']
['10004', 'Durant', '25']
['10005', '凯文', '23']

这里我们构造的是 Reader 对象,通过遍历输出了每行的内容,每一行都是一个列表形式。注意,如果 CSV 文件中包含中文的话,还需要指定文件编码。

另外,如果接触过 pandas 的话,可以利用 read_csv() 方法将数据从 CSV 文件中读取出来,例如:

python">import pandas as pd

df = pd.read_csv('csv_data.csv')
print(df)

上述代码输出的文件内容如下:

python">      id    name  age
0  10001    Mike   20
1  10002     Bob   22
2  10003  Jordan   21
3  10004  Durant   25
4  10005     凯文   23

上述文章内容如有错误,欢迎各位读者在评论区留言!


http://www.niftyadmin.cn/n/1190209.html

相关文章

爬虫框架Scrapy(1)Scrapy框架安装与项目开发

文章目录一. Scrapy框架简介1. Scrapy 框架介绍2. 数据处理流程二. Scrapy 及其依赖库的安装三. Scrapy 项目开发流程1. 常用命令2. 创建 Scrapy 项目3. 创建 Spider4. 创建 Item5. 解析 Response6. 使用 Item7. 后续 Request&#xff08;1&#xff09;继承 Scrapy.spider&…

关于HDPHP,HDCMS 安装,空白问题

这几天&#xff0c;框论坛发现&#xff0c;HDPHP&#xff0c;号称还不错。 微信&#xff0c;支付宝支付&#xff0c;短信&#xff0c;阿里云OSS&#xff0c;权限认证等&#xff0c;都有。对开发人员来说很好了。。 马上下载来试试&#xff0c; HDPHP官方文档说需要PHP5.6,不过貌…

爬虫框架Scrapy(2)Selector的用法

文章目录Selector 的用法&#xff08;一&#xff09;直接使用 Selector&#xff08;二&#xff09;Scrapy Shell 模拟 Scrapy 请求&#xff08;三&#xff09;Xpath 选择器1. 基本用法2. 嵌套选择3. 以属性选择4. 获取节点内容5. 获取节点属性与文本&#xff08;四&#xff09;…

.Net Core 爬坑日记

安装【DotNetCore.1.0.1-VS2015Tools.Preview2.0.3.exe】失败 查看log发现&#xff0c;发现猫腻&#xff0c;然后copy下链接&#xff0c;用迅雷手动下载【AspNetCoreLocalFeed_69.msi】 并安装好&#xff0c;再次安装【DotNetCore.1.0.1-VS2015Tools.Preview2.0.3.exe】&#x…

爬虫框架Scrapy(3)使用Item封装数据

文章目录使用 Item 封装数据&#xff08;一&#xff09;Item 基类1. 自定义 Item2. 拓展 Item&#xff08;二&#xff09;Field 元数据&#xff08;三&#xff09;在多个爬虫里使用 Item使用 Item 封装数据 我们首先来看上一篇文章在最后所写的那个实例中的 spider.py &#x…

Css display值与解释

Css display值与解释-&#xff08;详细可见CSS手册的CSS display手册&#xff09;参数&#xff1a;block :块对象的默认值。用该值为对象之后添加新行none :隐藏对象。与visibility属性的hidden值不同&#xff0c;其不为被隐藏的对象保留其物理空间inline :内联对象的默认值。用…

爬虫框架Scrapy(4)Spider的用法

文章目录一. Spider 的用法1. Spider 运行流程2. Spider 类分析二. 请求与响应1. Request 对象2. Response 对象一. Spider 的用法 在 Scrapy 中&#xff0c;要抓取网站的链接配置、抓取逻辑、解析逻辑里其实都是在 Spider 中配置的。本节我们就专门了解一下 Spider 的基本用法…

爬虫框架Scrapy(5)DownLoader Middleware 的用法

文章目录四. DownLoader Middleware 的用法1. 使用说明2. 核心方法3. 项目实战四. DownLoader Middleware 的用法 Downloader Middleware 即下载中间件&#xff0c;它是处于 Scrapy 的 Request 和 Response 之间的处理模块。Scheduler 从队列中拿出一个 Request 发送给 Downlo…