上次使用了BeautifulSoup库爬取电影排行榜,爬取相对来说有点麻烦,爬取的速度也较慢。本次使用的lxml库,我个人是最喜欢的,爬取的语法很简单,爬取速度也快。
本次爬取的豆瓣书籍排行榜的首页地址是:
https://www.douban.com/doulist/1264675/?start=0&sort=time&playable=0&sub_type=
该排行榜一共有22页,且发现更改网址的 start=0 的 0 为25、50就可以跳到排行榜的第二、第三页,所以后面只需更改这个数字然后通过遍历就可以爬取整个排行榜的书籍信息。
本次爬取的内容有书名、评分、评价数、出版社、出版年份以及书籍封面图,封面图保存为图片,其他数据存为csv文件,方面后面读取分析。
本次的项目步骤:一、分析网页,确定爬取数据
二、使用lxml库爬取内容并保存
三、读取数据并选择部分内容进行分析
- 步骤一:
分析网页源代码可以看到,书籍信息在属性为 class="doulist-item"的div标签中,打开发现,我们需要爬取的信息都在标签内部,通过xpath语法我们可以很简便的爬取所需内容。
(书籍各类信息所在标签)
所需爬取的内容在 class为post、title、rating、abstract的div标签中。
- 步骤二:
- 先定义爬取函数,爬取所需内容
- 执行函数,并存入csv文件
具体代码如下: 注:转载代码请标明出处
1 import requests
2 from lxml import etree
3 import time
4 import csv
5
6 #信息头
7 headers = {
8 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'
9 }
10
11 #定义爬取函数
12 def douban_booksrank(url):
13 res = requests.get(url, headers=headers)
14 selector = etree.HTML(res.text)
15 contents = selector.xpath('//div[@class="article"]/div[contains(@class,"doulist-item")]') #循环点
16 for content in contents:
17 try:
18 title = content.xpath('div/div[2]/div[3]/a/text()')[0] #书名
19 scores = content.xpath('div/div[2]/div[4]/span[2]/text()') #评分
20 scores.append('9.0') #因为有一些书没有评分,导致列表为空,此处添加一个默认评分,若无评分则默认为9.0
21 score = scores[0]
22 comments = content.xpath('div/div[2]/div[4]/span[3]/text()')[0] #评论数量
23 author = content.xpath('div/div[2]/div[5]/text()[1]')[0] #作者
24 publishment = content.xpath('div/div[2]/div[5]/text()[2]')[0] #出版社
25 pub_year = content.xpath('div/div[2]/div[5]/text()[3]')[0] #出版时间
26 img_url = content.xpath('div/div[2]/div[2]/a/img/@src')[0] #书本图片的网址
27 img = requests.get(img_url) #解析图片网址,为下面下载图片
28 img_name_file = 'C:/Users/lenovo/Desktop/douban_books/{}.png'.format((title.strip())[:3]) #图片存储位置,图片名只取前3
29 #写入csv
30 with open('C:\\Users\lenovo\Desktop\\douban_books.csv', 'a+', newline='', encoding='utf-8')as fp: #newline 使不隔行
31 writer = csv.writer(fp)
32 writer.writerow((title, score, comments, author, publishment, pub_year, img_url))
33 #下载图片,为防止图片名导致格式错误,加入try...except
34 try:
35 with open(img_name_file, 'wb')as imgf:
36 imgf.write(img.content)
37 except FileNotFoundError or OSError:
38 pass
39 time.sleep(0.5) #睡眠0.5s
40 except IndexError:
41 pass
42 #执行程序
43 if __name__=='__main__':
44 #爬取所有书本,共22页的内容
45 urls = ['https://www.douban.com/doulist/1264675/?start={}&sort=time&playable=0&sub_type='.format(str(i))for i in range(0,550,25)]
46 #写csv首行
47 with open('C:\\Users\lenovo\Desktop\\douban_books.csv', 'a+', newline='', encoding='utf-8')as f:
48 writer = csv.writer(f)
49 writer.writerow(('title', 'score', 'comment', 'author', 'publishment', 'pub_year', 'img_url'))
50 #遍历所有网页,执行爬取程序
51 for url in urls:
52 douban_booksrank(url)
爬取结果截图如下:
- 步骤三:
本次使用Python常用的数据分析库pandas来提取所需内容。pandas的read_csv()函数可以读取csv文件并根据文件格式转换为Series、DataFrame或面板对象。
此处我们提取的数据转变为DataFrame(数据帧)对象,然后通过Matplotlib绘图库来进行绘图。
具体代码如下:
1 from matplotlib import pyplot as plt
2 import pandas as pd
3 import re
4
5 plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
6 plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
7 plt.subplots_adjust(wsapce=0.5, hspace=0.5) #调整subplot子图间的距离
8
9 pd.set_option('display.max_rows', None) #设置使dataframe 所有行都显示
10
11 df = pd.read_csv('C:\\Users\lenovo\Desktop\\douban_books.csv') #读取csv文件,并赋为dataframe对象
12
13 comment = re.findall('\((.*?)人评价', str(df.comment), re.S) #使用正则表达式获取评论人数
14 #将comment的元素化为整型
15 new_comment = []
16 for i in comment:
17 new_comment.append(int(i))
18
19 pub_year = re.findall(r'\d{4}', str(df.pub_year),re.S) #获取书籍出版年份
20 #同上
21 new_pubyear = []
22 for n in pub_year:
23 new_pubyear.append(int(n))
24
25 #绘图
26 #1、绘制书籍评分范围的直方图
27 plt.subplot(2,2,1)
28 plt.hist(df.score, bins=16, edgecolor='black')
29 plt.title('豆瓣书籍排行榜评分分布', fontweight=700)
30 plt.xlabel('scores')
31 plt.ylabel('numbers')
32
33 #绘制书籍评论数量的直方分布图
34 plt.subplot(222)
35 plt.hist(new_comment, bins=16, color='green', edgecolor='yellow')
36 plt.title('豆瓣书籍排行榜评价分布', fontweight=700)
37 plt.xlabel('评价数')
38 plt.ylabel('书籍数量(单位/本)')
39
40 #绘制书籍出版年份分布图
41 plt.subplot(2,2,3)
42 plt.hist(new_pubyear, bins=30, color='indigo',edgecolor='blue')
43 plt.title('书籍出版年份分布', fontweight=700)
44 plt.xlabel('出版年份/year')
45 plt.ylabel('书籍数量/本')
46
47 #寻找关系
48 plt.subplot(224)
49 plt.bar(new_pubyear,new_comment, color='red', edgecolor='white')
50 plt.title('书籍出版年份与评论数量的关系', fontweight=700)
51 plt.xlabel('出版年份/year')
52 plt.ylabel('评论数')
53
54 plt.savefig('C:\\Users\lenovo\Desktop\\douban_books_analysis.png') #保存图片
55 plt.show()
这里需要注意的是,使用了正则表达式来提取评论数和出版年份,将其中的符号和文字等剔除。
分析结果如下:
本次分析的内容也较为简单,从上面的几个图形中我们也能得出一些结论。
- 这些高分书籍中绝大多数的评论数量都在50000以下;
- 多数排行榜上的高分书籍都出版在2000年以后;
- 出版年份在2000年后的书籍有更多的评论数量。
以上数据也见解的说明了在进入二十世纪后我国的图书需求量更大了,网络更发达,更多人愿意发表自己的看法。
本次的分享到此。若有错误,欢迎指正。有建议的话也可以留言。