更新:已更新豆瓣电影Top250的脚本及网站
概述
经常用豆瓣读书的童鞋应该知道,豆瓣Top250用的是综合排序,除用户评分之外还考虑了很多比如是否畅销、点击量等等,这也就导致了一些近年来评分不高的畅销书在这个排行榜上高高在上远比一些经典名著排名还高,于是在这里打算重新给Top250纯按照用户打分排一下序。思路就是:先用爬虫爬下来豆瓣Top250的所有书目,然后存到本地之后进行排序,最后再做一个网站把排完序的书单重新挂上去。
先挂上成品网站:
豆瓣读书Top250逆序 | Alanzjl
豆瓣电影Top250逆序 | Alanzjl
这个网站完全按照豆瓣原网站制作,html代码都是直接扒豆瓣的。或者作为我的个人网站内嵌使用:
豆瓣读书Top250 | Homepage of Alan
豆瓣电影Top250 | Homepage of Alan
可以看到已经按照打分排序。
OK下面进入正文
代码
先说一下大体流程:
爬虫爬取信息->排序->生成Html文件
从爬取信息开始。
先打开豆瓣Top250网站,看看其URL:http://book.douban.com/top250?start=0,点一下“下一页”就可以看到跳转到了start=25的地址.哈,分析一下就可以知道每页显示25本书,正好10页,那就循环遍历10个页面就可以了。那要是不是25的整数倍呢?start=1呢?打开一看也是可以的,网站上面显示的第一本书就变成了第二本(start=0)是第一本,也就是说只要我们从start=0遍历到start=250这样每本书就是当前URL的第一本书。对于爬虫来讲这两种遍历方式(每次跳25个,爬虫爬取每页25本书以及每次跳一个,爬虫爬取得每页第一本书)都是可以的,但是实践发现前者使用的时候经常会有遗漏,比如这一次爬全了,下一次突然就不全了这样,也不太清楚是怎么回事就用第二种方法吧。
爬虫主要用到两个Python库,urllib(打开网站)和re(正则表达式)库,先写一下整体代码框架:
import re
import urllib
class book:
#book class, used as container
title = ""
author = ""
url = ""
img = ""
rate = 0.0
def __init__(self, Title, Author, Url, Img, Rate):
self.title = Title
self.author = Author
self.url = Url
self.img = Img
self.rate = Rate
def content(self):
return "Title:%s\tAuthor:%s\tUrl:%s\tImg:%s\tRate:%s\n"\
%(self.title,self.author,self.url,self.img,self.rate)
def makeHtml(blist, path):
#generate html file
def run():
count = 0
while count < 250:
url = 'http://book.douban.com/top250?start=%d'%count
page = urllib.urlopen(url).read()
count += 1
bookList = []
run()
bookListSorted = sorted(bookList, key=lambda ele:ele.rate, reverse=1)
makeHtml(bookListSorted,'DoubanBook.html');
大概就是这样了,解释一下每个函数/类的作用:
book类:存储每本书需要用到的信息,有title、author、豆瓣链接、图片链接、rate
makeHtml函数:根据传入的参数(blist书目列表以及生成html file位置)生成html
run函数:遍历250本书,进行正则表达式信息提取
先从run()
说起
首先一个count=0
记录遍历的书目序号,然后进入循环,url = 'http://book.douban.com/top250?start=%d'%count
生成当前需要遍历书目的url地址,使用page = urllib.urlopen(url).read()
保存这个页面的内容。可以print一下这个page的内容,就是html文件。
然后进行信息提取。以题目为例,从豆瓣网站中找到任意一本书的题目,
(可以在chrome中使用开发者工具,然后点开发者工具中左上角的小箭头,这样鼠标移到页面中的哪一个位置就可以自动在源代码中标记出来,移到题目处就可以看到源代码)
单独复制出来:
<a href="http://book.douban.com/subject/1084336/"
onclick="moreurl(this,{i:'0'})"
title="小王子">小王子</a>
多试几个就可以看到,每本书的题目在源代码的定义方式都是:
" title="题目">题目</a>
保留前面的"
只是为了缩小范围提高精确度,这样,我们就可以写出其正则表达式:
(?<=" title=").*?(?=")
简单分析一下,(?<=" title=")
表示前缀是" title="
,然后跟.*?
表示满足条件的尽量少的任意字符,然后(?=")
表示后缀为"
,连起来就是满足前缀和后缀要求的中间尽量少的字符集。相似的方法,我们可以找出其他信息的正则表达式表述。就是:
titlePat = re.compile(r'(?<=" title=").*?(?=")')
authorPat = re.compile(r'(?<=<p class="pl">).