python爬取网页并保存在mongodb中

python爬取网页并保存在mongodb中

  • MongoDB简介
  • 基本概念
    • (1)文档
    • (2)集合
    • (3)数据库

MongoDB简介

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

基本概念

(1)文档

文档是 MongoDB 中数据的基本单位,类似于关系数据库中的行(但是比行复杂)。多个键及其关联的值有序地放在一起就构成了文档。不同的编程语言对文档的表示方法不同,在JavaScript 中文档表示为:
{“greeting”:“hello,world”}
这个文档只有一个键“greeting”,对应的值为“hello,world”。多数情况下,文档比这个更复杂,它包含多个键/值对。例如:
{“greeting”:“hello,world”,“foo”: 3}
文档中的键/值对是有序的,下面的文档与上面的文档是完全不同的两个文档。
{“foo”: 3 ,“greeting”:“hello,world”}
文档中的值不仅可以是双引号中的字符串,也可以是其他的数据类型,例如,整型、布尔型等,也可以是另外一个文档,即文档可以嵌套。文档中的键类型只能是字符串。需要注意的是:

  1. 文档中的键/值对是有序的。
  2. 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
  3. MongoDB区分类型和大小写。
  4. MongoDB的文档不能有重复的键。
  5. 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
    文档键命名规范:
    • 键不能含有\0 (空字符)。这个字符用来表示键的结尾。
    • .和$有特别的意义,只有在特定环境下才能使用。
    • 以下划线"_"开头的键是保留的(不是严格要求的)。

(2)集合

集合就是一组文档,类似于关系数据库中的表。集合是无模式的,集合中的文档可以是各式各样的。例如,{“hello,word”:“Mike”}和{“foo”: 3},它们的键不同,值的类型也不同,但是它们可以存放在同一个集合中,也就是不同模式的文档都可以放在同一个集合中。既然集合中可以存放任何类型的文档,那么为什么还需要使用多个集合?这是因为所有文档都放在同一个集合中,无论对于开发者还是管理员,都很难对集合进行管理,而且这种情形下,对集合的查询等操作效率都不高。所以在实际使用中,往往将文档分类存放在不同的集合中,例如,对于网站的日志记录,可以根据日志的级别进行存储,Info级别日志存放在Info 集合中,Debug 级别日志存放在Debug 集合中,这样既方便了管理,也提供了查询性能。但是需要注意的是,这种对文档进行划分来分别存储并不是MongoDB 的强制要求,用户可以灵活选择。

(3)数据库

MongoDB 中多个文档组成集合,多个集合组成数据库。一个MongoDB 实例可以承载多个数据库。它们之间可以看作相互独立,每个数据库都有独立的权限控制。在磁盘上,不同的数据库存放在不同的文件中。MongoDB 中存在以下系统数据库。
● Admin 数据库:一个权限数据库,如果创建用户的时候将该用户添加到admin 数据库中,那么该用户就自动继承了所有数据库的权限。
● Local 数据库:这个数据库永远不会被复制,可以用来存储本地单台服务器的任意集合。
● Config 数据库:当MongoDB 使用分片模式时,config 数据库在内部使用,用于保存分片的信息。

由于mongodb是文档型数据库,区别于传统的数据库,它是用来管理文档的。在传统的数据库中,信息被分割成离散的数据段,而在文档数据库中,文档是处理信息的基本单位。一个文档可以很长、很复杂、可以无结构,与字处理文档类似。一个文档相当于关系数据库中的一条记录。
文档存储一般用类似json的格式存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能。
MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。

MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
因此我们可以很方便的将网页用MongoDB保存下来。
这里爬取的是深圳市规划和自然资源局的公示
python爬取网页并保存在mongodb中_第1张图片
源码:
这里参考了其他的博主,每一行基本都写了注释方便理解

import requests	#requests是一个很实用的Python HTTP客户端库,编写爬虫和测试服务器响应数据时经常会用到。
import re		#正则表达式(要从网页代码里面提取特定的标签里面的内容)
from lxml import etree	
#lxml:python 的HTML/XML的解析器,
#是libxml2和libxslt两个C库的Python化绑定,它的独特之处在于兼顾了这些库的速度和功能完整性
import pymongo # PyMongo 驱动
import time
from multiprocessing import Pool	#进程池

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                  'AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/78.0.3904.97 Safari/537.36'
}
#定制header,向访问网站提供所使用的浏览器类型及版本、操作系统及版本、浏览器内核、等信息的标识。

# MongoDB的连接
myclient = pymongo.MongoClient('localhost', 27017)		#mondodb默认端口号是27017
mydb = myclient["webpages"]           #创建数据库webpag
dblist = myclient.list_database_names()	
if "webpages" in dblist:		#检测数据库是否存在
    print("该数据库存在")
mycol = mydb['gov.publicity5']
'''
创建集合gov.publicity
在 MongoDB 中,数据库只有在内容插入后才会创建,
就是说,数据库创建后要创建集合(数据表)并插入一个文档(记录),数据库才会真正创建。
'''
collist = mydb.list_collection_names()
if "gov.publicity5" in collist:	#检测集合是否存在
    print("该集合存在")


def get_link(url):  # 获取每一页公示的所有链接
    res = requests.get(url, headers)	#GET请求
    selector = etree.HTML(res.text)		# 利用 etree.HTML 把字符串解析成 HTML文件
    href = selector.xpath('//ul[@class="list-group"]/li/a/@href')	
	#获取值为list-group的ul元素下li标签下的a所有href属性对应的值,href 属性用于指定超链接目标的 URL
	#这里即得到每篇公示的url,比如从源码可以看到某个为 href="./201911/t20191114_489608.html
	#XPath 是一门在 XML/HTML 文档中查找信息的语言。XPath 可用来在 XML /HTML文档中对元素和属性进行遍历。
    links = []	#空列表
    for i in href:
        links.append(re.sub('^.', 'http://pnr.sz.gov.cn/xxgk/gggs', str(i)))
        '''
	在上面得到的url前添加http://pnr.sz.gov.cn/xxgk/gggs/
	这样我们就得到了完整的链接如:http://pnr.sz.gov.cn/xxgk/gggs/201911/t20191114_489608.html
	re.sub()字符串替换函数,
	第一个参数为正则表达式需要被替换的参数,
	第二个参数是替换后的字符串,
	第三个参数为输入的字符串
	'''
    return links


def page_scratch(link):  # 抓取公示详细页面信息
    res = requests.get(link, headers)	  #GET请求
    res.encoding = res.apparent_encoding  # 更改可能的编码方式也可以直接用"GB2312"
    selector = etree.HTML(res.text)		  # 利用 etree.HTML 把字符串解析成 HTML 文件
    # 公示标题
    title = selector.xpath('//div[@class="xxym"]/h4/text()')[0]	#寻找名为xxym的块级元素下的h4文本
    # 公示来源
    source = re.findall('来源:(.*)\n', selector.xpath('//div[@class="xxym"]/h5/text()')[0])[0]
    # 公示日期
	# findall 方法能够以列表的形式返回能匹配的子串,语法findall(pattern, string, flags=0)
    date = re.findall(' (.*?)\t', selector.xpath('//div[@class="xxym"]/h5/text()')[1])[0]
    # 公示内容
    article = '\n'.join(selector.xpath('//div[@class="TRS_Editor"]/p/text()'))
    # 附件的下载连接
    download_link = selector.xpath('//i[@class="fa fa-caret-right"]/a/@href')
    if len(download_link) == 0:
        appendix = '无'		#如果没有附件
    else:
        appendix = re.search('^http(.*)/', res.url)[0] + download_link[0].replace('./', '')     #得到完整的附件链接
    # 保存到MongoDB中
    mycol.insert_one({'标题': title, '来源': source, '日期': date, '公示内容': article, '附件链接': appendix})
    #insert_one() 方法返回 InsertOneResult 对象,该对象包含 inserted_id 属性,它是插入文档的 id 值
    time.sleep(0.1)
    # 缓冲,推迟调用线程的运行,单位:秒


if __name__ == '__main__':	#_name_:当前模块名。
    try:				#如果模块是被直接运行的,则代码块被运行,如果模块是被导入的,则代码块不被运行。
        urls = ['http://pnr.sz.gov.cn/xxgk/gggs/index.html']	#用一个列表存储页面链接,第一个链接为首页
        urls.extend(['http://pnr.sz.gov.cn/xxgk/gggs/index_{}.html'.format(i) for i in range(1, 34)])  
		# 查看1-8页的公示,加上首页共9页
		#格式化字符串的函数 str.format(),作用是将{}用format()里的内容替换
        links = []	#空列表,用于存储公示链接
        for url in urls:
            links.extend(get_link(url))	#获取每一篇公示的链接
        pool = Pool(processes=4)	#维持执行的进程总数为processes
        pool.map(page_scratch, links)
    '''
	map()函数会将第二个参数的需要迭代的列表元素一个个的传入第一个参数我们的函数中,
	创建一个个进程,放进进程池中
	第一个参数是我们需要引用的函数
	多进程,这样主要用于加快爬虫效率
	'''
    except:	#捕获所有异常
        print ("error")

这里用的是mongodb的可视化客户端robo3t进行查看

结果
python爬取网页并保存在mongodb中_第2张图片
python爬取网页并保存在mongodb中_第3张图片

你可能感兴趣的:(数据库,mongodb)