本文利用Python3爬虫抓取豆瓣图书Top250,并利用xlwt模块将其存储至excel文件,图片下载到相应目录。旨在进行更多的爬虫实践练习以及模块学习。

工具

1.Python 3.5

2.BeautifulSoup、xlwt模块

开始动手

首先查看目标网页的url: https://book.douban.com/top250?start=0, 然后我尝试了在代码里直接通过字符串连接仅改变”start=“后面的数字的方法来遍历所有的250/25 = 10页内容,但是后来发现不行,那样的话出来的永远是第一页,于是通过浏览器的F12开发者工具检查,发现start是要post上去的,如下图:

(图1)

所以建立一个postData的dict:

postData = {"start": i}    #i为0,25,...225

每次将其post上去即可解决返回都是第一页的问题。

分析网页可知,一本书的罗列信息以及要爬取的点如下图:

(图2)

从上到下需要爬取的信息有:

1.图书链接地址

2.封面图片链接    我到时候会将此链接打开,下载图片到本地 (download_img函数)

3.书名    要注意的是这里书名取title的内容而不去a标签中的string信息,因为string信息可能包含诸如空格、换行符之类的字符,给处理造成麻烦,直接取title格式正确且无需额外处理。

4.别名    这里主要是副标题或者是外文名,有的书没有这项,那么我们就写入一个“无”,千万不可以写入一个空串,否则的话会出现故障,我下面会提到。

5.出版信息  如作者、译者、出版社、出版年份、价格, 这也是重要信息之一,否则有多本书名字一致可能会无法分辨

6.评分

7.评价人数

除此之外,我还爬取一个“标签”信息,它在图书链接打开之后的网页中,找到它的位置如下:

(图3)

爬到标签以后将它们用逗号连接起来作为标签值。

好了,既然明确了要爬的指标,以及了解了网页结构以及指标所在的html中的位置,那么就可以写出如下代码:

    geturl = url + "/start=" + str(i)                     #要获取的页面地址
print("Now to get " + geturl)
postData = {"start":i} #post数据
res = s.post(url,data = postData,headers = header) #post
soup = BeautifulSoup(res.content,"html.parser") #BeautifulSoup解析
table = soup.findAll('table',{"width":"100%"}) #找到所有图书信息的table
sz = len(table) #sz = 25,每页列出25篇文章
for j in range(1,sz+1): #j = 1~25
sp = BeautifulSoup(str(table[j-1]),"html.parser") #解析每本图书的信息
#print(sp.div)
imageurl = sp.img['src'] #找图片链接
bookurl = sp.a['href'] #找图书链接
bookName = sp.div.a['title']
nickname = sp.div.span #找别名
if(nickname): #如果有别名则存储别名否则存’无‘
nickname = nickname.string.strip()
else:
nickname = "None" #print(type(imageurl),imageurl)
#print(type(bookurl),bookurl)
#print(type(bookName),bookName)
#print(type(nickname),nickname) notion = str(sp.find('p',{"class":"pl"}).string) #抓取出版信息,注意里面的.string还不是真的str类型
#print(type(notion),notion)
rating = str(sp.find('span',{"class":"rating_nums"}).string) #抓取平分数据
nums = sp.find('span',{"class":"pl"}).string #抓取评分人数
nums = nums.replace('(','').replace(')','').replace('\n','').strip()
nums = re.findall('(\d+)人评价',nums)[0]
#print(type(rating),rating)
#print(type(nums),nums)
download_img(imageurl,bookName) #下载图片
book = requests.get(bookurl) #打开该图书的网页
sp3 = BeautifulSoup(book.content,"html.parser") #解析
taglist = sp3.find_all('a',{"class":" tag"}) #找标签信息
tag = ""
lis = []
for tagurl in taglist:
sp4 = BeautifulSoup(str(tagurl),"html.parser") #解析每个标签
lis.append(str(sp4.a.string)) tag = ','.join(lis) #加逗号
if tag == "": #如果标签为空,置"无"
tag = "None"

通过xlwt模块存入xls文件及其问题

爬取下来了以后当然要考虑存储,这时我想试试把它存到Excel文件(.xls)中,于是搜得python操作excel可以使用xlwt,xlrd模块,虽然他们暂时只支持到excel2003,但是足够了。xlwt为Python生成.xls文件的模块,而xlrd为读取的。由于我想的是直接生成xls文件,不需用到xlrd,所以先安装xlwt。

直接进入Python目录使用如下命令即可安装xlwt:

pip3 install xlwt

安装完后写出操作代码,这里同时也写入txt文件,方便比较:

#建立Excel
workbook = xlwt.Workbook(encoding='utf-8')
sheet = workbook.add_sheet('book_top250',cell_overwrite_ok=True)
item = ['书名','别称','评分','评价人数','封面','图书链接','出版信息','标签']
for i in range(1,9):
sheet.write(0,i,item[i-1]) ...
for j in range(1,sz+1):
...
writelist = [i+j,bookName,nickname,float(rating),int(nums),"I:\\douban\\image\\"+bookName+".jpg",bookurl,notion,tag]
for k in range(0,9):
sheet.write(i+j,k,writelist[k])
txtfile.write(str(writelist[k]))
txtfile.write('\t')
txtfile.write(u'\r\n') workbook.save("I:\\douban\\booktop250.xls")
...

满以为这样就可以了,但是还是出现了一些错误。

比如曾经出现了写着写着就写不下去了的情况(以下并非以上代码产生的结果):

(图4)

这时我把不是str的都改成str了,不该str的尽量用数字(int,float),然后又遇到了下面的情况:

(图5)

写到第64项又写不下去了,但是那些int,float都写完了,‘无’也是断断续续显示几个,我想,既然找不到问题,那么慢慢套吧。首先极大可能是中文编码的问题,因为我把一些可以不为str类型的都赋成非str类型以后都正确地显示了,而且上图中的显示在图片路径名那里断了,所以我让那一列都不显示,居然,成功了!

(图6)

如图,除了不显示的那一列,其它完全正常,可以断定就是下面这里出现的错误:

writelist=[i+j,bookName,nickname,float(rating),int(nums),"I:\\douban\\image\\"+bookName+".jpg",bookurl,notion,tag]

我的图片路径那里是直接字符串拼接而成的,所以可能会有编码的错误。那么稍微改一下试试:

imgpath = str("I:\\douban\\image\\"+bookName+".jpg");
writelist=[i+j,bookName,nickname,float(rating),int(nums),imgpath,bookurl,notion,tag]

好吧,还是不行,还是出现图5的问题,但是打印在Python IDLE里面又都是正确的。

既然如此,把图片链接全部改成一样的英文试一下:

imgpath = str("I:\\douban\\image\\"+"a"+".jpg")
writelist=[i+j,bookName,nickname,float(rating),int(nums),imgpath,bookurl,notion,tag]

又是正确的:('无'已改为'None')

(图7)

所以说,还是图片路径的问题,那我们索性将图片路径那列换成图片链接,采取消极应对方法,反正这项是图片链接还是图片路径无关紧要,反正图片路径里面有图片就可以了。此外我还加了一个计时的代码,计算总爬取时间,因为我觉得这样干爬太慢了,没有个将近10分钟完不成,考虑利用多线程去爬,这里先记录一下时间以观后效。然后发现还是不行!现在成了只要imageurl固定(中文也行),就能够顺利输出到xls中,否则就不行。很诡异。于是我又尝试了缩短imageurl,实验得知,当取imageurl[:-6]时是可以的,但imageurl[:-5]就不行了。后面又干脆不写入imageurl这一列,可以,不写入别名或者不写入图书链接都是正常的,但是不写入标号就不行。至今仍不得解。初步猜测莫非是写入的字符数受限制了?还得靠更多的实验才能确定。而且也说不定就是Windows下的编码问题,这又得靠在Linux下进行实验判断。所以要做的事情还很多,这里先把正确的绝大部分工作做了再说。

于是干脆不要图书地址一列,最后得出如下最终代码:

# -*- coding:utf-8 -*-
import requests
import re
import xlwt
from bs4 import BeautifulSoup
from datetime import datetime
import codecs now = datetime.now() #开始计时
print(now) txtfile = codecs.open("top250.txt",'w','utf-8')
url = "http://book.douban.com/top250?" header = { "User-Agent": "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.13 Safari/537.36",
"Referer": "http://book.douban.com/"
} image_dir = "I:\\douban\\image\\"
#下载图片
def download_img(imageurl,imageName = "xxx.jpg"):
rsp = requests.get(imageurl, stream=True)
image = rsp.content
path = image_dir + imageName +'.jpg'
#print(path)
with open(path,'wb') as file:
file.write(image) #建立Excel
workbook = xlwt.Workbook(encoding='utf-8')
sheet = workbook.add_sheet('book_top250',cell_overwrite_ok=True) item = ['书名','别称','评分','评价人数','封面','图书链接','出版信息','标签']
for i in range(1,9):
sheet.write(0,i,item[i-1]) s = requests.Session() #建立会话
s.get(url,headers=header) for i in range(0,250,25):
geturl = url + "/start=" + str(i) #要获取的页面地址
print("Now to get " + geturl)
postData = {"start":i} #post数据
res = s.post(url,data = postData,headers = header) #post
soup = BeautifulSoup(res.content.decode(),"html.parser") #BeautifulSoup解析
table = soup.findAll('table',{"width":"100%"}) #找到所有图书信息的table
sz = len(table) #sz = 25,每页列出25篇文章
for j in range(1,sz+1): #j = 1~25
sp = BeautifulSoup(str(table[j-1]),"html.parser") #解析每本图书的信息
#print(sp.div)
imageurl = sp.img['src'] #找图片链接
bookurl = sp.a['href'] #找图书链接
bookName = sp.div.a['title']
nickname = sp.div.span #找别名
if(nickname): #如果有别名则存储别名否则存’无‘
nickname = nickname.string.strip()
else:
nickname = "None" #print(type(imageurl),imageurl)
#print(type(bookurl),bookurl)
#print(type(bookName),bookName)
#print(type(nickname),nickname) notion = str(sp.find('p',{"class":"pl"}).string) #抓取出版信息,注意里面的.string还不是真的str类型
#print(type(notion),notion)
rating = str(sp.find('span',{"class":"rating_nums"}).string) #抓取平分数据
nums = sp.find('span',{"class":"pl"}).string #抓取评分人数
nums = nums.replace('(','').replace(')','').replace('\n','').strip()
nums = re.findall('(\d+)人评价',nums)[0]
#print(type(rating),rating)
#print(type(nums),nums)
download_img(imageurl,bookName) #下载图片
book = requests.get(bookurl) #打开该图书的网页
sp3 = BeautifulSoup(book.content,"html.parser") #解析
taglist = sp3.find_all('a',{"class":" tag"}) #找标签信息
tag = ""
lis = []
for tagurl in taglist:
sp4 = BeautifulSoup(str(tagurl),"html.parser") #解析每个标签
lis.append(str(sp4.a.string)) tag = ','.join(lis) #加逗号
if tag == "": #如果标签为空,置"无"
tag = "None" writelist=[i+j,bookName,nickname,float(rating),int(nums),imageurl,bookurl,notion,tag]
for k in range(0,9):
if(k == 5):
continue
sheet.write(i+j,k,writelist[k])
txtfile.write(str(writelist[k]))
txtfile.write('\t')
txtfile.write(u'\r\n') workbook.save("I:\\douban\\booktop250.xls") end = datetime.now() #结束计时
print(end)
print("程序耗时: " + str(end-now))
txtfile.close()

运行(7分多钟):

(图8)

还是断了,那就真不知道怎么办才好了。再改变方法,先写到TXT文本文件再导入到xls中,就先不管本文标题了。

运行:

2016-03-27 21:47:17.914149
Now to get http://book.douban.com/top250?/start=0
Now to get http://book.douban.com/top250?/start=25
Now to get http://book.douban.com/top250?/start=50
Now to get http://book.douban.com/top250?/start=75
Now to get http://book.douban.com/top250?/start=100
Now to get http://book.douban.com/top250?/start=125
Now to get http://book.douban.com/top250?/start=150
Now to get http://book.douban.com/top250?/start=175
Now to get http://book.douban.com/top250?/start=200
Now to get http://book.douban.com/top250?/start=225
2016-03-27 21:56:16.046792
程序耗时: 0:08:58.132643

在.txt中是正确的:

(图9)

然后在xls文件中选择数据->导入数据即可得到最终结果:

(图10)

封面图片:

(图11)

问题先解决到这,后面的问题有待深入研究。

后期可改进

1.采用多进程/多线程加快爬取速度

2.可考虑采用xlutis模块分多步写入到excel中

3.可考虑改换excel处理模块

3.考虑在Linux环境下进行试验

------------------------------------------------------------------------------------------------------

听人说数据分析绝大部分时间都花在数据采集与清洗,以前不怎么觉得,现在终于有一点感受了,任重道远啊..

如果您对我的方法有什么看法,欢迎留下您的评论:-)

【Python数据分析】Python3操作Excel-以豆瓣图书Top250为例的更多相关文章

  1. 【Python数据分析】Python3多线程并发网络爬虫-以豆瓣图书Top250为例

    基于上两篇文章的工作 [Python数据分析]Python3操作Excel-以豆瓣图书Top250为例 [Python数据分析]Python3操作Excel(二) 一些问题的解决与优化 已经正确地实现 ...

  2. python爬虫1——获取网站源代码(豆瓣图书top250信息)

    # -*- coding: utf-8 -*- import requests import re import sys reload(sys) sys.setdefaultencoding('utf ...

  3. 【Python数据分析】Python3操作Excel(二) 一些问题的解决与优化

    继上一篇[Python数据分析]Python3操作Excel-以豆瓣图书Top250为例 对豆瓣图书Top250进行爬取以后,鉴于还有一些问题没有解决,所以进行了进一步的交流讨论,这期间得到了一只尼玛 ...

  4. Python 2.7_利用xpath语法爬取豆瓣图书top250信息_20170129

    大年初二,忙完家里一些事,顺带有人交流爬取豆瓣图书top250 1.构造urls列表 urls=['https://book.douban.com/top250?start={}'.format(st ...

  5. 转 Python - openpyxl 读写操作Excel

    Python - openpyxl 读写操作Excel   openpyxl特点   openpyxl(可读写excel表)专门处理Excel2007及以上版本产生的xlsx文件,xls和xlsx之间 ...

  6. Python爬虫-爬取豆瓣图书Top250

    豆瓣网站很人性化,对于新手爬虫比较友好,没有如果调低爬取频率,不用担心会被封 IP.但也不要太频繁爬取. 涉及知识点:requests.html.xpath.csv 一.准备工作 需要安装reques ...

  7. 豆瓣图书Top250

    从豆瓣图书Top250抓取数据,并通过词云图展示 导入库 from lxml import etree #解析库 import time #时间 import random #随机函数 import ...

  8. python系列之(4)豆瓣图书《平凡的世界》书评及情感分析

    本篇主要是通过对豆瓣图书<平凡的世界>短评进行抓取并进行分析,并用snowNLP对其进行情感分析. 用到的模块有snowNLP,是一个python库,用来进行情感分析. 1.抓取数据 我们 ...

  9. python用openpyxl操作excel

    python操作excel方法 1)自身有Win32 COM操作office但讲不清楚,可能不支持夸平台,linux是否能用不清楚,其他有专业处理模块,如下 2)xlrd:(读excel)表,xlrd ...

随机推荐

  1. WPF实现无边框窗体拖拽右下角▲ 改变窗体大小【framwork4.0】 谢谢大家关注

    效果图:(右下角拖拽改变窗体大小) 第一步:添加xaml代码: <Border Name="ResizeBottomRight" MouseMove="Resize ...

  2. html页面内容超出后显示水平滚动条的问题

    这个问题已经遇到好几次,解决起来也熟练了很多.   出现这种问题一般都是html或页面中的某一内部元素宽度超了. 下面总结我遇到的几种情况: 1.某一内部元素width设为100%,然而它还有bord ...

  3. eclipse控制台下实现jdbc简单的增删改查测试

    1.现在MySQL中创建一个表 2.首先创建一个类 //导入的包 import java.sql.Connection;import java.sql.DriverManager;import jav ...

  4. Thinking in Java——笔记(19)

    Enumerated Types Basic enum features When you create an enum, an associated class is produced for yo ...

  5. python基础之数据类型(二)

    Python3 元组 Python 的元组与列表类似,不同之处在于元组的元素不能修改. 元组使用小括号,列表使用方括号. 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可. 不可变的tupl ...

  6. python之协程与IO操作

    协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...

  7. [Cordova] Plugin里使用iOS Framework

    [Cordova] Plugin里使用iOS Framework 前言 开发Cordova Plugin的时候,在Native Code里使用第三方Library,除了可以加速项目的时程.也避免了重复 ...

  8. js基础知识梳理(最简版)

    基础的JavaScript知识,只放XMind截图.小白 JS01 JS02 JS03 最基础的js知识--!

  9. MFC--响应鼠标和键盘操作

    一个程序最重要的部分之一是对鼠标和键盘操作的响应. 一.  理解鼠标事件.之前对鼠标事件的认识仅仅局限于处理控件的单击与双击事件.但实际鼠标的操作包含很多.这里将以一个画图的小程序讲解对鼠标的响应. ...

  10. iOS之2016面试题二

    前言 招聘高峰期来了,大家都非常积极地准备着跳槽,那么去一家公司面试就会有一堆新鲜的问题,可能不会,也可能会,但是了解不够深.本篇文章为群里的小伙伴们去要出发公司的笔试题,由笔者整理并提供笔者个人参考 ...