当前位置: 移动技术网 > IT编程>脚本编程>Python > PYTHON爬虫大作业:豆瓣读书“小说”标签下1000本书籍的爬取与分析

PYTHON爬虫大作业:豆瓣读书“小说”标签下1000本书籍的爬取与分析

2020年07月12日  | 移动技术网IT编程  | 我要评论


本文记录笔者大二下学期选修课数据科学导论的期末大作业,新手小白,才疏学浅,欢迎批评指正。

项目概述

本项目框架如下:

  • 数据爬取
  • 数据分析与可视化
  • 项目报告

数据爬取

import requests
import re
from bs4 import BeautifulSoup
import pymysql.cursors

#给出url,爬取页面信息
def getHTMLText(url, kv):
    try:
        r = requests.get(url, timeout = 30, headers = kv)
        r.raise_for_status()
        #r.encoding = r.apparent_encoding #该语句导致标签显示为乱码
        r.encoding = 'utf-8' 
        return r.text
    except:
        print('爬取失败')
    
#建立表格    
def buildTable():
    conn = pymysql.connect(
        host = '127.0.0.1',
        user = 'root',
        passwd = 'sime1234', #数据库密码
        db = 'douban_novels',
        charset = 'utf8mb4'
        )
    cursor = conn.cursor()
    
    sql_create1 = '''CREATE TABLE NOVELS(
                     NO INT PRIMARY KEY, 
                     TITLE VARCHAR(45), 
                     AUTHOR VARCHAR(45),
                     RATING DECIMAL(2,1),
                     PUBLISHER VARCHAR(45))             
                  '''
    cursor.execute(sql_create1)
    print('BOOKS CREATED')
    
    sql_create2 = '''CREATE TABLE NOVEL_TAGS(
                     BOOKNO INT, 
                     TAG VARCHAR(45))             
                  '''
    cursor.execute(sql_create2)
    print('TAGS CREATED')
    
    cursor.close()
    conn.close()

#给出书籍编号、链接与标题,爬取书籍详情信息,形成字典并返回
def getBookInfo(no, href, title):
    kv = {
        #略。提示:将头文件的头部信息复制过来,加上引号和逗号形成字典,注意要删掉Accept-Encoding一行。
        }
    
    html = getHTMLText(href, kv)
    book = BeautifulSoup(html, 'html.parser')
    
    bookInfo = {'author' : '未知',
                'rating' : 0.0,
                'publisher' : '未知'}
    
    bookInfo['no'] = no
    
    bookInfo['title'] = title
    
    rating = book.find('strong')
    if rating.string:
        try:
            bookInfo['rating'] = float(rating.text)
        except:
            pass
    
    for keyTag in book.find_all(name = 'span', attrs = {'class' : 'pl'}):        
        if re.search(r'作者', str(keyTag.string)): 
            author = keyTag#.parent 
            for i in range(4):
                author = author.next_element        
            author = author.replace('\n', '')
            author = author.replace(' ', '')
            bookInfo['author'] = author
        
    for keyTag in book.find_all(string = '出版社:'):
        publisher = keyTag.parent 
        for i in range(2):
            publisher = publisher.next_element        
        publisher = publisher.replace('\n', '')
        publisher = publisher.replace(' ', '')
        bookInfo['publisher'] = publisher
    
    tags = []
    for tag in book.find_all(attrs = {'class' : 'tag'}):
        tags.append(tag.text)
    print(tags)    
    bookInfo['tags'] = tags
    
    print(bookInfo)    
    return bookInfo

#爬取豆瓣读书小说标签下综合排序前1000本小说的信息     
def getBooks():    
    conn = pymysql.connect(
                            host = '127.0.0.1',
                            user = 'root',
                            passwd = 'sime1234', #数据库密码
                            db = 'douban_novels',
                            charset = 'utf8mb4'
                            )
    cursor = conn.cursor()
        
    no = 0
    start_url = "https://book.douban.com/tag/%E5%B0%8F%E8%AF%B4?start="
    kv = {
		#略。提示:将头文件的头部信息复制过来,加上引号和逗号形成字典,注意要删掉Accept-Encoding一行。
        }    
    
    for i in range(50):
        print('第{0}页'.format(i + 1))
        end_url = str(20 * i)
        html = getHTMLText(start_url + end_url, kv)
        topBooks = BeautifulSoup(html, 'html.parser')
        
        for book in topBooks.find_all('a'):                        
            href = str(book['href'])
            if re.match(r'https://book.douban.com/subject/', href):                                                   
                title = book.get('title')
                if title:
                    no += 1
                   
                    info = getBookInfo(no, href, title)
                                        
                    sql_insert1 = "INSERT INTO NOVELS(NO, TITLE, AUTHOR, RATING, PUBLISHER)\
                                  VALUES({0}, '{1}', '{2}', {3}, '{4}')".format(info['no'], \
                                  info['title'], info['author'], str(info['rating']), info['publisher']) 
                    cursor.execute(sql_insert1)
                    print(('book{0} stored').format(info['no']))
                    
                    tags = info['tags']              
                    for i in range(len(tags)):
                        sql_insert2 = "INSERT INTO NOVEL_TAGS(BOOKNO, TAG) VALUES({0}, '{1}')".format(info['no'], tags[i])
                        cursor.execute(sql_insert2)
                    print(('book{0} tags stored').format(info['no']))
                    conn.commit()    
    
    cursor.close()
    conn.close()
               
if __name__ == '__main__':     
    buildTable()    
    getBooks()    

数据分析与可视化

·书籍标签词云

import pymysql.cursors
import wordcloud
from scipy.misc import imread

#以空格为间隔,将标签连接成一个string
def get_tags_str():
   
   conn = pymysql.connect(
       host = '127.0.0.1',
       user = 'root',
       passwd = 'sime1234', #数据库密码
       db = 'douban_novels',
       charset = 'utf8mb4'
       )
   cursor = conn.cursor()
   
   sql_select = 'select tag from douban_novels.novel_tags'
   cursor.execute(sql_select)
   tags = cursor.fetchall()
   tags_str = ''
   for tag in tags:
       tags_str += tag[0]
       tags_str += ' '
   
   cursor.close()
   conn.close()
   
   return tags_str

#以空格为间隔,将出版社连接成一个string    
def get_publishers_str():
   conn = pymysql.connect(
       host = '127.0.0.1',
       user = 'root',
       passwd = 'sime1234', #数据库密码
       db = 'douban_novels',
       charset = 'utf8mb4'
       )
   cursor = conn.cursor()
   
   sql_select = 'select publisher from douban_novels.novels'
   cursor.execute(sql_select)
   publishers = cursor.fetchall()
   publishers_str = ''
   for publisher in publishers:
       publishers_str += publisher[0]
       publishers_str += ' '
   
   cursor.close()
   conn.close()
   
   return publishers_str

#生成词云
if __name__ == '__main__': 
   #读取卡通书本图片
   mask = imread('book.png')
   #生成并配置wordcloud对象
   w = wordcloud.WordCloud(font_path = 'msyh.ttc', mask = mask,\
                       #width = 2000, height = 1000, \
                       background_color = 'white', max_words = 200)
  #生成标签词云
   tags_str = get_tags_str()
   w.generate(tags_str)
   w.to_file('tags_wordcloud.png')
   print('tags_wordcloud.png generated')
   #生成出版社词云
   publishers_str = get_publishers_str()
   w.generate(publishers_str)
   w.to_file('publishers_wordcloud.png')
   print('publishers_wordcloud.png generated')

·箱线图与直方图

import pymysql.cursors
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.family'] = 'SimHei'

plt.figure(dpi = 600)
#连接数据库
conn = pymysql.connect(
    host = '127.0.0.1',
    user = 'root',
    passwd = 'sime1234', #数据库密码
    db = 'douban_novels',
    charset = 'utf8mb4'
    )
cursor = conn.cursor()


#生成dataframe:图书信息与高分图书信息
sql_select = 'select * from douban_novels.novels'
cursor.execute(sql_select)
title = []
author = []
rating = []
publisher = []
for book in cursor.fetchall():
    title.append(book[1])
    author.append(book[2])
    if book[3] == 0:
        rating.append(None)
    else:
        rating.append(float(book[3]))
    if book[4] == '未知':
        publisher.append(None)
    else:
        publisher.append(book[4])

dic = {'TITLE' : title, 'AUTHOR' : author,\
       'RATING' : rating, 'PUBLISHER' : publisher}

d = pd.DataFrame(dic) #生成dataframe:图书信息
d_high_rating = d[d['RATING'] >= 8.5] #生成dataframe:高分图书信息

#得到图书各类信息的描绘信息
print('\n' + '1000本小说评分统计信息', end = '\n')
d_des = d.describe()
d_des.to_csv('1000本小说评分统计信息.csv') 
print(d_des)

print('\n' + '1000本小说作者统计信息', end = '\n')
d_aut_des = d['AUTHOR'].describe()
d_aut_des.to_csv('1000本小说作者统计信息.csv') 
print(d_aut_des)

print('\n' + '1000本小说出版社统计信息', end = '\n')
d_pls_des = d['PUBLISHER'].describe()
d_pls_des.to_csv('1000本小说出版社统计信息.csv') 
print(d_pls_des)

#图书评分箱线图
rating_box = d.plot.box(title="1000本小说评分箱线图")
plt.grid(linestyle="--", alpha=0.3)
plt.savefig('1000本小说评分箱线图.png')
plt.show()

#将图书信息进行分类统计,统计1000本小说中出现最多的10个出版社的出现次数
pls_group = d_high_rating.groupby('PUBLISHER')
pls_group = pls_group.size().reset_index(name='counts')
pls_group = pls_group.set_index('PUBLISHER')
pls_group = pls_group.sort_values('counts', ascending = False)
pls_group = pls_group[0 : 10]
pls_bar = pls_group.plot.bar() 
plt.title('1000本小说中出现最多的10个出版社的出现次数')
plt.xlabel('出现次数')
plt.ylabel('出版社')
pls_bar.legend_.remove()
plt.savefig('1000本小说中出现最多的10个出版社的出现次数.png', bbox_inches = 'tight')
plt.show()

#将高分图书信息进行分类统计,统计1000本小说中,8.5分以上的小说出现最多的10个出版社的出现次数
pls_group = d.groupby('PUBLISHER')
pls_group = pls_group.size().reset_index(name='counts')
pls_group = pls_group.set_index('PUBLISHER')
pls_group = pls_group.sort_values('counts', ascending = False)
pls_group = pls_group[0 : 10]
pls_bar = pls_group.plot.bar() 
plt.title('1000本小说中,8.5分以上的小说出现最多的10个出版社的出现次数')
plt.xlabel('出现次数')
plt.ylabel('出版社')
pls_bar.legend_.remove()
plt.savefig('1000本小说中,8.5分以上的小说出现最多的10个出版社的出现次数.png', bbox_inches = 'tight')
plt.show()

#生成datframe:图书标签与高分图书标签
sql_select = '''select * from douban_novels.novels, douban_novels.novel_tags
                where douban_novels.novels.NO = douban_novels.novel_tags.BOOKNO;'''
cursor.execute(sql_select)
title = []
author = []
rating = []
publisher = []
tag = []
for book in cursor.fetchall():
    title.append(book[1])
    author.append(book[2])
    if book[3] == 0:
        rating.append(None)
    else:
        rating.append(float(book[3]))
    if book[4] == '未知':
        publisher.append(None)
    else:
        publisher.append(book[4])
    tag.append(book[6])

dic = {'TITLE' : title, 'AUTHOR' : author,\
       'RATING' : rating, 'PUBLISHER' : publisher, 'TAG' : tag}
d = pd.DataFrame(dic) #生成datframe:图书标签
d_high_rating = d[d['RATING'] >= 8.5] #生成datframe:高分图书标签

#得到图书标签的描绘信息
print('\n' + '1000本小说标签统计信息', end = '\n')
d_tags_des = d['TAG'].describe()
d_tags_des.to_csv('1000本小说标签统计信息.csv') 
print(d_tags_des)

#将图书标签进行分类统计,统计1000本小说中出现最多的10个标签的出现次数
tags_group = d.groupby('TAG')
tags_group = tags_group.size().reset_index(name='counts')
tags_group = tags_group.sort_values('counts', ascending = False)
tags_group = tags_group.set_index('TAG')
tags_top10 = tags_group[0 : 10]
tags_top10_bar = tags_top10.plot.bar() 
plt.title('1000本小说中出现最多的10个标签的出现次数')
plt.xlabel('出现次数')
plt.ylabel('标签')
tags_top10_bar.legend_.remove()
plt.savefig('1000本小说中出现最多的10个标签的出现次数.png', bbox_inches = 'tight')
plt.show()

#将高分图书标签进行分类统计,统计1000本小说中,8.5分以上的小说出现最多的10个标签的出现次数
tags_group = d_high_rating.groupby('TAG')
tags_group = tags_group.size().reset_index(name='counts')
tags_group = tags_group.sort_values('counts', ascending = False)
tags_group = tags_group.set_index('TAG')
tags_top10 = tags_group[0 : 10]
tags_top10_bar = tags_top10.plot.bar() 
plt.title('1000本小说中,8.5分以上的小说出现最多的10个标签的出现次数')
plt.xlabel('出现次数')
plt.ylabel('标签')
tags_top10_bar.legend_.remove()
plt.savefig('1000本小说中,8.5分以上的小说出现最多的10个标签的出现次数.png', bbox_inches = 'tight')
plt.show()

cursor.close()
conn.close()

·关联分析

import pymysql.cursors
from efficient_apriori import apriori

if __name__ == '__main__':
   conn = pymysql.connect(
       host = '127.0.0.1',
       user = 'root',
       passwd = 'sime1234', #数据库密码
       db = 'douban_novels',
       charset = 'utf8mb4'
       )
   cursor = conn.cursor()
   
   sql_select = '''select douban_novels.novels.publisher, douban_novels.novel_tags.tag 
                   from douban_novels.novels, douban_novels.novel_tags
                   where douban_novels.novels.NO = douban_novels.novel_tags.BOOKNO;'''
   cursor.execute(sql_select)
   transactions = list(cursor.fetchall())
   itemsets, rules = apriori(transactions, min_support = 0.003, min_confidence = 0.1)
   print(rules)
   
   with open('出版社与标签的关联.txt', 'w') as f:
       for rule in rules:
           f.write(str(rule) + '\n')
   
   cursor.close()
   conn.close()

项目报告

摘要

随着数码产品的流行,人们能获得的娱乐形式越来越多样化,影视、音乐、社交媒体等等精彩刺激的娱乐形式,无疑对人们的阅读习 惯造成了极大的冲击。此外,电子书成为了很多人阅读的首选资源,快节奏的生活也使人们的阅读偏好更加偏向于碎片化的阅读。在这个时候,国人的阅读口味究竟呈现出怎样的状态?现在在国内流行的都是什么样的书籍?书籍是我们的精神食粮,通过国人的读偏好,我们得以窥见当代人的精神世界一角,并能从中得到启示与 反思。在本项目中,笔者爬取了豆瓣读书中“小说”标签下,按照综合 排序的前 1000 本小说的信息,并对数据进行分析与可视化,对国人小说阅读偏好做初步的分析。
关键词:爬虫 阅读偏好 数据分析 数据可视化

一、数据爬取

本文选择爬取的网站为豆瓣读书,具体爬取对象为:豆瓣读书的小说标签下, 按综合排序的前 1000 本小说。
具体爬取过程如下:

  1. 爬取豆瓣读书的小说标签下前 50 页的内容;
  2. 从爬取的内容中:
    (1) 遍历名称为’a’的标签,找出链接前缀为’https://book.douban.com/subject/’ 的标签;
    (2) 标签的’title’属性作为书籍的标题;
    (3) 对书籍详情链接进行进一步爬取;
  3. 爬取书籍详情页面:
    (1) 将上一步中得到的’title’属性作为书籍标题存入字典;
    (2) 查找名称为’strong’的标签,将标签的 text 属性转化为 float 类型,作为书 籍评分存入字典;
    (3) 查找名称为’span’,’class’属性为’pl’的标签,在查找结果中查找标签的 strng 属性为’作者:’的标签,选取标签后的第 4 个元素作为作者存入字 典;查找标签的 string 属性’出版社’的标签,选取标签后的第 2 个元素作 为出版社存入字典;
    (4) 在爬取过程中,发现部分作者标签的 string 类型为“作者”,缺失了冒 号,于是将查找条件修改为:匹配 string 属性含有’作者’字符串的标签;
    (5) 查找’class’属性为’tag’的标签,将这些标签的’text’属性存储在一个列表 中,然后将列表存入字典;
    (6) 将字典存入数据库

二、数据预处理

在爬取过程中,发现部分书籍的评分或者出版社有缺失的情况,于是:
1.将评分缺失的书籍评分记为 0.0 分;
2.将出版社缺失的书籍记为’未知’;
3.在读取数据库数据并生成 data frame 时,将评分为 0.0,以及出版社为’未知’ 的项读取为 None

三、数据存储

本项目选取的存储方式为存入 mysql 数据库。
以下为数据集介绍:
数据库

数据库名称 douban_novels
表格 1 novels
表格 2 novel_tags

书籍信息数据集(不含标签)
表格名称:novels
共包含 1000 条数据

字段名 说明
NO 书籍编号,按照网页上书籍排列顺序编号,从 1 到 1000
TITLE 书籍标题
AUTHOR 书籍作者
RATING 书籍评分
PUBLISHER 书籍出版社

书籍标签数据集
表格名称:novel_tags
共包含 8000 条数据

字段名 说明
BOOKNO 书籍编号(与 novels 表格对应)
TAG 书籍标签

四、数据分析

本项目的数据分析内容包括:
1.小说统计信息
(1)评分统计信息(生成 csv 文件)
(2)作者统计信息(生成 csv 文件)
(3)出版社统计信息(生成 csv 文件)
(4)标签统计信息(生成 csv 文件)
2.小说评分分布(生成箱线图)
3.最受欢迎的小说出版社与标签(生成词云)
4.最受欢迎的小说出版社与标签(生成直方图)
5.高分小说中最受欢迎的出版社与标签(生成直方图)
6.出版社与标签关联分析(生成 txt 文件)

以上各项数据分析使用的分析方法:

1、2、4、5: 主要工具:pandas 库、matplotlib 库
过程:

  1. 分别生成 4 个 dataframe,分别为图书信息(不含标签)、高分图书信息 (不含标签)、图书标签、高分图书标签;
  2. 对图书信息(不含标签)与图书标签两个 dataframe 使用 describe()函数 得到统计信息并存为 csv 文件
  3. 通过 groupby 函数,得到各个 dataframe 中各出版社、标签出现次数,生 成直方图并保存

3(生成词云):
主要工具:wordcloud 库
过程:

  1. 分别将出版社和标签,以空格为分隔,形成字符串
  2. 通过 wordcloud 库的 WordCloud 对象分别生成两个词云并保存

6(出版社与标签关联分析):
主要工具:efficient_apriori 库
过程:

  1. 将每本小说的出版社与各个标签分别形成的元组存为列表
  2. 通过 efficient_apriori 库的 apriori 类进行分析,得到关联规则,输出并存 为 txt 文件

五、结果与可视化

1.小说统计信息
(1)评分统计信息(生成 csv 文件)
评分统计信息
(2)作者统计信息(生成 csv 文件)
作者统计信息
(3)出版社统计信息(生成 csv 文件)
出版社统计信息
(4)标签统计信息(生成 csv 文件)
标签统计信息
2.小说评分分布(生成箱线图)
小说评分分布
3.最受欢迎的小说出版社与标签(生成词云)
出版社词云
标签词云
4.最受欢迎的小说出版社与标签(生成直方图)
最受欢迎的10个出版社
最受欢迎的10个标签
5.高分小说中最受欢迎的出版社与标签(生成直方图)
8.5分以上小说中最受欢迎出版社
8.5分以上小说中最受欢迎标签
6.出版社与标签关联分析(生成 txt 文件)
{东野圭吾} -> {南海出版公司} (conf: 0.718, supp: 0.004, lift: 9.447, conv: 3.276)
{推理} -> {南海出版公司} (conf: 0.240, supp: 0.004, lift: 3.162, conv: 1.216)
{日本文学} -> {南海出版公司} (conf: 0.323, supp: 0.005, lift: 4.251, conv: 1.365)
{日本} -> {南海出版公司} (conf: 0.311, supp: 0.006, lift: 4.090, conv: 1.341)
{南海出版公司} -> {小说} (conf: 0.113, supp: 0.009, lift: 0.968, conv: 0.996)
{北京联合出版公司} -> {小说} (conf: 0.117, supp: 0.004, lift: 0.995, conv: 0.999)
{经典} -> {人民文学出版社} (conf: 0.233, supp: 0.005, lift: 2.454, conv: 1.180)
{人民文学出版社} -> {小说} (conf: 0.111, supp: 0.011, lift: 0.943, conv: 0.992)
{中国} -> {人民文学出版社} (conf: 0.170, supp: 0.003, lift: 1.787, conv: 1.090)
{外国文学} -> {人民文学出版社} (conf: 0.121, supp: 0.006, lift: 1.278, conv: 1.030)
{中国文学} -> {人民文学出版社} (conf: 0.195, supp: 0.004, lift: 2.057, conv: 1.125)
{文学} -> {人民文学出版社} (conf: 0.103, supp: 0.004, lift: 1.088, conv: 1.009)
{北京十月文艺出版社} -> {小说} (conf: 0.125, supp: 0.004, lift: 1.066, conv: 1.009)
{译林出版社} -> {外国文学} (conf: 0.114, supp: 0.006, lift: 2.349, conv: 1.074)
{外国文学} -> {译林出版社} (conf: 0.129, supp: 0.006, lift: 2.349, conv: 1.085)
{译林出版社} -> {小说} (conf: 0.123, supp: 0.007, lift: 1.047, conv: 1.006)
{小说} -> {上海译文出版社} (conf: 0.102, supp: 0.012, lift: 1.055, conv: 1.006)
{上海译文出版社} -> {小说} (conf: 0.124, supp: 0.012, lift: 1.055, conv: 1.007)
{外国文学} -> {上海译文出版社} (conf: 0.209, supp: 0.010, lift: 2.158, conv: 1.142)
{上海译文出版社} -> {外国文学} (conf: 0.104, supp: 0.010, lift: 2.158, conv: 1.063)
{文学} -> {上海译文出版社} (conf: 0.143, supp: 0.006, lift: 1.473, conv: 1.053)
{湖南文艺出版社} -> {小说} (conf: 0.121, supp: 0.004, lift: 1.034, conv: 1.005)
{江苏凤凰文艺出版社} -> {小说} (conf: 0.117, supp: 0.004, lift: 1.001, conv: 1.000)
{上海文艺出版社} -> {小说} (conf: 0.116, supp: 0.003, lift: 0.990, conv: 0.999)
{新星出版社} -> {小说} (conf: 0.121, supp: 0.003, lift: 1.028, conv: 1.004)

六、结论

从数据分析与可视化的结果中,我们得出如下结论:
豆瓣读书“小说”标签下,按照综合排序前 1000 本小说具有这样的特征:

  1. 整体评分较高,平均评分为 8.37,但也有少数评分较低的小说,说明受欢迎的的小说大多为高分优质小说,但也有少量低分作品受到欢迎;
  2. 最受欢迎的作者、出版社、标签分别为东野圭吾、上海译文出版社以及“小说”;
  3. 比较最受欢迎的小说出版社与高分小说中最受欢迎的小说出版社,榜首几家出版社变化不大(上海译文出版社、人民文学出版社等出版社都是 名声大、作品质量高的出版社),说明这几个高质量的出版社受到欢迎;其余的出版社在两图中有相对明显差异,高分图书中文艺类出版社更常见,说明这几个文艺类出版社出版小说质量更佳却没有那么受欢迎;
  4. 比较最受欢迎的小说标签与高分小说中最受欢迎的小说标签,总体差异不大,大多为各国文学的标签。值得注意的是推理小说在最受欢迎的10个小说标签中出现,却没有在高分小说中最受欢迎的 10 个小说标签中出现,说明推理类小说虽受欢迎,总体质量相对其它文学类书籍较低;
  5. 从关联分析可知,最热门的几个出版社的最受欢迎的小说里,上海 译文出版社与译林出版社出版作品多为外国文学,人民文学出版社出版作品 则包括了各国作品,南海出版集团出版了较多的日本推理小说(尤其是东野圭吾的小说)并受到欢迎。

本文地址:https://blog.csdn.net/weixin_44102283/article/details/107252665

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网