Python数据采集与爬虫爬虫Python爬虫学习笔记

从零开始学爬虫(3):通过MongoDB数据库获取爬虫数据

2017-02-23  本文已影响718人  BruceYeen

这是我学习爬虫的笔记,作为备忘,如果可以帮到大家,那就更好了~
从零开始学爬虫(1):爬取房天下二手房信息
从零开始学爬虫(2):突破限制,分类爬取,获得全部数据
从零开始学爬虫(3):通过MongoDB数据库获取爬虫数据

一、MongoDB数据库是什么

MongoDB是一个基于分布式文件存储的数据库。他支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
  一个MongoDB 实例可以包含一组数据库,一个DataBase 可以包含一组Collection(集合),一个集合可以包含一组Document(文档)。一个Document包含一组field(字段),每一个字段都是一个key/value pair。

以上是百度百科的介绍,那么所谓的bson格式是什么东西呢?文档是什么样的?集合又是什么呢?下图是我在mongodb可视化管理工具上Robomongo截的图:

图片.png

好了,那我们可以看到一个集合里面有很多个文档,这类似于一个关系型数据库一个表里面有多条记录。

二、MongoDB安装与配置

安装MongoDB数据库

MongoDB数据库安装的详细过程参见 Windows下安装MongoDB
安装MongoDB数据库可视化管理工具Robomongo:直接去官网下载安装即可,下载地址:https://robomongo.org/download

安装Pymongo

以管理员身份打开命令行提示符,执行pip3 install pymongo,即可完成安装。

至此MongoDB数据库的安装与配置就已经完成了。

三、如何通过pymongo操作MongoDB

1、连接数据库

第一步,通过pymongo建立数据库连接,并且连接一个名为test的数据库(如果test不存在,这一步就是新建一个test数据库)

from pymongo import MongoClient
# 建立 MongoDB 数据库连接
conn = MongoClient('localhost', 27017)
# 新建一个test数据库
db = conn.test
2、数据库操作

假设向test数据库中的test集合中插入文档data(data = {"name": "xyl", "age": 25}),只需要一条语句db.test.insert_one(data)就行了;多个文档的插入用db.test.insert_many(data)。

db = conn.test

# 插入一条记录
data = {"name": "xyl", "age": 25}
db.test.insert_one(data)

# 插入多条记录
data = [{"name": "xyl", "age": 25}, {"name": "yxl", "age": 27}]
db.test.insert_many(data)

# 遍历coll = db.test.find()查询到的所有结果,以及它key=name的value
coll = db.test.find()
for i in col1:
    print(i)
    print(i["name"])

OK,其实MongoDB也是一个数据库,也是可以通过一些语句对其中的数据进行各种操作,这里我们先不深究,有兴趣的可以看一下官方文档。现在,我们来把上一篇文章最后的问题解决一下。

3、二手房信息存储与导出

基于pymongo的常用操作,我们来改写一下上篇文章中的代码(以下是修改后的数据存储的代码)

# -*- coding: utf-8 -*-
from pymongo import MongoClient
from bs4 import BeautifulSoup
import requests
import time

# 建立 MongoDB 数据库连接
conn = MongoClient('localhost', 27017)

# 新建一个test数据库
db = conn.test

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
    'Cookie': 'global_cookie=gpe9gqaciutsnx2zke6bsyizp29iypii6li; polling_imei=231c34cb02f4f783; SoufunSessionID_Esf=3_1486111884_10590; SKHRecordsXIAN=%25e5%258d%258e%25e8%25bf%259c%25e6%25b5%25b7%25e8%2593%259d%25e5%259f%258e%257c%255e2017%252f2%252f13%2b16%253a13%253a18%257c%255e3610937652%257c%2523%25e6%259d%258e%25e5%25ae%25b6%25e6%259d%2591%25e4%25b8%2587%25e8%25be%25be%25e5%25b9%25bf%25e5%259c%25ba%25e9%259b%2581%25e5%25a1%2594%25e8%25b7%25af%25e6%2596%2587%25e8%2589%25ba%25e8%25b7%25af%25e5%258f%258b%25e8%25b0%258a%25e8%25b7%25af%25e9%2593%2581%25e4%25b8%2580%25e4%25b8%25ad215%25e5%25b9%25b3%25e5%2586%2599%25e5%25ad%2597%25e9%2597%25b4%257c%255e2017%252f2%252f16%2b21%253a41%253a27%257c%255e0; logGuid=1d724a5a-3c98-4e52-8e84-924a7ca9a1ac; __utmt_t0=1; __utmt_t1=1; __utmt_t2=1; city=xian; __utma=147393320.1839238857.1486108289.1487256095.1487298760.22; __utmb=147393320.87.10.1487298760; __utmc=147393320; __utmz=147393320.1486191809.5.3.utmcsr=esf.xian.fang.com|utmccn=(referral)|utmcmd=referral|utmcct=/; unique_cookie=U_6qx5dbebzr1btgohc0anj3f2p16iz97a455*29'
}

def spider_1(url):
    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.text, 'lxml')

    titles = soup.select('dd > p.title > a')            # 标题
    hrefs = soup.select('dd > p.title > a')            # 链接
    details = soup.select('dd > p.mt12')                # 建筑信息
    courts = soup.select('dd > p:nth-of-type(3) > a')   # 小区
    adds = soup.select('dd > p:nth-of-type(3) > span')  # 地址
    areas = soup.select('dd > div.area.alignR > p:nth-of-type(1)')     # 面积
    prices = soup.select('dd > div.moreInfo > p:nth-of-type(1) > span.price')  # 总价
    danjias = soup.select('dd > div.moreInfo > p.danjia.alignR.mt5')    # 单价
    authors = soup.select('dd > p.gray6.mt10 > a')      # 发布者
    trains = soup.select('dd > div.mt8.clearfix > div.pt4.floatl > span.train.note')   # 地铁房

    for title, href, detail, court, add, area, price, danjia, author, train in zip(titles, hrefs, details, courts, adds, areas, prices, danjias, authors, trains):
        data = {
            'title': title.get_text(),
            'href': 'http://esf.xian.fang.com' + href.get('href'),
            'detail': list(detail.stripped_strings),
            'court': court.get_text(),
            'add': add.get_text(),
            'area': area.get_text(),
            'price': price.get_text(),
            'danjia': danjia.get_text(),
            'author': author.get_text(),
            'train': train.get_text()
        }
        db.test.insert_one(data)

response = requests.get('http://esf.xian.fang.com/', headers=headers)
soup = BeautifulSoup(response.text, 'lxml')

regions = soup.select('#list_D02_10 > div.qxName > a')  # 区域
totprices = soup.select('#list_D02_11 > p > a')  # 总价
housetypes = soup.select('#list_D02_12 > a')  # 户型
areas = soup.select('#list_D02_13 > p > a')  # 面积

# 对于大于等于100页的进行细分
n = 1
while n < 8:
    i = 1
    while i < 7:
        resp1 = requests.get('http://esf.xian.fang.com' + regions[n].get('href') + '-' + housetypes[i].get('href')[7:], headers=headers)
        soup1 = BeautifulSoup(resp1.text, 'lxml')
        pages = soup1.select('#list_D10_01 > span > span')  # 页数
        if pages and pages[0].get_text().split('/')[1] == '100':
            m = 1
            while m < 10:
                resp1 = requests.get('http://esf.xian.fang.com' + regions[n].get('href') + totprices[m].get('href')[7:-1] + '-' + housetypes[i].get('href')[7:], headers=headers)
                soup1 = BeautifulSoup(resp1.text, 'lxml')
                pages = soup1.select('#list_D10_01 > span > span')  # 页数
                if pages and pages[0].get_text().split('/')[1] == '100':
                    j = 1
                    while j < 10:
                        resp1 = requests.get('http://esf.xian.fang.com' + regions[n].get('href') + totprices[m].get('href')[7:-1] + '-' + housetypes[i].get('href')[7:-1] + '-' + areas[j].get('href')[7:], headers=headers)
                        soup1 = BeautifulSoup(resp1.text, 'lxml')
                        pages = soup1.select('#list_D10_01 > span > span')  # 页数
                        if pages:
                            k = int(pages[0].get_text().split('/')[1])
                            while k > 0:
                                spider_1('http://esf.xian.fang.com' + regions[n].get('href') + totprices[m].get('href')[7:-1] + '-' + housetypes[i].get('href')[7:-1] + '-' + areas[j].get('href')[7:-1] + '-i3' + str(k))
                                k = k - 1
                                time.sleep(2)
                        else:
                            pass
                        j = j + 1
                else:
                    if pages:
                        k = int(pages[0].get_text().split('/')[1])
                        while k > 0:
                            spider_1('http://esf.xian.fang.com' + regions[n].get('href') + totprices[m].get('href')[7:-1] + '-' + housetypes[i].get('href')[7:-1] + '-i3' + str(k))
                            k = k - 1
                            time.sleep(2)
                    else:
                        pass
                m = m + 1
        else:
            if pages:
                k = int(pages[0].get_text().split('/')[1])
                while k > 0:
                    spider_1('http://esf.xian.fang.com' + regions[n].get('href') + housetypes[i].get('href')[7:-1] + '-i3' + str(k))
                    k = k - 1
                    time.sleep(2)
            else:
                pass
        i = i + 1
    n = n + 3         # 只取1/4/7

这段代码运行的时候我们就可以打开Robomongo去查看当前爬取下来的数据量,它支持三种模式的查看:


图片.png
图片.png
图片.png

  OK,没问题的话就让他一直运行下去,直至把我们需要的数据全部爬下来。虽然MongoDB是一种文件型数据库,但它毕竟不是数据分析工具,接下来,我们需要把这些数据导出为txt、csv、excel等文件类型,方便后续处理。
  重写一段专门用来导出已经存储在test数据库test集合中的数据如下:

# -*- coding: utf-8 -*-
from pymongo import MongoClient
# 建立 MongoDB 数据库连接
conn = MongoClient('localhost', 27017)
# 新建一个test数据库
db = conn.test
Myfile = open('D:\ershoufang.txt', 'w', encoding='utf-8')
coll = db.test.find()
k = 1
for i in coll:
# 把字段名写入文件第一行
    while k == 1:
        m = 1
        for j in i:
            if m < 11:
                Myfile.write(j + "\t")
            else:
                Myfile.write(j + "\n")
            m = m + 1
        k = k + 1
# 把字段值写入文件第2到x行
    n = 1
    for j in i:
        if n < 11:
            Myfile.write(str(i[j]) + "\t")
        else:
            Myfile.write(str(i[j]) + "\n")
        n = n + 1

注意,在windows下面,新文件的默认编码是gbk,这样的话,python解释器会用gbk编码去解析我们的网络数据流txt,然而txt此时已经是decode过的unicode编码,这样的话就会导致解析不了,出现上述问题。 解决的办法就是,改变目标文件的编码:Myfile = open('D:\ershoufang.txt', 'w', encoding='utf-8')中加上encoding='utf-8'。

导出以tab分隔的txt数据:

图片.png

四、后记

到现在为止,简单的网页信息爬取和数据存储基本上可以搞定了,剩下的就是数据的整理和分析。
  当然,其实在爬取网页信息的过程中我们还遇到一些问题:同一个网页每次爬取到的文档数不同,据了解,应该是因为目标网站有反爬虫策略(ban),对方可以根据你的访问频率和IP判断你是爬虫,而不是正常的浏览。知乎大神介绍了3个常用的防ban的策略:

1、设置随机漫步。访问间隔时间可以设为一个服从正态分布的随机数,模拟人类浏览网页的频率。
2、设置爬虫的工作周期。深更半夜就停止工作,早上就开始工作,尽量模拟人类的作息。
3、搭建集群,分布式爬虫。这样访问的IP会不固定,对方不容易判断出你是爬虫。这个开发成本就相对较高了。推荐NUTCH框架。
作者:淡泊明志
链接:https://www.zhihu.com/question/38123412/answer/75123814
来源:知乎

咱们有空再来研究研究~~~待续

上一篇下一篇

猜你喜欢

热点阅读