上次使用了 BeautifulSoup 库爬取电影排行榜, 爬取相对来说有点麻烦, 爬取的速度也较慢. 本次使用的 lxml 库, 我个人是最喜欢的, 爬取的语法很简单, 爬取速度也快.
本次爬取的豆瓣书籍排行榜的首页地址是:
该排行榜一共有 22 页, 且发现更改网址的 start=0 的 0 为 25,50 就可以跳到排行榜的第二, 第三页, 所以后面只需更改这个数字然后通过遍历就可以爬取整个排行榜的书籍信息.
本次爬取的内容有书名, 评分, 评价数, 出版社, 出版年份以及书籍封面图, 封面图保存为图片, 其他数据存为 CSV 文件, 方面后面读取分析.
本次的项目步骤: 一, 分析网页, 确定爬取数据
二, 使用 lxml 库爬取内容并保存
三, 读取数据并选择部分内容进行分析
步骤一:
分析网页源代码可以看到, 书籍信息在属性为 class="doulist-item" 的 div 标签中, 打开发现, 我们需要爬取的信息都在标签内部, 通过 xpath 语法我们可以很简便的爬取所需内容.
(书籍各类信息所在标签)
所需爬取的内容在 class 为 post,title,rating,abstract 的 div 标签中.
步骤二:
先定义爬取函数, 爬取所需内容
执行函数, 并存入 CSV 文件
- import requests
- from lxml import etree
- import time
- import CSV
- #信息头
- headers = {
- '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'
- }
- #定义爬取函数
- def douban_booksrank(url):
- res = requests.get(url, headers=headers)
- selector = etree.HTML(res.text)
- contents = selector.xpath('//div[@class="article"]/div[contains(@class,"doulist-item")]') #循环点
- for content in contents:
- try:
- title = content.xpath('div/div[2]/div[3]/a/text()')[0] #书名
- scores = content.xpath('div/div[2]/div[4]/span[2]/text()') #评分
- scores.append('9.0') #因为有一些书没有评分, 导致列表为空, 此处添加一个默认评分, 若无评分则默认为 9.0
- score = scores[0]
- comments = content.xpath('div/div[2]/div[4]/span[3]/text()')[0] #评论数量
- author = content.xpath('div/div[2]/div[5]/text()[1]')[0] #作者
- publishment = content.xpath('div/div[2]/div[5]/text()[2]')[0] #出版社
- pub_year = content.xpath('div/div[2]/div[5]/text()[3]')[0] #出版时间
- img_url = content.xpath('div/div[2]/div[2]/a/img/@src')[0] #书本图片的网址
- img = requests.get(img_url) #解析图片网址, 为下面下载图片
- img_name_file = 'C:/Users/lenovo/Desktop/douban_books/{}.png'.format((title.strip())[:3]) #图片存储位置, 图片名只取前 3
- #写入 CSV
- with open('C:\\Users\lenovo\Desktop\\douban_books.csv', 'a+', newline='', encoding='utf-8')as fp: #newline 使不隔行
- writer = CSV.writer(fp)
- writer.writerow((title, score, comments, author, publishment, pub_year, img_url))
- #下载图片, 为防止图片名导致格式错误, 加入 try...except
- try:
- with open(img_name_file, 'wb')as imgf:
- imgf.write(img.content)
- except FileNotFoundError or OSError:
- pass
- time.sleep(0.5) #睡眠 0.5s
- except IndexError:
- pass
- #执行程序
- if __name__=='__main__':
- #爬取所以书本, 共 22 页的内容
- urls = ['https://www.douban.com/doulist/1264675/?start={}&sort=time&playable=0&sub_type='.format(str(i))for i in range(0,550,25)]
- #写 CSV 首行
- with open('C:\\Users\lenovo\Desktop\\douban_books.csv', 'a+', newline='', encoding='utf-8')as f:
- writer = CSV.writer(f)
- writer.writerow(('title', 'score', 'comment', 'author', 'publishment', 'pub_year', 'img_url'))
- #遍历所有网页, 执行爬取程序
- for url in urls:
- douban_booksrank(url)
- from matplotlib import pyplot as plt
- import pandas as pd
- import re
- plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
- plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
- plt.subplots_adjust(hspace=0.5) #调整 subplot 上下子图间的距离
- pd.set_option('display.max_rows', None) #设置使 dataframe 所有行都显示
- df = pd.read_csv('C:\\Users\lenovo\Desktop\\douban_books.csv') #读取 CSV 文件, 并赋为 dataframe 对象
- comment = re.findall('\((.*?) 人评价', str(df.comment), re.S) #使用正则表达式获取评论人数
- #将 comment 的元素化为整型
- new_comment = []
- for i in comment:
- new_comment.append(int(i))
- pub_year = re.findall(r'\d{4}', str(df.pub_year),re.S) #获取书籍出版年份
- #同上
- new_pubyear = []
- for n in pub_year:
- new_pubyear.append(int(n))
- #绘图
- #1, 绘制书籍评分范围的直方图
- plt.subplot(2,2,1)
- plt.hist(df.score, bins=16, edgecolor='black')
- plt.title('豆瓣书籍排行榜评分分布', fontweight=700)
- plt.xlabel('scores')
- plt.ylabel('numbers')
- #绘制书籍评论数量的直方分布图
- plt.subplot(222)
- plt.hist(new_comment, bins=16, color='green', edgecolor='yellow')
- plt.title('豆瓣书籍排行榜评价分布', fontweight=700)
- plt.xlabel('评价数')
- plt.ylabel('书籍数量 (单位 / 本)')
- #绘制书籍出版年份分布图
- plt.subplot(2,2,3)
- plt.hist(new_pubyear, bins=30, color='indigo',edgecolor='blue')
- plt.title('书籍出版年份分布', fontweight=700)
- plt.xlabel('出版年份 / year')
- plt.ylabel('书籍数量 / 本')
- #寻找关系
- plt.subplot(224)
- plt.bar(new_pubyear,new_comment, color='red', edgecolor='white')
- plt.title('书籍出版年份与评论数量的关系', fontweight=700)
- plt.xlabel('出版年份 / year')
- plt.ylabel('评论数')
- plt.savefig('C:\\Users\lenovo\Desktop\\douban_books_analysis.png') #保存图片
- plt.show()
来源: https://www.cnblogs.com/berryguotoshare/p/10708143.html