前言
17年底,买了清华大学出版社出版的《Hadoop权威指南》(第四版)学习,没想到这本书质量之差,超越我的想象,然后上网一看,也是骂声一片。从那个时候其就对出版社综合实力很感兴趣,想通过具体数据分析各个出版社的出版质量,另外借此也可以熟悉大数据生态和相关操作。
豆瓣上的书籍数据刚好可以满足需求,所以有了思路:
1. 用python编写爬虫,爬取豆瓣上的书籍信息,并持久化到mysql数据库;
2. 使用sqoop将mysql里的数据导入hive;
3. 通过hive进行离线分析。
一、爬虫部分
1. 爬虫思路及架构
通过观察豆瓣网书籍的具体页面,我们可以发现,具体书籍网址的组成形式为:
其中bookid为具体的数字。第一种思路是设定一个比较大的数字,然后从1到这个数字的范围之内去遍历所有数字对应的网址,但是我们可以发现,这些书的id往往非常大,基本都是百万级别的数字,一个个去撞库非常不现实。
其实每本书都有很多标签,每个标签都汇集了同一类的所有书,要是可以获取到所有标签,然后根据这些标签一个个去遍历就可以获得豆瓣上所有的书了,当然不同标签之间肯定有重复的书,所以还要考虑去重的问题。
所以,这个爬虫的思路主要是:
2) 通过上一步获取的标签,组成标签页的网址,进入标签页获得本页的所有书籍链接,通过书籍链接获取需要信息;
3) 对书籍信息进行标准化,持久化处理;
4) 爬完本标签,切到下一个标签,继续爬,重复2,3步;
5) 应对网站反爬虫机制;
6) 错误日志和程序崩溃处理。
编程语言选择python,持久化工具选择mysql数据库。
2. 数据库设计
1) 标签信息
表名为tag_info,用来保存从豆瓣爬下来的标签名字,另外,增加多两个字段用来表示进度,一个是当前页数,另一个是完成状态。
2) 书籍信息:
表名为book_info,保存了每本书的基本信息,包括书名、作者、出版社、评分、评分人数等。为了去重,唯一id采用了豆瓣网中书籍的id。
3. 爬虫框架及流程分析
在这个爬虫中使用的模块有 requests,BeautifulSoup,re,logging和MySQLdb,主要流程如下图所示。
1) 用requests模块向豆瓣网发起GET请求,获得网页返回的信息;
2) 把上一步返回的信息用BeautifulSoup模块进行分析,如果还需要进一步匹配过滤,传给re模块进一步加工,否则直接传给MySQLdb模块;
3) re模块对BeautifulSoup传过来的信息进行匹配处理,最后传给MySQLdb;
4) 用MySQLdb连接mysql数据库,将获得的书籍信息持久化到mysql中;
5) 用logging模块负责系统日志的输出和维护。
4. 应对豆瓣的反爬虫
实际上这次爬取数据,比计划多花了一倍时间,大部分时间在于应对豆瓣的反爬虫机制。
刚开始requests去请求数据的时候,准备了若干个header,然后每次请求都换一个。没过多久就开始返回403了,于是减低了爬取的频率,还是继续403。
查了半天资料,首次请求保存cookie,以后每次都修改cookiebid去请求,过一段时间还是被豆瓣检测出来了,有的页面直接返回空的或者定向到别的页面。
最后想到了代理,写了一个动态获取免费代理并验证的函数,不过免费的ip代理不仅慢而且不稳定,又不想买收费的代理服务。
后面查到了ADSL拨号服务器代理的相关文章,发现自己正是用ADSL上网,于是折腾了一番,写了一个断开路由器连接的函数,每当爬虫被豆瓣封杀的时候,就断开路由器连接重新获取ip,由此解决了爬虫正常运行的问题。当然,我这次爬取的数据量比较少,所以用这种方式还是能解决问题,如果是需要在短时间获取大量数据的,还是需要用代理的方式。
花了几天时间,最后终于完成了所有标签的爬取工作,爬到的数据去重后有6万条左右。因为各个标签之间肯定有重叠的部分,所以符合原来的预期。
二、使用sqoop将数据从mysql导入hive
1. 导入hive:
sqoop import --connect jdbc:mysql://localhost:3306/test --username root -P --split-by id --table book_info --target-dir /tmp/douban/book_info --hive-import --create-hive-table --hive-table douban.book_info --direct
2. 查看hive管理表结构
desc formatted book_info;
col_name data_type comment#col_name data_type comment
id int
book_name string
author string
publisher string
translator string
publish_date string
page_num int
isbn string
score double
rating_num int
…(后面省略)
三、通过hive cli分析数据
1. 计算所有书籍的平均分
HiveQL语句:
SELECT AVG(score) AS avg_score FROM book_info WHERE score > 0 AND rating_num > 100;
为了准确,过滤掉没有评分和评分人数不足的,结果:
OK
avg_score8.057803307115979
平均分在8分左右,估计一般用户都是看完一本觉得不错的书才上豆瓣进行评分。
2. 计算清华大学出版社书籍的平均分
HiveQL语句:
SELECT AVG(score) AS avg_score FROM book_info WHERE publisher ='清华大学出版社' AND score > 0 AND rating_num > 100;
结果:
OK
avg_score8.167039106145252
结果显示清华大学出版社出版的平均分比总体的稍高,但是也没有很突出。
3. 计算平均分排名前十的出版社
HiveQL语句:
SELECT publisher, AVG(score) as avg_score, COUNT(id) AS book_num
FROM book_info
WHERE score> 0 AND rating_num > 100GROUP BY publisher
HAVING COUNT(id)> 10ORDER BY avg_score
DESC
LIMIT10;
结果:
publisher avg_score book_num
茜新社9.157894736842104 19山西古籍出版社9.027272727272727 11太田出版8.933333333333334 15浙江人民美术出版社8.930769230769231 13東立出版社有限公司8.910526315789474 19少年儿童出版社8.898484848484848 66上海辞书出版社8.887500000000001 16上海科学技术出版社8.86875 16白泉社8.857692307692309 26岳麓书社8.845833333333331 24
可以看出排名前十的出版社平均分都在8.8以上,最高的平均分达9.16分。
4. 计算清华大学出版社的排名
HiveQL语句:
FROM (
SELECT publisher, count(id) as book_num, AVG(score) as avg_score, row_number() over(ORDER BY avg_score DESC) as rank_num
FROM book_info
WHERE score> 0 AND rating_num > 100GROUP BY publisher
HAVING count(id)> 10LIMIT200) t1
SELECT t1.*WHERE t1.publisher= '清华大学出版社';
结果:
t1.publisher t1.book_num t1.avg_score t1.rank_num
清华大学出版社179 8.167039106145252 146
数据显示清华大学出版社的排名100名之内都排不进,而且如果把每个出版社出版书籍数量放宽一点来处理的话,结果会更难看一点。
四、小结
实际上只要找到合适的数据源,就能够通过这些数据挖掘出自己需要的东西。从数据采集到最后产出分析结果,整个过程相对来说还比较粗糙,sqoop和hive部分由于数据量不大,只是作为学习目的来检索,暂时未涉及到分区和分桶的操作,因为数据量小,查询也没作并行优化,等后面有时间再进行深入。
(完)