文章目录
- 一. 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 数据类型的映射关系如下:
Python | JSON |
---|---|
dict | object |
list, tuple | array |
str, unicode | string |
int, long, float | number |
True | true |
False | false |
None | null |
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
上述文章内容如有错误,欢迎各位读者在评论区留言!