博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【爬虫】应用Python爬虫爬取豆瓣租房上的帖子信息
阅读量:4083 次
发布时间:2019-05-25

本文共 18282 字,大约阅读时间需要 60 分钟。

:https://github.com/Donvink/Spider.BC

:https://b23.tv/waSfUa
:https://blog.csdn.net/sinat_16020825/article/details/108538779

目录

一、项目简介

1.1 简介

    本项目应用Python爬虫、Flask框架、Echarts、WordCloud等技术将豆瓣租房信息爬取出来保存于Excel和数据库中,进行数据可视化操作、制作网页展示。

    主要内容包括三部分:

  • :Python 爬虫将 上的租房信息爬取出来,解析数据后将其存储于Excel和SQLite数据库中。
  • :测试使用Flask框架。
  • :应用Flask框架、Echarts、WordCloud技术将数据库中的租房信息以网页的形式展示出来。

1.2 Results

1.2.1 Excel保存数据

EXCEL

1.2.2 网页展示数据

  • 首页

    首页

  • 帖子列表

    帖子列表

  • 词云

    词云


作者 Donvink

二、代码分析

2.1 概述

2.1.1 robots协议

    在爬取数据之前,必须先查看目标网站的robots协议,查阅网站允许和禁止的爬取的数据,对于网站禁止爬虫访问的数据,一定不要去爬取,否则严重的话需要承担相应的法律责任。网站的robots协议地址一般是:https://网站地址/robots.txt。

    豆瓣robots协议地址为:https://www.douban.com/robots.txt。

robots协议

    原则上禁止访问robots协议上disallow的地址。

2.1.2 爬取流程

    准备工作 -> 获取数据 -> 解析内容 -> 保存数据

    调用的库:

import sysfrom bs4 import BeautifulSoup   # 网页解析,获取数据import re                       # 正则表达式,进行文件匹配import urllib                   # 制定URL,获取网页数据import urllib.requestimport urllib.errorimport xlwt                     # 进行excel操作import sqlite3                  # 进行SQLite数据库操作

需要安装requests、urllib3、xlwt和pysqlite3库,在终端执行一下命令。

pip install requests urllib3 xlwt pysqlite3

2.2 准备工作

    URL分析:

    首页 https://www.douban.com/group/558444/ (包含50条数据,和第一第二页数据相同);

    第一页 https://www.douban.com/group/558444/discussion?start=0;

    第二页 https://www.douban.com/group/558444/discussion?start=25。

    总结:

    1)页面包含x条租房数据,每页25条,

    2)每页的URL的不同之处:最后的数值 = (页数 - 1) * 25。

# 1.爬取网页    2、逐一解析网页数据    baseurl = 'https://www.douban.com/group/558444/discussion?start='   # 基本URL    pagecount = 10                                                      # 爬取的网页数量    num = 25                                                            # 每页的帖子数    datalist = getData(baseurl, pagecount, num)                         # 爬取网页、解析数据        # 调用获取页面信息的函数pagecount次    for i in range(0, pagecount):        url = baseurl + str(i * num)                    # 拼接网页链接        # 1.爬取网页        html = askURL(url)                              # 保存获取到的网页源码

2.2.1 分析页面

    1)借助Chrome开发者工具(F12)来分析页面,在Elements下找到需要的数据位置。

    2)在页面中选择一个元素以进行检查(Ctrl+Shift+C)(开发者工具最左上方的小箭头),点击页面内容即可定位到具体标签位置。

    3)点击Network,可以查看每个时间点发送的请求和交互情况,可点击最上方小红点(停止记录网络日志Ctrl+E)停止交互。

    4)Headers查看发送给服务器的命令情况。

    5)服务器返回信息可在Response中查看。

2.2.2 编码规范

    1)一般Python程序第一行需要加入

# -*- coding = utf-8 -*-

    这样可以在代码中包含中文。

    2)使用函数实现单一功能或相关联功能的代码段,可以提高可读性和代码重复利用率。

    3)Python文件中可以加入main函数用于测试程序

if __name__ == "__main__":

    4)Python使用#添加注释,说明代码(段)的作用。

2.2.3 引入模块

    sys, bs4 -> BeautifulSoup, re, urllib, xlwt。

2.3 获取数据

    python一般使用urllib库获取页面。

    获取页面数据:

    1)对每一个页面,调用askURL函数获取页面内容

def askURL(url):    """    得到指定一个URL的网页信息    :param url: 网页链接    :return:    """

    2)定义一个获取页面的函数askURL,传入一个url参数,表示网址,如https://www.douban.com/group/558444/discussion?start=0

# 模拟浏览器头部信息,向豆瓣服务器发送消息    head = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37' } # 用户代理,表示告诉豆瓣服务器,我们是什么类型的机器、浏览器(本质上是告诉浏览器,我们可以接收什么水平的文件内容)

    3)urllib.Request生成请求,urllib.urlopen发送请求获取响应,read获取页面内容

request = urllib.request.Request(url, headers=head)     # 发送请求    html = ''

    4)在访问页面时经常会出现错误,为了程序正常运行,假如异常捕获try…except…语句

# 捕获异常    try:        response = urllib.request.urlopen(request)          # 取得响应        html = response.read().decode('utf-8')              # 获取网页内容        # print(html)    except urllib.error.URLError as e:                      # 若发生异常,则打印相关信息        if hasattr(e, 'code'):                              # 异常代码            print(e.code)        if hasattr(e, 'reason'):                            # 异常原因            print(e.reason)    return html

2.4 解析内容

    对爬取的html文件进行解析

    1、使用BeautifulSoup定位特定的标签位置

# 2.逐一解析数据        soup = BeautifulSoup(html, 'html.parser')       # 对网页源码进行解析        # print(soup)                                   # 测试html是否能被解析

    2、使用正则表达式找到具体的内容

for item in soup.find_all('tr', class_=''):     # 找到每一个帖子选项(查找符合要求的字符串,形成列表)            # print(item)                               # 测试:查看item全部信息            data = []            item = str(item)                            # 转换成字符串            # print(item)                               # 测试item            # 帖子名            title = re.findall(findTitle, item)[0]            # print(title)                              # 测试:查看title            data.append(title)                          # 添加帖子名            # 帖子详情链接            link = re.findall(findLink, item)            # print(link)                               # 测试:查看link信息            # 进入帖子里爬取租房信息介绍内容和照片            info, imglink = getInfo(link[0])            data.append(info)                           # 添加详细信息            data.append(imglink)                        # 添加图片链接            if (len(link) == 2):                data.append(link[0])                    # 添加详情链接                # alink = re.findall(findLink, item)[1]                # print(alink)                          # 测试:查看link信息                data.append(link[1])                    # 添加发帖人主页链接            else:                data.append(link[0])                data.append(' ')                        # 留空            # 回帖数            rcount = re.findall(findRCount, item)[0]            # print(rcount)                             # 测试:查看回帖数            if rcount == '':                # print(0)                              # 测试:查看回帖数                data.append(0)                          # 添加回帖数            else:                # print(rcount)                         # 测试:查看回帖数                data.append(int(rcount))                # 添加回帖数            # 最后回帖时间            rtime = re.findall(findRTime, item)[0]            # print(rtime)            data.append(rtime)                          # 添加最后回帖时间            # otitle = titles[1].replace('/', '')       # 去掉无关符号,或re.sub()            datalist.append(data)                       # 把处理好的信息放入datalist    # print(datalist)                                   # 测试

2.5 保存数据

    Excel表格存储:利用python库xlwt将抽取的数据datalist写入Excel表格。

def saveData(datalist, pagecount, num, savepath):    """    将解析后的数据保存在Excel文件中    :param datalist: 网页解析后的数据    :param pagecount: 爬取的网页数量    :param num: 每页的帖子数    :param savepath: Excel文件保存路径    :return:    """    # print('saving...')    book = xlwt.Workbook(encoding='utf-8', style_compression=0)     # 新建workbook    sheet = book.add_sheet('豆瓣租房信息', cell_overwrite_ok=True)    # 添加sheet    # 列名    col = ('序号', '帖子名', '详细介绍', '图片链接', '帖子详情链接', '发帖人主页链接', '回帖数', '最后回帖时间')    # 写入列名    for i in range(0, len(col)):        sheet.write(0, i, col[i])    # 将每条帖子的相关内容写入Excel对应行中    for i in range(0, pagecount*num):        # print('第%d条' % (i+1))        data = datalist[i]        sheet.write(i+1, 0, i+1)            # 写入序号        for j in range(0, len(data)):            sheet.write(i+1, j+1, data[j])  # 写入数据    book.save(savepath)                     # 保存文件    # print('Successful!')

    数据库存储

def saveData2DB(datalist, dbpath):    """    将解析后的数据保存在数据库文件中    :param datalist: 网页解析后的数据    :param dbpath: 数据库文件保存路径    :return:    """    init_DB(dbpath)                                 # 初始化数据库    conn = sqlite3.connect(dbpath)                  # 连接数据库    cur = conn.cursor()                             # 获取游标    # 将数据逐一保存到数据库中    for data in datalist:        for index in range(len(data)):            if index != 5:                          # index为5的数据类型是int                data[index] = '"'+data[index]+'"'   # 每项的字符串需要加上双引号或单引号        # 插入字符串,以逗号隔开        sql = '''            insert into renting(            title, introduction, img_link, title_link, person_link, re_count, re_time)            values(%s)''' % ",".join(str(v) for v in data)        # print(sql)        cur.execute(sql)                            # 执行数据库操作        conn.commit()                               # 提交    cur.close()                                     # 关闭游标    conn.close()                                    # 关闭连接

2.7 网页展示数据

待更新

2.8 TODO

  • 添加筛选:区域、小区名、地铁站等
  • 定义类,获取各种不同途径信息(豆瓣、自如、链家、贝壳等)

三、完整代码

# -*- coding = utf-8 -*-# @Time: 2020/5/30 19:57# @Author: Donvink# @File: douban.py# @Software: PyCharm"""查看robots协议:https://www.douban.com/robots.txt零、流程    准备工作 -> 获取数据 -> 解析内容 -> 保存数据一、准备工作    URL分析:        首页  https://www.douban.com/group/558444/ (包含50条数据,和第一第二页数据相同)        第一页 https://www.douban.com/group/558444/discussion?start=0        第二页 https://www.douban.com/group/558444/discussion?start=25        1)页面包含x条租房数据,每页25条        2)每页的URL的不同之处:最后的数值 = (页数 - 1) * 25    1、分析页面        1)借助Chrome开发者工具(F12)来分析页面,在Elements下找到需要的数据位置。        2)在页面中选择一个元素以进行检查(Ctrl+Shift+C)(开发者工具最左上方的小箭头),点击页面内容即可定位到具体标签位置。        3)点击Network,可以查看每个时间点发送的请求和交互情况,可点击最上方小红点(停止记录网络日志Ctrl+E)停止交互。        4)Headers查看发送给服务器的命令情况。        5)服务器返回信息可在Response中查看。    2、编码规范        1)一般Python程序第一行需要加入                    # -*- coding = utf-8 -*-        这样可以在代码中包含中文。        2)使用函数实现单一功能或相关联功能的代码段,可以提高可读性和代码重复利用率。        3)Python文件中可以加入main函数用于测试程序                    if __name__ == "__main__":        4)Python使用#添加注释,说明代码(段)的作用    3、引入模块        sys, bs4 -> BeautifulSoup, re, urllib, xlwt二、获取数据    python一般使用urllib库获取页面    获取页面数据:        1)对每一个页面,调用askURL函数获取页面内容        2)定义一个获取页面的函数askURL,传入一个url参数,表示网址,如https://www.douban.com/group/558444/discussion?start=0        3)urllib.Request生成请求,urllib.urlopen发送请求获取响应,read获取页面内容        4)在访问页面时经常会出现错误,为了程序正常运行,假如异常捕获try...except...语句三、解析内容    对爬取的html文件进行解析    1、使用BeautifulSoup定位特定的标签位置    2、使用正则表达式找到具体的内容四、保存数据    Excel表格存储:利用python库xlwt将抽取的数据datalist写入Excel表格TODO:    1、添加筛选【区域,小区名,地铁站】    2、定义类,获取各种不同途径信息(豆瓣,自如,链家,贝壳)"""import sysfrom bs4 import BeautifulSoup   # 网页解析,获取数据import re                       # 正则表达式,进行文件匹配import urllib                   # 制定URL,获取网页数据import urllib.requestimport urllib.errorimport xlwt                     # 进行excel操作import sqlite3                  # 进行SQLite数据库操作def main():    """    主函数入口    1.爬取网页    2.逐一解析数据    3.保存数据    :return:    """    print('开始爬取······')    # 1.爬取网页    2、逐一解析网页数据    baseurl = 'https://www.douban.com/group/558444/discussion?start='   # 基本URL    pagecount = 10                                                      # 爬取的网页数量    num = 25                                                            # 每页的帖子数    datalist = getData(baseurl, pagecount, num)                         # 爬取网页、解析数据    # 3.保存数据    savepath = 'douban_renting.xls'                                     # Excel文件保存路径    saveData(datalist, pagecount, num, savepath)                        # 将数据保存在Excel中    dbpath = 'douban_renting.db'                                        # 数据库文件保存路径    saveData2DB(datalist, dbpath)                                       # 将数据保存在数据库中    # askURL('https://www.douban.com/group/558444/discussion?start=0')  # 测试askURL    print('爬取完毕!')def getData(baseurl, pagecount, num):    """    爬取网页,逐一对网页数据进行分析。主要内容有:            '帖子名', '详细介绍', '图片链接', '帖子详情链接', '发帖人主页链接', '回帖数', '最后回帖时间'    :param baseurl: 基本URL    :param pagecount: 爬取的网页数量    :param num: 每页的帖子数    :return:    """    datalist = []    # 正则表达式规则    findTitle = re.compile(r'title="(.*?)"', re.S)                  # 找到帖子名,有的帖子名带有\n    findLink = re.compile(r'href="(.*?)"')                          # 找到帖子详情链接    findRCount = re.compile(r'<.*class="r-count".*>(.*?)')     # 找到回帖数    findRTime = re.compile(r'<.*class="time".*">(.*?)')        # 找到最后回帖时间    # 注意:正则表达式匹配空格时,需用.*或\s匹配    # 调用获取页面信息的函数pagecount次    for i in range(0, pagecount):        url = baseurl + str(i * num)                    # 拼接网页链接        # 1.爬取网页        html = askURL(url)                              # 保存获取到的网页源码        # 2.逐一解析数据        soup = BeautifulSoup(html, 'html.parser')       # 对网页源码进行解析        # print(soup)                                   # 测试html是否能被解析        for item in soup.find_all('tr', class_=''):     # 找到每一个帖子选项(查找符合要求的字符串,形成列表)            # print(item)                               # 测试:查看item全部信息            data = []            item = str(item)                            # 转换成字符串            # print(item)                               # 测试item            # 帖子名            title = re.findall(findTitle, item)[0]            # print(title)                              # 测试:查看title            data.append(title)                          # 添加帖子名            # 帖子详情链接            link = re.findall(findLink, item)            # print(link)                               # 测试:查看link信息            # 进入帖子里爬取租房信息介绍内容和照片            info, imglink = getInfo(link[0])            data.append(info)                           # 添加详细信息            data.append(imglink)                        # 添加图片链接            if (len(link) == 2):                data.append(link[0])                    # 添加详情链接                # alink = re.findall(findLink, item)[1]                # print(alink)                          # 测试:查看link信息                data.append(link[1])                    # 添加发帖人主页链接            else:                data.append(link[0])                data.append(' ')                        # 留空            # 回帖数            rcount = re.findall(findRCount, item)[0]            # print(rcount)                             # 测试:查看回帖数            if rcount == '':                # print(0)                              # 测试:查看回帖数                data.append(0)                          # 添加回帖数            else:                # print(rcount)                         # 测试:查看回帖数                data.append(int(rcount))                # 添加回帖数            # 最后回帖时间            rtime = re.findall(findRTime, item)[0]            # print(rtime)            data.append(rtime)                          # 添加最后回帖时间            # otitle = titles[1].replace('/', '')       # 去掉无关符号,或re.sub()            datalist.append(data)                       # 把处理好的信息放入datalist    # print(datalist)                                   # 测试    return datalistdef getInfo(url):    """    进入每个帖子里爬取租房信息介绍内容和照片链接    :param url: 帖子URL    :return:    """    tempinfo = []    info = ''                                               # 租房详细信息介绍    tempimglink = []    imglink = ''                                            # 图片链接    # 正则表达式规则    findInfo = re.compile(r'

(.*?)

', re.S) # 找到帖子详情信息,有的帖子名带有\n findImgLink = re.compile(r'src="(.*?)"') # 找到帖子附带的照片链接 html = askURL(url) # 保存获取到的网页源码 soup = BeautifulSoup(html, 'html.parser') # 解析网页源码 topic = soup.find_all('div', class_='topic-richtext') # 找到介绍内容和照片所在标签 topic = str(topic) # 转换为字符串 # 详细介绍 tempinfo = re.findall(findInfo, topic) info = " , ".join(str(v) for v in tempinfo) # 帖子里详细介绍内容会有换行,用逗号隔开 # 照片链接 tempimglink = re.findall(findImgLink, topic) imglink = " , ".join(str(v) for v in tempimglink) # 每个帖子可能会附带多张照片,用逗号隔开 return info, imglinkdef askURL(url): """ 得到指定一个URL的网页信息 :param url: 网页链接 :return: """ # 模拟浏览器头部信息,向豆瓣服务器发送消息 head = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37' } # 用户代理,表示告诉豆瓣服务器,我们是什么类型的机器、浏览器(本质上是告诉浏览器,我们可以接收什么水平的文件内容) request = urllib.request.Request(url, headers=head) # 发送请求 html = '' # 捕获异常 try: response = urllib.request.urlopen(request) # 取得响应 html = response.read().decode('utf-8') # 获取网页内容 # print(html) except urllib.error.URLError as e: # 若发生异常,则打印相关信息 if hasattr(e, 'code'): # 异常代码 print(e.code) if hasattr(e, 'reason'): # 异常原因 print(e.reason) return htmldef saveData(datalist, pagecount, num, savepath): """ 将解析后的数据保存在Excel文件中 :param datalist: 网页解析后的数据 :param pagecount: 爬取的网页数量 :param num: 每页的帖子数 :param savepath: Excel文件保存路径 :return: """ # print('saving...') book = xlwt.Workbook(encoding='utf-8', style_compression=0) # 新建workbook sheet = book.add_sheet('豆瓣租房信息', cell_overwrite_ok=True) # 添加sheet # 列名 col = ('序号', '帖子名', '详细介绍', '图片链接', '帖子详情链接', '发帖人主页链接', '回帖数', '最后回帖时间') # 写入列名 for i in range(0, len(col)): sheet.write(0, i, col[i]) # 将每条帖子的相关内容写入Excel对应行中 for i in range(0, pagecount*num): # print('第%d条' % (i+1)) data = datalist[i] sheet.write(i+1, 0, i+1) # 写入序号 for j in range(0, len(data)): sheet.write(i+1, j+1, data[j]) # 写入数据 book.save(savepath) # 保存文件 # print('Successful!')def saveData2DB(datalist, dbpath): """ 将解析后的数据保存在数据库文件中 :param datalist: 网页解析后的数据 :param dbpath: 数据库文件保存路径 :return: """ init_DB(dbpath) # 初始化数据库 conn = sqlite3.connect(dbpath) # 连接数据库 cur = conn.cursor() # 获取游标 # 将数据逐一保存到数据库中 for data in datalist: for index in range(len(data)): if index != 5: # index为5的数据类型是int data[index] = '"'+data[index]+'"' # 每项的字符串需要加上双引号或单引号 # 插入字符串,以逗号隔开 sql = ''' insert into renting( title, introduction, img_link, title_link, person_link, re_count, re_time) values(%s)''' % ",".join(str(v) for v in data) # print(sql) cur.execute(sql) # 执行数据库操作 conn.commit() # 提交 cur.close() # 关闭游标 conn.close() # 关闭连接def init_DB(dbpath): """ 初始化数据库 :param dbpath: 数据库保存路径 :return: """ # create table renting # 若不加if not exists,则每次运行程序需要先删除database;否则不用先删除,但无法更新sql里的格式 sql = ''' create table if not exists renting ( id integer primary key autoincrement, title text, introduction text, img_link text, title_link text, person_link text, re_count numeric, re_time text ) ''' # 创建数据表 conn = sqlite3.connect(dbpath) # 创建或连接数据库 cursor = conn.cursor() # 获取游标 cursor.execute(sql) # 执行数据库操作 conn.commit() # 提交 cursor.close() # 关闭游标 conn.close() # 关闭if __name__ == "__main__": # 当程序执行时 # 调用函数 main() # init_DB('租房.db') # 测试初始化数据库
你可能感兴趣的文章