python爬虫中的mongo实践
目标
- 获取全本小说网的小说封面
- 保存封面图片到mongo数据库中
- 记录封面图片对应的小说信息
分析
- 本次使用requests作为http请求工具
- 获取小说封面的方式采用爬虫分类型、分页爬去小说封面地址
- 向图片的原地址发送http的get请求,获取response的content就是图片的二进制数据了
- 本次存储图片的方式并采用系统自带的文件系统,而是使用mongodb的GridFS
- 将步骤3中的content数据put到gridfs中获取文件id
- 从步骤2中获取小说的名称等信息,与步骤5中的id做映射,保存到novel集合集中
注:步骤6在是可以将小说的其他信息一同保存至gridfs的files集合中的,使用方法会在代码中进行注释
示例
import requests
from bs4 import BeautifulSoup
import pymongo
import gridfs
import time
# 获取mongoClient对象
client = pymongo.MongoClient("localhost", 27017)
# 获取使用的database对象
db = client.test
# 获取图片存储集合
fs = gridfs.GridFS(db, "images")
def save_pic_to_disk():
"""
将数据库中文件转存到本地文件系统
:return: 无
"""
fss = fs.find()
for fl in fss:
print(fl.md5)
tp_file = open('d:/img/' + fl.md5 + '.jpg', "wb")
tp_file.write(fl.read())
tp_file.close()
def mongodb_delete(title, author):
"""
根据小说标题和作者删除其小说封面信息
例如:mongodb_delete('桃花扇','孔尚任')
:param title 小说标题
:param author 小说作者
"""
record = db.novel.find_one({"title": title, "author": author})
print(record)
_id = record.get("_id")
_img = record.get("imag")
db.novel.remove({"_id": _id})
fs.delete(_img)
def iterator(url):
"""
遍历指定地址下的小说条目
获取小说封面、标题和作者信息
然后保存至数据库
最后获取递归到下一页进行遍历
:param url: 小说列表页面地址
:return: 无返回
"""
print(url)
# 获取页面html,并使用beautifulsoup进行解析
rs = requests.get(url).content.decode("gbk")
bs_obj = BeautifulSoup(rs, "html.parser")
content = bs_obj.find("div", {"id": "content"})
# 获取小说列表,并遍历小说数据
novels = bs_obj.findAll("div", {"class": "Sum Search_line"})
for novel in novels:
# 获取小说的名称、索引地址、作者
name = novel.find("h2").find("a").get_text()
index = novel.find("h2").find("a").get("href")
author = novel.find("em").find("a").get_text()
# 获取小说封面,并使用gridfs保存至mongo
img = novel.find("img")
rs = requests.get(img.get("src"))
# 这种方式是将小说的题目等信息与封面图片保存在一起
# fs.put(rs.content,title=name, author=author, url=index)
# 这种方式进行保存图片文件,然后记录文件编号,比较像关系数据库的使用方式
_id = fs.put(rs.content)
db.novel.save(dict(title=name, author=author, url=index, imag=_id, time=time.time()))
# 获取下一页链接,如果存在下一页就进行递归遍历
next_page = content.find("div", {"id": "pagelink"}).find("a", {"class": "next"})
if next_page:
iterator(next_page.get("href"))
# 遍历全本小说网的小说信息
# 从小说站点的导航上可以看出,该站点小说分为11个类型,而且类型编号从1开始递增
for i in range(1, 12):
iterator('http://www.wanbenxiaoshuo.net/sort/' + str(i) + '_1.html')
# 关闭资源
client.close()
总结
- pymongo的简单使用
在上述代码中使用pymongo作为python操作mongodb的工具,除了完成目标要求,还额外书写了两个方法用于体验pymongo的使用。总结一下pymongo的简单使用方法:
1. 获取Client对象
client = pymongo.MongoClient("localhost", 27017)
- 使用client获取database对象
db = client.test
- 如果需要可以使用database对象获取GridFS
fs = gridfs.GridFS(db, "images")
- 使用database对象存储document,python中可以直接存储字典数据
db.novel.save(dict(title=name, author=author, url=index, imag=_id, time=time.time()))
db.novel.remove({"_id": _id})
novels = bs_obj.findAll("div", {"class": "Sum Search_line"})
- 可以使用GridFS对文件进行存储、删除等操作
fs.put(rs.content,title=name, author=author, url=index)
fs.delete(_img)
fss = fs.find()
- 遍历查询结果,直接使用 for-in即可
for fl in fss:
print(fl.md5)
- 使用完毕后需要关闭资源
client.close()
- request处理中文乱码问题:
request中貌似并没有直接处理字符集的工具,所以只能自己对相应体中的content进行编码判断,然后再decode。
rs = requests.get(url).content.decode("gbk")
到目前为止,我还没有发现比较有效的字符集判断方法,以前看过chardet,但是我认为那种判断方式效率过于底下,所以我还是使用的人工观测,然后直接指定字符集。