爬取豆瓣TOP250图书榜的出版社分布(一)-urllib2

爬取豆瓣TOP250图书榜的出版社分布(一)-urllib2


0. 需求

现在准备爬取豆瓣上的图书TOP250然后看哪个出版社的图书上榜最多。将使用两种办法分别实现,urllib2与scrapy框架。爬取的内容包括书的名字,书的出版社,书的评分,书的链接。

1. 使用urllib2实现

1.1 分析网站链接

http://book.douban.com/top250?start=0这个是豆瓣网站上图书TOP250首页的链接,很容易就会发现规律,就是?start=0,表示的是这一个页面的开始是从第0本书开始的,那么第二页就是从start=25开始的,了解了这一点,首先就可以把要爬取网站的url搞定了。

def __init__(self):
    self.addr = 'http://book.douban.com/top250'#?start=0
    self.colums=1
1.2 获取网页源代码

为了防止豆瓣网检测出是机器爬虫访问网站,从而封掉自己的ip,因此在发送request请求的时候,附带上模拟浏览器访问的headers。

headers通过浏览器访问豆瓣网,然后右键点击审查元素,就可以查询出用浏览器访问的时候产生的headers和cookie。

下面的代码中cookie有点长。

获取相应的网站的源代码步骤是:

  • 构建url
  • 构建request(本次要加上headers)
  • 发送请求
  • 读取返回的源代码数据

获取网页源代码数据的代码如下:

def getPage(self,page):
    '''获取每个页面的源代码,豆瓣网站编码是utf-8'''
    start=(page-1)*25
    headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.8',
        'Connection': 'keep-alive',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36',
        'Host':'book.douban.com',
        'Referer':'http://book.douban.com/top250?start=0',
        'Cookie':'bid="uJg2YuOLKDc"; ll="118371"; gr_user_id=f349a8b5-04e7-4132-b6c3-659f79f124fa; _pk_ref.100001.3ac3=%5B%22%22%2C%22%22%2C1454551498%2C%22http%3A%2F%2Fwww.douban.com%2F%22%5D; viewed="1770782_21323941_19952400_10432347_10436668_3155710_4830438_1152111_1015813"; _pk_id.100001.3ac3=c7eb454cb471541c.1439423804.9.1454552955.1448876420.; __utma=30149280.2012237960.1437482698.1451533495.1454551966.15; __utmc=30149280; __utmz=30149280.1451533495.14.11.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; __utma=81379588.2012449174.1439423804.1448876271.1454551966.9; __utmc=81379588; __utmz=81379588.1448876271.8.6.utmcsr=baidu|utmccn=(organic)|utmcmd=organic'
            }
    url=self.addr+'?start='+str(start)
    try:
        request=urllib2.Request(url,headers=headers)
        response=urllib2.urlopen(request)
        result=response.read()
    except urllib2.URLError,e:
        print '无法连接,原因:',e.reason
    else:
        return result

在这里有一个需要注意的tip就是,最好不要直接return response.read()这样有可能导致返回不了完整的数据。

1.3 利用正则表达式匹配需要的信息

首先分析豆瓣网页源代码,找到图书信息在HTML代码中相应的位置,建立合理的正则表达式。

然后利用上一小节getPage()方法返回的网页源代码信息进行匹配。

利用re模块里面的findall()函数进行匹配,将获取到整个页面的25本图书的信息results,并且返回的是一个列表,其中每一本书在这个列表中也是以列表存在的,因此迭代返回的列表,获取对应的信息。

[日] 东野圭吾 / 刘子倩 / 南海出版公司 / 2008-9 / 28.00

其中出版社这一条,可以通过观察发现,出版社都在网站相应字段的倒数第三个,因此解析出版社数据的时候利用resultlt=result[2].split('/')将字符串分裂开,然后使用resultlt[-3]即可提取出出版社的信息。

整个处理方法代码如下:

def getContent(self,page):
    '''通过获得的源代码将所需要的图书信息匹配出来'''
    response=self.getPage(page)
    #建立正则表达式
    pattern=re.compile('<div class="pl2".*?<a href="(.*?)".*?title="(.*?)".*?<p class="pl">(.*?)</p>.*?<span class="rating_nums">(.*?)</span>',re.S)
    results=re.findall(pattern,response)
    contents=[]
    #1是链接2是名字3是作者与出版社信息4是得分
    for result in results:
        resultlt=result[2].split('/')
        contents.append([result[1],result[0],resultlt[-3].strip(),result[3]])
    return contents
    #只是直接打印列表的话才不会显示正常字符,如果直接打印就会打印出中文字符
1.4 将数据写入文件

将获取到的数据按照对应字段写入txt文本文档还是相当容易的,主要的问题是字符串的编码问题,豆瓣网采用了utf-8编码,因此在读取网页源代码以及写入文件的时候并不需要考虑编码问题。

直接将要写入文件的字符串格式建立好就可以直接写入了。

写入函数如下所示:

def writeFile(self,contents):
    '''将数据写入到文本文件中'''
    file=open('douban.txt','a')
    for content in contents:
        line='\n'+str(self.colums)
        info='\nname:'+content[0]+'\naddress:'+content[1]+'\npress:'+content[2]+'\nrate:'+content[3]+'\n'
        file.write(line)
        file.write(info)
        self.colums+=1
    file.close()

写入后的文本效果如下图:

写入的文本
1.5 开始程序

在这里将控制程序打开豆瓣图书TOP250的10个页面,然后分别进行数据提取。代码如下:

def start(self):
    '''开始程序,首先读取content然后将数据保存到shelve模块中'''
    i=1
    while i<=10:
        print '正在读取并存储第',str(i),'页内容'
        contents=self.getContent(i)
        self.writeData(contents)
        self.writeFile(contents)
        i+=1
        time.sleep(10)

这里需要注意的是在打开页面之间最好留有间隔,time.sleep(10),间隔10秒,否则豆瓣网站会禁止访问。

1.6 数据分析

#######1.6.1 从文本文件提取出版社信息

下面进行的内容就是将存入文件的TOP250的图书信息提取出来,然后将每本书的出版社汇总分析,看每个出版社出现了多少次,然后做成直观的统计图表进行显示。

做到这一切的核心思想是:将文本文件的文本提取出来(使用readlines()),然后使用正则表达式提取press的信息,创建一个字典,出版社名为键,出版社出现的次数作为值。

使用字典的has_key方法判断是否有该出版社键值,没有则将该出版社作为键加入字典,设置默认值1。如果有的话,将该出版社对应的值加一。最后获得整个的出版社次数的字典。

另外程序还使用了shelve作为对出版社数据的简单的存储,shelve的使用非常方便,只需要将其当成普通的字典,这一点刚好符合我们的提取信息的方案,将其存入shelve后,在下一步的数据可视化中就可以方便的将数据提取出来。

代码如下:

#coding=utf-8

'''这个程序是将存入文件的豆瓣图书书单的出版社提取出来,存入字典
使用setdefault()方法,如果有相应的键则返回键对应的值
否则返回默认值1'''
import re
import shelve

class ExtractPress():
'''首先定义一个存放出版社和数量的字典,然后是输入,以及匹配的内容'''

    def __init__(self):
        self.pressDic={}
        self.pattern=re.compile('press:(.*?).rate',re.S)
        self.database=shelve.open('press.dat')


    def getAllValue(self):
        text=open('douban.txt').readlines()
        strText=''.join(text)
        results=re.findall(self.pattern,strText)
        for result in results:
            if self.pressDic.has_key(result):
                number=int(self.pressDic[result])
                number+=1
                self.pressDic[result]=number
            else:
                self.pressDic.setdefault(result,1)

    def printDic(self):
        for press in self.pressDic:
            print press+':'+str(self.pressDic[press])
            self.database[press]=self.pressDic[press]
        self.database.close()

if __name__=='__main__':
example=ExtractPress()
example.getAllValue()
example.printDic()

#######1.6.2 对提取的信息进行可视化处理

在这里我将使用matplotlib进行数据分析,有关matplotlib的环境配置,请移步利用matplotlib进行数据分析(一)一文,其中包含了matplotlib以及相关依赖的安装配置方法。

这里直接上代码,然后对代码进行分析,关于数据可视化编程的知识,参考Python数据可视化编程实战 [爱尔兰]Igon Milovanovic一书。

#coding=utf-8

import shelve
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties

class dataAnalyseGraph():
    '''这个类将上面的出版社的数据信息提取出来,做出一个可视化的图表'''

    def __init__(self):
        self.database=shelve.open('press.dat')
        self.keys=[]
        self.data=[]
        self.font=FontProperties(fname=r'C:\WINDOWS\Fonts\STSONG.TTF',size=10)

    def getData(self):
        for key in self.database.iterkeys():
            if self.database[key]<=1:
                continue
            self.keys.append(key.decode('utf-8'))
            self.data.append(self.database[key])
        print self.data
        for n in self.keys:
            print n

    def makeGraph(self):
        y_pos=range(len(self.keys))
        # colors=np.random.rand(len(self.keys))
        #水平柱状图
        plt.barh(y_pos,self.data,align='center',alpha=0.4)
        #刻度
        plt.yticks(y_pos,self.keys,fontproperties=self.font)
        #绘图
        for datas,y_pos in zip(self.data,y_pos):
            plt.text(datas,y_pos,datas,horizontalalignment='center',
                 verticalalignment='center',weight='bold')
        #set y limit
        plt.ylim(+32,-1.0)
        plt.title(u'豆瓣图书TOP250出版社统计',fontproperties=self.font)
        plt.ylabel(u'出版社',fontproperties=self.font)
        plt.subplots_adjust(bottom=0.15)
        plt.xlabel(u'出版社出现次数',fontproperties=self.font)
        plt.show()
        plt.savefig("douban_book_press.png")

if __name__=='__main__':
    graph=dataAnalyseGraph()
    graph.getData()
    graph.makeGraph()

建立了一个专门作为数据可视化的类,在该类的构造方法中,打开了shelve文件(上一小节中存储的出版社数据),初始化了两个列表,同时打开了一个宋体的文字文件作为图表显示文字。

getData方法中将文件的数据提取出来,这里由于出版社过多,因此只提取了出现1次以上的出版社,将出版社和出现次数按顺序存放至建立好的keys和data列表中。

makeGraph是数据可视化的核心部分。barh是绘制水平柱状图的函数,接收y_pos为竖直坐标,而self.data作为柱状图的值,align指定了矩形条的居中设置,alpha指定了透明度。yticks是竖直方向的刻度设置,y_pos指定了刻度,而self.keys指定了刻度的显示,这两个是一一对应的。然后将每一组data与y_pos的柱指定它的文本,坐标就是datas(出版社出现次数,横坐标的值),y_pos是y坐标,datas是显示的数字。然后ylim设置y的上下限。设置标题,设置y轴的标签,x的标签,然后show显示该图像。

最后的可视化效果如图所示:

豆瓣图书TOP250出版社分布
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,050评论 25 707
  • 今天开始这学期就算正式开始了,心情还是没有收拾好,有些累。 不会说话办事的,唉,慢慢熬吧~ 对自己的坚持也有所怀疑...
    salen小伦阅读 138评论 0 0
  • 前两天一个人正在发呆,朋友发了一条消息:你考完试直接到我们学校找我吧,不要联系他了。 心里咯哒一下。 分了?我问她...
    盛夏安年阅读 159评论 0 0
  • 上一章 吃饱喝足后,林思凡心满意足地坐到了一边,至于洗碗刷锅的事自然由另外两个男生分担,自己暂时可以歇歇了。整理好...
    狐狸九阅读 364评论 0 4
  • 明净的车窗外 蓝天白云绿水青山红砖灰瓦 原来天上的空和云是分开的 白云就像刚出炉的棉花糖 扯一片含在口中 一丝甜蜜...
    叫我梅芳就好阅读 911评论 1 0