本文利用Python3爬虫抓取豆瓣图书Top250,并利用xlwt模块将其存储至excel文件,图片下载到相应目录。旨在进行更多的爬虫实践练习以及模块学习。html
1.Python 3.5python
2.BeautifulSoup、xlwt模块浏览器
首先查看目标网页的url: https://book.douban.com/top250?start=0, 而后我尝试了在代码里直接经过字符串链接仅改变”start=“后面的数字的方法来遍历全部的250/25 = 10页内容,可是后来发现不行,那样的话出来的永远是第一页,因而经过浏览器的F12开发者工具检查,发现start是要post上去的,以下图:多线程
(图1)app
因此创建一个postData的dict:ide
postData = {"start": i} #i为0,25,...225
每次将其post上去便可解决返回都是第一页的问题。函数
分析网页可知,一本书的罗列信息以及要爬取的点以下图:工具
(图2)post
从上到下须要爬取的信息有:学习
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"
爬取下来了之后固然要考虑存储,这时我想试试把它存到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环境下进行试验
------------------------------------------------------------------------------------------------------
听人说数据分析绝大部分时间都花在数据采集与清洗,之前不怎么以为,如今终于有一点感觉了,任重道远啊..
若是您对个人方法有什么见解,欢迎留下您的评论:-)