本文利用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環境下進行試驗
------------------------------------------------------------------------------------------------------
聽人說數據分析絕大部分時間都花在數據採集與清洗,之前不怎麼以爲,如今終於有一點感覺了,任重道遠啊..
若是您對個人方法有什麼見解,歡迎留下您的評論:-)