這兩天爬了豆瓣讀書的十萬條左右的書目信息,用時(shí)將近一天,現(xiàn)在趁著這個(gè)空閑把代碼總結(jié)一下,還是菜鳥,都是用的最簡(jiǎn)單最笨的方法,還請(qǐng)路過的大神不吝賜教。
第一步,先看一下我們需要的庫:
import requests #用來請(qǐng)求網(wǎng)頁
from bs4 import BeautifulSoup #解析網(wǎng)頁
import time #設(shè)置延時(shí)時(shí)間,防止爬取過于頻繁被封IP號(hào)
import re #正則表達(dá)式庫
import pymysql #由于爬取的數(shù)據(jù)太多,我們要把他存入MySQL數(shù)據(jù)庫中,這個(gè)庫用于連接數(shù)據(jù)庫
import random #這個(gè)庫里用到了產(chǎn)生隨機(jī)數(shù)的randint函數(shù),和上面的time搭配,使爬取間隔時(shí)間隨機(jī)
這個(gè)是豆瓣的網(wǎng)址:https://book.douban.com/tag/?view=type&icn=index-sorttags-all
我們要從這里獲取所有分類的標(biāo)簽鏈接,進(jìn)一步去爬取里面的信息,代碼先貼上來:
import requests
from bs4 import BeautifulSoup #導(dǎo)入庫
url="https://book.douban.com/tag/?icn=index-nav"
wb_data=requests.get(url) #請(qǐng)求網(wǎng)址
soup=BeautifulSoup(wb_data.text,"lxml") #解析網(wǎng)頁信息
tags=soup.select("#content > div > div.article > div > div > table > tbody > tr > td > a")
#根據(jù)CSS路徑查找標(biāo)簽信息,CSS路徑獲取方法,右鍵-檢查-copy selector,tags返回的是一個(gè)列表
for tag in tags:
tag=tag.get_text() #將列表中的每一個(gè)標(biāo)簽信息提取出來
helf="https://book.douban.com/tag/"
#觀察一下豆瓣的網(wǎng)址,基本都是這部分加上標(biāo)簽信息,所以我們要組裝網(wǎng)址,用于爬取標(biāo)簽詳情頁
url=helf+str(tag)
print(url) #網(wǎng)址組裝完畢,輸出
以上我們便爬取了所有標(biāo)簽下的網(wǎng)址,我們將這個(gè)文件命名為channel,并在channel中創(chuàng)建一個(gè)channel字符串,放上我們所有爬取的網(wǎng)址信息,等下爬取詳情頁的時(shí)候直接從這里提取鏈接就好了,如下:
channel='''
https://book.douban.com/tag/小說
https://book.douban.com/tag/外國(guó)文學(xué)
https://book.douban.com/tag/文學(xué)
https://book.douban.com/tag/隨筆
https://book.douban.com/tag/中國(guó)文學(xué)
https://book.douban.com/tag/經(jīng)典
https://book.douban.com/tag/日本文學(xué)
https://book.douban.com/tag/散文
https://book.douban.com/tag/村上春樹
https://book.douban.com/tag/詩歌
https://book.douban.com/tag/童話
https://book.douban.com/tag/雜文
https://book.douban.com/tag/王小波
https://book.douban.com/tag/兒童文學(xué)
https://book.douban.com/tag/古典文學(xué)
https://book.douban.com/tag/張愛玲
https://book.douban.com/tag/名著
https://book.douban.com/tag/余華
https://book.douban.com/tag/當(dāng)代文學(xué)
https://book.douban.com/tag/錢鐘書
https://book.douban.com/tag/魯迅
https://book.douban.com/tag/外國(guó)名著
https://book.douban.com/tag/詩詞
https://book.douban.com/tag/茨威格
https://book.douban.com/tag/米蘭·昆德拉
https://book.douban.com/tag/杜拉斯
https://book.douban.com/tag/港臺(tái)
https://book.douban.com/tag/漫畫
https://book.douban.com/tag/繪本
https://book.douban.com/tag/推理
https://book.douban.com/tag/青春
https://book.douban.com/tag/言情
https://book.douban.com/tag/科幻
https://book.douban.com/tag/東野圭吾
https://book.douban.com/tag/懸疑
https://book.douban.com/tag/武俠
https://book.douban.com/tag/奇幻
https://book.douban.com/tag/韓寒
https://book.douban.com/tag/日本漫畫
https://book.douban.com/tag/耽美
https://book.douban.com/tag/亦舒
https://book.douban.com/tag/三毛
https://book.douban.com/tag/安妮寶貝
https://book.douban.com/tag/網(wǎng)絡(luò)小說
https://book.douban.com/tag/推理小說
https://book.douban.com/tag/郭敬明
https://book.douban.com/tag/穿越
https://book.douban.com/tag/金庸
https://book.douban.com/tag/輕小說
https://book.douban.com/tag/阿加莎·克里斯蒂
https://book.douban.com/tag/幾米
https://book.douban.com/tag/魔幻
https://book.douban.com/tag/張小嫻
https://book.douban.com/tag/幾米
https://book.douban.com/tag/青春文學(xué)
https://book.douban.com/tag/科幻小說
https://book.douban.com/tag/J.K.羅琳
https://book.douban.com/tag/高木直子
https://book.douban.com/tag/古龍
https://book.douban.com/tag/滄月
https://book.douban.com/tag/落落
https://book.douban.com/tag/張悅?cè)?
https://book.douban.com/tag/蔡康永
https://book.douban.com/tag/歷史
https://book.douban.com/tag/心理學(xué)
https://book.douban.com/tag/哲學(xué)
https://book.douban.com/tag/傳記
https://book.douban.com/tag/文化
https://book.douban.com/tag/社會(huì)學(xué)
https://book.douban.com/tag/藝術(shù)
https://book.douban.com/tag/設(shè)計(jì)
https://book.douban.com/tag/政治
https://book.douban.com/tag/社會(huì)
https://book.douban.com/tag/建筑
https://book.douban.com/tag/宗教
https://book.douban.com/tag/電影
https://book.douban.com/tag/數(shù)學(xué)
https://book.douban.com/tag/政治學(xué)
https://book.douban.com/tag/回憶錄
https://book.douban.com/tag/思想
https://book.douban.com/tag/中國(guó)歷史
https://book.douban.com/tag/國(guó)學(xué)
https://book.douban.com/tag/音樂
https://book.douban.com/tag/人文
https://book.douban.com/tag/人物傳記
https://book.douban.com/tag/戲劇
https://book.douban.com/tag/生活
https://book.douban.com/tag/成長(zhǎng)
https://book.douban.com/tag/勵(lì)志
https://book.douban.com/tag/心理
https://book.douban.com/tag/攝影
https://book.douban.com/tag/女性
https://book.douban.com/tag/職場(chǎng)
https://book.douban.com/tag/美食
https://book.douban.com/tag/教育
https://book.douban.com/tag/游記
https://book.douban.com/tag/靈修
https://book.douban.com/tag/健康
https://book.douban.com/tag/情感
https://book.douban.com/tag/手工
https://book.douban.com/tag/養(yǎng)生
https://book.douban.com/tag/兩性
https://book.douban.com/tag/人際關(guān)系
https://book.douban.com/tag/家居
https://book.douban.com/tag/自助游
https://book.douban.com/tag/經(jīng)濟(jì)學(xué)
https://book.douban.com/tag/管理
https://book.douban.com/tag/經(jīng)濟(jì)
https://book.douban.com/tag/商業(yè)
https://book.douban.com/tag/金融
https://book.douban.com/tag/投資
https://book.douban.com/tag/營(yíng)銷
https://book.douban.com/tag/創(chuàng)業(yè)
https://book.douban.com/tag/理財(cái)
https://book.douban.com/tag/廣告
https://book.douban.com/tag/股票
https://book.douban.com/tag/企業(yè)史
https://book.douban.com/tag/策劃
https://book.douban.com/tag/科普
https://book.douban.com/tag/互聯(lián)網(wǎng)
https://book.douban.com/tag/編程
https://book.douban.com/tag/科學(xué)
https://book.douban.com/tag/交互設(shè)計(jì)
https://book.douban.com/tag/用戶體驗(yàn)
https://book.douban.com/tag/算法
https://book.douban.com/tag/web
https://book.douban.com/tag/科技
https://book.douban.com/tag/UE
https://book.douban.com/tag/通信
https://book.douban.com/tag/交互
https://book.douban.com/tag/UCD
https://book.douban.com/tag/神經(jīng)網(wǎng)絡(luò)
https://book.douban.com/tag/程序
'''
現(xiàn)在,我們開始第二個(gè)程序。

標(biāo)簽頁下每一個(gè)圖片的信息基本都是這樣的,我們可以直接從這里提取到標(biāo)題,作者,出版社,出版時(shí)間,價(jià)格,評(píng)價(jià)人數(shù),以及評(píng)分等信息(有些外國(guó)作品還會(huì)有譯者信息),提取方法與提取標(biāo)簽類似,也是根據(jù)CSS路徑提取。
我們先用一個(gè)網(wǎng)址來實(shí)驗(yàn)爬?。?/p>
url="https://book.douban.com/tag/科技"
wb_data = requests.get(url)
soup = BeautifulSoup(wb_data.text.encode("utf-8"), "lxml")
tag=url.split("?")[0].split("/")[-1] #從鏈接里面提取標(biāo)簽信息,方便存儲(chǔ)
detils=soup.select("#subject_list > ul > li > div.info > div.pub") #抓取作者,出版社信息,稍后我們用spite()函數(shù)再將他們分離出來
scors=soup.select("#subject_list > ul > li > div.info > div.star.clearfix > span.rating_nums") #抓取評(píng)分信息
persons=soup.select("#subject_list > ul > li > div.info > div.star.clearfix > span.pl") #評(píng)價(jià)人數(shù)
titles=soup.select("#subject_list > ul > li > div.info > h2 > a") #書名
#以上抓取的都是我們需要的html語言標(biāo)簽信息,我們還需要將他們一一分離出來
for detil,scor,person,title in zip(detils,scors,persons,titles):
#用一個(gè)zip()函數(shù)實(shí)現(xiàn)一次遍歷
#因?yàn)橐恍?biāo)簽中有譯者信息,一些標(biāo)簽中沒有,為避免錯(cuò)誤,所以我們要用一個(gè)try來把他們分開執(zhí)行
try:
author=detil.get_text().split("/",4)[0].split()[0] #這是含有譯者信息的提取辦法,根據(jù)“/” 把標(biāo)簽分為五部分,然后依次提取出來
yizhe= detil.get_text().split("/", 4)[1]
publish=detil.get_text().split("/", 4)[2]
time=detil.get_text().split("/", 4)[3].split()[0].split("-")[0] #時(shí)間我們只提取了出版年份
price=ceshi_priceone(detil) #因?yàn)閮r(jià)格的單位不統(tǒng)一,我們用一個(gè)函數(shù)把他們換算為“元”
scoe=scor.get_text() if True else "" #有些書目是沒有評(píng)分的,為避免錯(cuò)誤,我們把沒有評(píng)分的信息設(shè)置為空
person=ceshi_person(person) #有些書目的評(píng)價(jià)人數(shù)顯示少于十人,爬取過程中會(huì)出現(xiàn)錯(cuò)誤,用一個(gè)函數(shù)來處理
title=title.get_text().split()[0]
#當(dāng)沒有譯者信息時(shí),會(huì)顯示IndexError,我們分開處理
except IndexError:
try:
author=detil.get_text().split("/", 3)[0].split()[0]
yizhe="" #將detil信息劃分為4部分提取,譯者信息直接設(shè)置為空,其他與上面一樣
publish=detil.get_text().split("/", 3)[1]
time=detil.get_text().split("/", 3)[2].split()[0].split("-")[0]
price=ceshi_pricetwo(detil)
scoe=scor.get_text() if True else ""
person=ceshi_person(person)
title=title.get_text().split()[0]
except (IndexError,TypeError):
continue
#出現(xiàn)其他錯(cuò)誤信息,忽略,繼續(xù)執(zhí)行(有些書目信息下會(huì)沒有出版社或者出版年份,但是數(shù)量很少,不影響我們大規(guī)模爬取,所以直接忽略)
except TypeError:
continue
#提取評(píng)價(jià)人數(shù)的函數(shù),如果評(píng)價(jià)人數(shù)少于十人,按十人處理
def ceshi_person(person):
try:
person = int(person.get_text().split()[0][1:len(person.get_text().split()[0]) - 4])
except ValueError:
person = int(10)
return person
#分情況提取價(jià)格的函數(shù),用正則表達(dá)式找到含有特殊字符的信息,并換算為“元”
def ceshi_priceone(price):
price = detil.get_text().split("/", 4)[4].split()
if re.match("USD", price[0]):
price = float(price[1]) * 6
elif re.match("CNY", price[0]):
price = price[1]
elif re.match("\A$", price[0]):
price = float(price[1:len(price)]) * 6
else:
price = price[0]
return price
def ceshi_pricetwo(price):
price = detil.get_text().split("/", 3)[3].split()
if re.match("USD", price[0]):
price = float(price[1]) * 6
elif re.match("CNY", price[0]):
price = price[1]
elif re.match("\A$", price[0]):
price = float(price[1:len(price)]) * 6
else:
price = price[0]
return price
實(shí)驗(yàn)成功后,我們就可以爬取數(shù)據(jù)并導(dǎo)入到數(shù)據(jù)庫中了,以下為全部源碼,特殊情況會(huì)用注釋一一說明。
import requests
from bs4 import BeautifulSoup
import time
import re
import pymysql
from channel import channel #這是我們第一個(gè)程序爬取的鏈接信息
import random
def ceshi_person(person):
try:
person = int(person.get_text().split()[0][1:len(person.get_text().split()[0]) - 4])
except ValueError:
person = int(10)
return person
def ceshi_priceone(price):
price = detil.get_text().split("/", 4)[4].split()
if re.match("USD", price[0]):
price = float(price[1]) * 6
elif re.match("CNY", price[0]):
price = price[1]
elif re.match("\A$", price[0]):
price = float(price[1:len(price)]) * 6
else:
price = price[0]
return price
def ceshi_pricetwo(price):
price = detil.get_text().split("/", 3)[3].split()
if re.match("USD", price[0]):
price = float(price[1]) * 6
elif re.match("CNY", price[0]):
price = price[1]
elif re.match("\A$", price[0]):
price = float(price[1:len(price)]) * 6
else:
price = price[0]
return price
#這是上面的那個(gè)測(cè)試函數(shù),我們把它放在主函數(shù)中
def mains(url):
wb_data = requests.get(url)
soup = BeautifulSoup(wb_data.text.encode("utf-8"), "lxml")
tag=url.split("?")[0].split("/")[-1]
detils=soup.select("#subject_list > ul > li > div.info > div.pub")
scors=soup.select("#subject_list > ul > li > div.info > div.star.clearfix > span.rating_nums")
persons=soup.select("#subject_list > ul > li > div.info > div.star.clearfix > span.pl")
titles=soup.select("#subject_list > ul > li > div.info > h2 > a")
for detil,scor,person,title in zip(detils,scors,persons,titles):
l = [] #建一個(gè)列表,用于存放數(shù)據(jù)
try:
author=detil.get_text().split("/",4)[0].split()[0]
yizhe= detil.get_text().split("/", 4)[1]
publish=detil.get_text().split("/", 4)[2]
time=detil.get_text().split("/", 4)[3].split()[0].split("-")[0]
price=ceshi_priceone(detil)
scoe=scor.get_text() if True else ""
person=ceshi_person(person)
title=title.get_text().split()[0]
except IndexError:
try:
author=detil.get_text().split("/", 3)[0].split()[0]
yizhe=""
publish=detil.get_text().split("/", 3)[1]
time=detil.get_text().split("/", 3)[2].split()[0].split("-")[0]
price=ceshi_pricetwo(detil)
scoe=scor.get_text() if True else ""
person=ceshi_person(person)
title=title.get_text().split()[0]
except (IndexError,TypeError):
continue
except TypeError:
continue
l.append([title,scoe,author,price,time,publish,person,yizhe,tag])
#將爬取的數(shù)據(jù)依次填入列表中
sql="INSERT INTO allbooks values(%s,%s,%s,%s,%s,%s,%s,%s,%s)" #這是一條sql插入語句
cur.executemany(sql,l) #執(zhí)行sql語句,并用executemary()函數(shù)批量插入數(shù)據(jù)庫中
conn.commit()
#主函數(shù)到此結(jié)束
# 將Python連接到MySQL中的python數(shù)據(jù)庫中
conn = pymysql.connect( user="root",password="123123",database="python",charset='utf8')
cur = conn.cursor()
cur.execute('DROP TABLE IF EXISTS allbooks') #如果數(shù)據(jù)庫中有allbooks的數(shù)據(jù)庫則刪除
sql = """CREATE TABLE allbooks(
title CHAR(255) NOT NULL,
scor CHAR(255),
author CHAR(255),
price CHAR(255),
time CHAR(255),
publish CHAR(255),
person CHAR(255),
yizhe CHAR(255),
tag CHAR(255)
)"""
cur.execute(sql) #執(zhí)行sql語句,新建一個(gè)allbooks的數(shù)據(jù)庫
start = time.clock() #設(shè)置一個(gè)時(shí)鐘,這樣我們就能知道我們爬取了多長(zhǎng)時(shí)間了
for urls in channel.split():
urlss=[urls+"?start={}&type=T".format(str(i)) for i in range(0,980,20)] #從channel中提取url信息,并組裝成每一頁的鏈接
for url in urlss:
mains(url) #執(zhí)行主函數(shù),開始爬取
print(url) #輸出要爬取的鏈接,這樣我們就能知道爬到哪了,發(fā)生錯(cuò)誤也好處理
time.sleep(int(format(random.randint(0,9)))) #設(shè)置一個(gè)隨機(jī)數(shù)時(shí)間,每爬一個(gè)網(wǎng)頁可以隨機(jī)的停一段時(shí)間,防止IP被封
end = time.clock()
print('Time Usage:', end - start) #爬取結(jié)束,輸出爬取時(shí)間
count = cur.execute('select * from allbooks')
print('has %s record' % count) #輸出爬取的總數(shù)目條數(shù)
# 釋放數(shù)據(jù)連接
if cur:
cur.close()
if conn:
conn.close()
這樣,一個(gè)程序就算完成了,豆瓣的書目信息就一條條地寫進(jìn)了我們的數(shù)據(jù)庫中,當(dāng)然,在爬取的過程中,也遇到了很多問題,比如標(biāo)題返回的信息拆分后中會(huì)有空格,寫入數(shù)據(jù)庫中會(huì)出現(xiàn)錯(cuò)誤,所以只截取了標(biāo)題的第一部分,因而導(dǎo)致數(shù)據(jù)庫中的一些書名不完整,過往的大神如果有什么辦法,還請(qǐng)指教一二。
等待爬取的過程是漫長(zhǎng)而又欣喜的,看著電腦上一條條信息被刷出來,成就感就不知不覺涌上心頭;然而如果你吃飯時(shí)它在爬,你上廁所時(shí)它在爬,你都已經(jīng)爬了個(gè)山回來了它還在爬時(shí),便會(huì)有點(diǎn)崩潰了,擔(dān)心電腦隨時(shí)都會(huì)壞掉(還是窮學(xué)生換不起啊啊啊啊~)
所以,還是要好好學(xué)學(xué)設(shè)置斷點(diǎn),多線程,以及正則,路漫漫其修遠(yuǎn)兮,吾將上下而求索共勉