Python爬虫实战六之抓取爱问知识人问题并保存至数据库
大家好,本次為大家?guī)淼氖亲ト蹎栔R人的問題并將問題和答案保存到數(shù)據(jù)庫的方法,涉及的內(nèi)容包括:
- Urllib的用法及異常處理
- Beautiful Soup的簡單應(yīng)用
- MySQLdb的基礎(chǔ)用法
- 正則表達(dá)式的簡單應(yīng)用
環(huán)境配置
在這之前,我們需要先配置一下環(huán)境,我的Python的版本為2.7,需要額外安裝的庫有兩個(gè),一個(gè)是Beautiful Soup,一個(gè)是MySQLdb,在這里附上兩個(gè)庫的下載地址,
Beautiful Soup? ? ?MySQLdb
大家可以下載之后通過如下命令安裝
python setup.py install| 1 | python setup.py install |
環(huán)境配置好之后,我們便可以開心地?cái)]爬蟲了
框架思路
首先我們隨便找一個(gè)分類地址,外語學(xué)習(xí) – 愛問知識人,打開之后可以看到一系列的問題列表。
我們在這個(gè)頁面需要獲取的東西有:
總的頁碼數(shù),每一頁的所有問題鏈接。
接下來我們需要遍歷所有的問題,來抓取每一個(gè)詳情頁面,提取問題,問題內(nèi)容,回答者,回答時(shí)間,回答內(nèi)容。
最后,我們需要把這些內(nèi)容存儲到數(shù)據(jù)庫中。
要點(diǎn)簡析
其實(shí)大部分內(nèi)容相信大家會了前面的內(nèi)容,這里的爬蟲思路已經(jīng)融匯貫通了,這里就說一下一些擴(kuò)展的功能
1.日志輸出
日志輸出,我們要輸出時(shí)間和爬取的狀態(tài),比如像下面這樣:
[2015-08-10 03:05:20] 113011 號問題存在其他答案 我個(gè)人認(rèn)為應(yīng)該是櫻桃溝很美的
[2015-08-10 03:05:20] 保存到數(shù)據(jù)庫,此問題的ID為 113011
[2015-08-10 03:05:20] 當(dāng)前爬取第 2 的內(nèi)容,發(fā)現(xiàn)一個(gè)問題 百度有一個(gè)地方,花兒帶著芳香,水兒流淌奔騰是什么意思 多多幫忙哦 回答數(shù)量 1
[2015-08-10 03:05:19] 保存到數(shù)據(jù)庫,此問題的ID為 113010
所以,我們需要引入時(shí)間函數(shù),然后寫一個(gè)獲取當(dāng)前時(shí)間的函數(shù)
import time#獲取當(dāng)前時(shí)間 def getCurrentTime(self):return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time()))#獲取當(dāng)前時(shí)間 def getCurrentDate(self):return time.strftime('%Y-%m-%d',time.localtime(time.time()))| 1 2 3 4 5 6 7 8 9 | import time #獲取當(dāng)前時(shí)間 def getCurrentTime(self): ????return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time())) ???? #獲取當(dāng)前時(shí)間 def getCurrentDate(self): ????return time.strftime('%Y-%m-%d',time.localtime(time.time())) |
以上分別是獲取帶具體時(shí)間和獲取日期的函數(shù),在輸出時(shí),我們可以在輸出語句的前面調(diào)用這函數(shù)即可。
然后我們需要將緩沖區(qū)設(shè)置輸出到log中,在程序的最前面加上這兩句即可
f_handler=open('out.log', 'w') sys.stdout=f_handler| 1 2 | f_handler=open('out.log', 'w') sys.stdout=f_handler |
這樣,所有的print語句輸出的內(nèi)容就會保存到out.log文件中了。
2.頁碼保存
爬蟲爬取過程中可能出現(xiàn)各種各樣的錯(cuò)誤,這樣會導(dǎo)致爬蟲的中斷,如果我們重新運(yùn)行爬蟲,那么就會導(dǎo)致爬蟲從頭開始運(yùn)行了,這樣顯然是不合理的。所以,我們需要把當(dāng)前爬取的頁面保存下來,比如可以保存到文本中,假如爬蟲中斷了,重新運(yùn)行爬蟲,讀取文本文件的內(nèi)容,接著爬取即可。
大家可以稍微參考一下函數(shù)的實(shí)現(xiàn):
#主函數(shù)def main(self):f_handler=open('out.log', 'w') sys.stdout=f_handlerpage = open('page.txt', 'r')content = page.readline()start_page = int(content.strip()) - 1page.close() print self.getCurrentTime(),"開始頁碼",start_pageprint self.getCurrentTime(),"爬蟲正在啟動,開始爬取愛問知識人問題"self.total_num = self.getTotalPageNum()print self.getCurrentTime(),"獲取到目錄頁面?zhèn)€數(shù)",self.total_num,"個(gè)"if not start_page:start_page = self.total_numfor x in range(1,start_page):print self.getCurrentTime(),"正在抓取第",start_page-x+1,"個(gè)頁面"try:self.getQuestions(start_page-x+1)except urllib2.URLError, e:if hasattr(e, "reason"):print self.getCurrentTime(),"某總頁面內(nèi)抓取或提取失敗,錯(cuò)誤原因", e.reasonexcept Exception,e: print self.getCurrentTime(),"某總頁面內(nèi)抓取或提取失敗,錯(cuò)誤原因:",eif start_page-x+1 < start_page:f=open('page.txt','w')f.write(str(start_page-x+1))print self.getCurrentTime(),"寫入新頁碼",start_page-x+1f.close()| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ????#主函數(shù) ????def main(self): ????????f_handler=open('out.log', 'w') ????????sys.stdout=f_handler ????????page = open('page.txt', 'r') ????????content = page.readline() ????????start_page = int(content.strip()) - 1 ????????page.close()???? ????????print self.getCurrentTime(),"開始頁碼",start_page ????????print self.getCurrentTime(),"爬蟲正在啟動,開始爬取愛問知識人問題" ????????self.total_num = self.getTotalPageNum() ????????print self.getCurrentTime(),"獲取到目錄頁面?zhèn)€數(shù)",self.total_num,"個(gè)" ????????if not start_page: ????????????start_page = self.total_num ????????for x in range(1,start_page): ????????????print self.getCurrentTime(),"正在抓取第",start_page-x+1,"個(gè)頁面" ????????????try: ????????????????self.getQuestions(start_page-x+1) ????????????except urllib2.URLError, e: ????????????????if hasattr(e, "reason"): ????????????????????print self.getCurrentTime(),"某總頁面內(nèi)抓取或提取失敗,錯(cuò)誤原因", e.reason ????????????except Exception,e:?? ????????????????print self.getCurrentTime(),"某總頁面內(nèi)抓取或提取失敗,錯(cuò)誤原因:",e ????????????if start_page-x+1 < start_page: ????????????????f=open('page.txt','w') ????????????????f.write(str(start_page-x+1)) ????????????????print self.getCurrentTime(),"寫入新頁碼",start_page-x+1 ????????????????f.close() |
這樣,不管我們爬蟲中途遇到什么錯(cuò)誤,媽媽也不會擔(dān)心了
3.頁面處理
頁面處理過程中,我們可能遇到各種各樣奇葩的HTML代碼,和上一節(jié)一樣,我們沿用一個(gè)頁面處理類即可。
import re#處理頁面標(biāo)簽類 class Tool:#將超鏈接廣告剔除removeADLink = re.compile('<div class="link_layer.*?</div>')#去除img標(biāo)簽,1-7位空格, removeImg = re.compile('<img.*?>| {1,7}| ')#刪除超鏈接標(biāo)簽removeAddr = re.compile('<a.*?>|</a>')#把換行的標(biāo)簽換為\nreplaceLine = re.compile('<tr>|<div>|</div>|</p>')#將表格制表<td>替換為\treplaceTD= re.compile('<td>')#將換行符或雙換行符替換為\nreplaceBR = re.compile('<br><br>|<br>')#將其余標(biāo)簽剔除removeExtraTag = re.compile('<.*?>')#將多行空行刪除removeNoneLine = re.compile('\n+')def replace(self,x):x = re.sub(self.removeADLink,"",x)x = re.sub(self.removeImg,"",x)x = re.sub(self.removeAddr,"",x)x = re.sub(self.replaceLine,"\n",x)x = re.sub(self.replaceTD,"\t",x)x = re.sub(self.replaceBR,"\n",x)x = re.sub(self.removeExtraTag,"",x)x = re.sub(self.removeNoneLine,"\n",x)#strip()將前后多余內(nèi)容刪除return x.strip()| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import re #處理頁面標(biāo)簽類 class Tool: ???? ????#將超鏈接廣告剔除 ????removeADLink = re.compile('<div class="link_layer.*?</div>') ????#去除img標(biāo)簽,1-7位空格, ????removeImg = re.compile('<img.*?>| {1,7}| ') ????#刪除超鏈接標(biāo)簽 ????removeAddr = re.compile('<a.*?>|</a>') ????#把換行的標(biāo)簽換為\n ????replaceLine = re.compile('<tr>|<div>|</div>|</p>') ????#將表格制表<td>替換為\t ????replaceTD= re.compile('<td>') ????#將換行符或雙換行符替換為\n ????replaceBR = re.compile('<br><br>|<br>') ????#將其余標(biāo)簽剔除 ????removeExtraTag = re.compile('<.*?>') ????#將多行空行刪除 ????removeNoneLine = re.compile('\n+') ???? ????def replace(self,x): ????????x = re.sub(self.removeADLink,"",x) ????????x = re.sub(self.removeImg,"",x) ????????x = re.sub(self.removeAddr,"",x) ????????x = re.sub(self.replaceLine,"\n",x) ????????x = re.sub(self.replaceTD,"\t",x) ????????x = re.sub(self.replaceBR,"\n",x) ????????x = re.sub(self.removeExtraTag,"",x) ????????x = re.sub(self.removeNoneLine,"\n",x) ????????#strip()將前后多余內(nèi)容刪除 ????????return x.strip() |
我們可以用一段含有HTML代碼的文字,經(jīng)過調(diào)用replace方法之后,各種冗余的HTML代碼就會處理好了。
比如我們這么一段代碼:
<articleclass="article-content"> <h2>前言</h2> <p>最近發(fā)現(xiàn)MySQL服務(wù)隔三差五就會掛掉,導(dǎo)致我的網(wǎng)站和爬蟲都無法正常運(yùn)作。自己的網(wǎng)站是基于MySQL,在做爬蟲存取一些資料的時(shí) 候也是基于MySQL,數(shù)據(jù)量一大了,MySQL它就有點(diǎn)受不了了,時(shí)不時(shí)會崩掉,雖然我自己有網(wǎng)站監(jiān)控和郵件通知,但是好多時(shí)候還是需要我來手動連接我 的服務(wù)器重新啟動一下我的MySQL,這樣簡直太不友好了,所以,我就覺定自己寫個(gè)腳本,定時(shí)監(jiān)控它,如果發(fā)現(xiàn)它掛掉了就重啟它。</p> <p>好了,閑言碎語不多講,開始我們的配置之旅。</p> <p>運(yùn)行環(huán)境:<strong>Ubuntu Linux 14.04</strong></p> <h2>編寫Shell腳本</h2> <p>首先,我們要編寫一個(gè)shell腳本,腳本主要執(zhí)行的邏輯如下:</p> <p>顯示mysqld進(jìn)程狀態(tài),如果判斷進(jìn)程未在運(yùn)行,那么輸出日志到文件,然后啟動mysql服務(wù),如果進(jìn)程在運(yùn)行,那么不執(zhí)行任何操 作,可以選擇性輸出監(jiān)測結(jié)果。</p> <p>可能大家對于shell腳本比較陌生,在這里推薦官方的shell腳本文檔來參考一下</p> <p><a href="http://wiki.ubuntu.org.cn/Shell%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80"data-original-title="" title="">Ubuntu Shell 編程基礎(chǔ)</a></p> <p>shell腳本的后綴為sh,在任何位置新建一個(gè)腳本文件,我選擇在 /etc/mysql 目錄下新建一個(gè) listen.sh 文件。</p> <p>執(zhí)行如下命令:</p>| 1 2 3 4 5 6 7 8 9 10 11 12 | <article class="article-content"> <h2>前言</h2> <p>最近發(fā)現(xiàn)MySQL服務(wù)隔三差五就會掛掉,導(dǎo)致我的網(wǎng)站和爬蟲都無法正常運(yùn)作。自己的網(wǎng)站是基于MySQL,在做爬蟲存取一些資料的時(shí)候也是基于MySQL,數(shù)據(jù)量一大了,MySQL它就有點(diǎn)受不了了,時(shí)不時(shí)會崩掉,雖然我自己有網(wǎng)站監(jiān)控和郵件通知,但是好多時(shí)候還是需要我來手動連接我的服務(wù)器重新啟動一下我的MySQL,這樣簡直太不友好了,所以,我就覺定自己寫個(gè)腳本,定時(shí)監(jiān)控它,如果發(fā)現(xiàn)它掛掉了就重啟它。</p> <p>好了,閑言碎語不多講,開始我們的配置之旅。</p> <p>運(yùn)行環(huán)境:<strong>Ubuntu Linux 14.04</strong></p> <h2>編寫Shell腳本</h2> <p>首先,我們要編寫一個(gè)shell腳本,腳本主要執(zhí)行的邏輯如下:</p> <p>顯示mysqld進(jìn)程狀態(tài),如果判斷進(jìn)程未在運(yùn)行,那么輸出日志到文件,然后啟動mysql服務(wù),如果進(jìn)程在運(yùn)行,那么不執(zhí)行任何操作,可以選擇性輸出監(jiān)測結(jié)果。</p> <p>可能大家對于shell腳本比較陌生,在這里推薦官方的shell腳本文檔來參考一下</p> <p><a href="http://wiki.ubuntu.org.cn/Shell%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80" data-original-title="" title="">Ubuntu Shell 編程基礎(chǔ)</a></p> <p>shell腳本的后綴為sh,在任何位置新建一個(gè)腳本文件,我選擇在 /etc/mysql 目錄下新建一個(gè) listen.sh 文件。</p> <p>執(zhí)行如下命令:</p> |
經(jīng)過處理后便會變成如下的樣子:
前 言 最近發(fā)現(xiàn)MySQL服務(wù)隔三差五就會掛掉,導(dǎo)致我的網(wǎng)站和爬蟲都無法正常運(yùn)作。自己的網(wǎng)站是基于MySQL,在做爬蟲存取一些資料的時(shí)候也是基于 MySQL,數(shù)據(jù)量一大了,MySQL它就有點(diǎn)受不了了,時(shí)不時(shí)會崩掉,雖然我自己有網(wǎng)站監(jiān)控和郵件通知,但是好多時(shí)候還是需要我來手動連接我的服務(wù)器重 新啟動一下我的MySQL,這樣簡直太不友好了,所以,我就覺定自己寫個(gè)腳本,定時(shí)監(jiān)控它,如果發(fā)現(xiàn)它掛掉了就重啟它。 好了,閑言碎語不多講,開始我們的配置之旅。 運(yùn)行環(huán)境:UbuntuLinux14.04 編寫Shell腳本 首先,我們要編寫一個(gè)shell腳本,腳本主要執(zhí)行的邏輯如下: 顯示mysqld進(jìn)程狀態(tài),如果判斷進(jìn)程未在運(yùn)行,那么輸出日志到文件,然后啟動mysql服務(wù),如果進(jìn)程在運(yùn)行,那么不執(zhí)行任何操作,可以選擇性輸出監(jiān) 測結(jié)果。 可能大家對于shell腳本比較陌生,在這里推薦官方的shell腳本文檔來參考一下 UbuntuShell編程基礎(chǔ) shell腳本的后綴為sh,在任何位置新建一個(gè)腳本文件,我選擇在/etc/mysql目錄下新建一個(gè)listen.sh文件。 執(zhí)行如下命令:| 1 2 3 4 5 6 7 8 9 10 11 | 前言 最近發(fā)現(xiàn)MySQL服務(wù)隔三差五就會掛掉,導(dǎo)致我的網(wǎng)站和爬蟲都無法正常運(yùn)作。自己的網(wǎng)站是基于MySQL,在做爬蟲存取一些資料的時(shí)候也是基于MySQL,數(shù)據(jù)量一大了,MySQL它就有點(diǎn)受不了了,時(shí)不時(shí)會崩掉,雖然我自己有網(wǎng)站監(jiān)控和郵件通知,但是好多時(shí)候還是需要我來手動連接我的服務(wù)器重新啟動一下我的MySQL,這樣簡直太不友好了,所以,我就覺定自己寫個(gè)腳本,定時(shí)監(jiān)控它,如果發(fā)現(xiàn)它掛掉了就重啟它。 好了,閑言碎語不多講,開始我們的配置之旅。 運(yùn)行環(huán)境:UbuntuLinux14.04 編寫Shell腳本 首先,我們要編寫一個(gè)shell腳本,腳本主要執(zhí)行的邏輯如下: 顯示mysqld進(jìn)程狀態(tài),如果判斷進(jìn)程未在運(yùn)行,那么輸出日志到文件,然后啟動mysql服務(wù),如果進(jìn)程在運(yùn)行,那么不執(zhí)行任何操作,可以選擇性輸出監(jiān)測結(jié)果。 可能大家對于shell腳本比較陌生,在這里推薦官方的shell腳本文檔來參考一下 UbuntuShell編程基礎(chǔ) shell腳本的后綴為sh,在任何位置新建一個(gè)腳本文件,我選擇在/etc/mysql目錄下新建一個(gè)listen.sh文件。 執(zhí)行如下命令: |
經(jīng)過上面的處理,所有亂亂的代碼都會被處理好了。
4.保存到數(shù)據(jù)庫
在這里,我們想實(shí)現(xiàn)一個(gè)通用的方法,就是把存儲的一個(gè)個(gè)內(nèi)容變成字典的形式,然后執(zhí)行插入語句的時(shí)候,自動構(gòu)建對應(yīng)的sql語句,插入數(shù)據(jù)。
比如我們構(gòu)造如下的字典:
#構(gòu)造最佳答案的字典 good_ans_dict = {"text": good_ans[0],"answerer": good_ans[1],"date": good_ans[2],"is_good": str(good_ans[3]),"question_id": str(insert_id)}| 1 2 3 4 5 6 7 8 | #構(gòu)造最佳答案的字典 good_ans_dict = { ????????"text": good_ans[0], ????????"answerer": good_ans[1], ????????"date": good_ans[2], ????????"is_good": str(good_ans[3]), ????????"question_id": str(insert_id) ????????} |
構(gòu)造sql語句并插入到數(shù)據(jù)庫的方法如下:
#插入數(shù)據(jù)def insertData(self, table, my_dict):try:self.db.set_character_set('utf8')cols = ', '.join(my_dict.keys())values = '"," '.join(my_dict.values())sql = "INSERT INTO %s (%s) VALUES (%s)" % (table, cols, '"'+values+'"')try:result = self.cur.execute(sql)insert_id = self.db.insert_id()self.db.commit()#判斷是否執(zhí)行成功if result:return insert_idelse:return 0except MySQLdb.Error,e:#發(fā)生錯(cuò)誤時(shí)回滾self.db.rollback()#主鍵唯一,無法插入if "key 'PRIMARY'" in e.args[1]:print self.getCurrentTime(),"數(shù)據(jù)已存在,未插入數(shù)據(jù)"else:print self.getCurrentTime(),"插入數(shù)據(jù)失敗,原因 %d: %s" % (e.args[0], e.args[1])except MySQLdb.Error,e:print self.getCurrentTime(),"數(shù)據(jù)庫錯(cuò)誤,原因%d: %s" % (e.args[0], e.args[1])| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #插入數(shù)據(jù) ????def insertData(self, table, my_dict): ???????? try: ???????????? self.db.set_character_set('utf8') ???????????? cols = ', '.join(my_dict.keys()) ???????????? values = '"," '.join(my_dict.values()) ???????????? sql = "INSERT INTO %s (%s) VALUES (%s)" % (table, cols, '"'+values+'"') ???????????? try: ???????????????? result = self.cur.execute(sql) ???????????????? insert_id = self.db.insert_id() ???????????????? self.db.commit() ???????????????? #判斷是否執(zhí)行成功 ???????????????? if result: ???????????????????? return insert_id ???????????????? else: ???????????????????? return 0 ???????????? except MySQLdb.Error,e: ???????????????? #發(fā)生錯(cuò)誤時(shí)回滾 ???????????????? self.db.rollback() ???????????????? #主鍵唯一,無法插入 ???????????????? if "key 'PRIMARY'" in e.args[1]: ???????????????????? print self.getCurrentTime(),"數(shù)據(jù)已存在,未插入數(shù)據(jù)" ???????????????? else: ???????????????????? print self.getCurrentTime(),"插入數(shù)據(jù)失敗,原因 %d: %s" % (e.args[0], e.args[1]) ???????? except MySQLdb.Error,e: ???????????? print self.getCurrentTime(),"數(shù)據(jù)庫錯(cuò)誤,原因%d: %s" % (e.args[0], e.args[1]) |
這里我們只需要傳入那個(gè)字典,便會構(gòu)建出對應(yīng)字典鍵值和鍵名的sql語句,完成插入。
5.PHP讀取日志
我們將運(yùn)行結(jié)果輸出到了日志里,那么怎么查看日志呢?很簡單,在這里提供兩種方法
方法一:
PHP倒序輸出所有日志內(nèi)容
<html><head><meta charset="utf-8"><meta http-equiv="refresh" content = "5"> </head><body><?php$fp = file("out.log");if ($fp) {for($i = count($fp) - 1;$i >= 0; $i --) echo $fp[$i]."<br>";}?></body> </html>| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <html> ????<head> ????????<meta charset="utf-8"> ????????<meta http-equiv="refresh" content = "5"> ????</head> ????<body> ????????<?php ????????????$fp = file("out.log"); ????????????if ($fp) { ????????????????for($i = count($fp) - 1;$i >= 0; $i --) ????????????????echo $fp[$i]."<br>"; ????????????} ?????????> ????</body> </html> |
此方法可以看到所有的輸入日志,但是如果日志太大了,那么就會報(bào)耗費(fèi)內(nèi)存太大,無法輸出。為此我們就有了第二種方法,利用linux命令,輸出后十行內(nèi)容。
方法二:
<html><head><meta charset="utf-8"><meta http-equiv="refresh" content = "5"> </head><body><?php $ph = popen('tail -n 100 out.log','r');while($r = fgets($ph)){echo $r."<br>";}pclose($ph);?></body> </html>| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <html> ????<head> ????????<meta charset="utf-8"> ????????<meta http-equiv="refresh" content = "5"> ????</head> ????<body> ????????<?php ????????????$ph = popen('tail -n 100 out.log','r'); ????????????while($r = fgets($ph)){ ????????????????echo $r."<br>"; ????????????} ????????????pclose($ph); ?????????> ????</body> </html> |
上面兩種方法都是5秒刷新一次網(wǎng)頁來查看最新的日志。
源代碼放送
好了,閑言碎語不多講,直接上源碼了
spider.py| 1 | spider.py |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # -*- coding:utf-8 -*- import urllib import urllib2 import re import time import types import page import mysql import sys from bs4 import BeautifulSoup class Spider: ???? ????#初始化 ????def __init__(self): ????????self.page_num = 1 ????????self.total_num = None ????????self.page_spider = page.Page() ????????self.mysql = mysql.Mysql() ???????? ????#獲取當(dāng)前時(shí)間 ????def getCurrentTime(self): ????????return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time())) ???? ????#獲取當(dāng)前時(shí)間 ????def getCurrentDate(self): ????????return time.strftime('%Y-%m-%d',time.localtime(time.time())) ???? ????#通過網(wǎng)頁的頁碼數(shù)來構(gòu)建網(wǎng)頁的URL |
| 1 | page.py |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # -*- coding:utf-8 -*- import urllib import urllib2 import re import time import types import tool from bs4 import BeautifulSoup #抓取分析某一問題和答案 class Page: ???? ????def __init__(self): ????????self.tool = tool.Tool() ???? ????#獲取當(dāng)前時(shí)間 ????def getCurrentDate(self): ????????return time.strftime('%Y-%m-%d',time.localtime(time.time())) ???? ????#獲取當(dāng)前時(shí)間 ????def getCurrentTime(self): ????????return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time())) ????#通過頁面的URL來獲取頁面的代碼 ????def getPageByURL(self, url): ????????try: ????????????request = urllib2.Request(url) ????????????response = urllib2.urlopen(request) ????????????return response.read().decode("utf-8") ????????except urllib2.URLError, e: |
| 1 | tool.py |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #-*- coding:utf-8 -*- import re #處理頁面標(biāo)簽類 class Tool: ???? ????#將超鏈接廣告剔除 ????removeADLink = re.compile('<div class="link_layer.*?</div>') ????#去除img標(biāo)簽,1-7位空格, ????removeImg = re.compile('<img.*?>| {1,7}| ') ????#刪除超鏈接標(biāo)簽 ????removeAddr = re.compile('<a.*?>|</a>') ????#把換行的標(biāo)簽換為\n ????replaceLine = re.compile('<tr>|<div>|</div>|</p>') ????#將表格制表<td>替換為\t ????replaceTD= re.compile('<td>') ????#將換行符或雙換行符替換為\n ????replaceBR = re.compile('<br><br>|<br>') ????#將其余標(biāo)簽剔除 ????removeExtraTag = re.compile('<.*?>') |
| 1 | mysql.py |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | # -*- coding:utf-8 -*- import MySQLdb import time class Mysql: ???? ????#獲取當(dāng)前時(shí)間 ????def getCurrentTime(self): ????????return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time())) ???? ????#數(shù)據(jù)庫初始化 ????def __init__(self): ????????try: ????????????self.db = MySQLdb.connect('ip','username','password','db_name') ????????????self.cur = self.db.cursor() ????????except MySQLdb.Error,e: ???????????? print self.getCurrentTime(),"連接數(shù)據(jù)庫錯(cuò)誤,原因%d: %s" % (e.args[0], e.args[1]) ????#插入數(shù)據(jù) ????def insertData(self, table, my_dict): ???????? try: ???????????? self.db.set_character_set('utf8') ???????????? cols = ', '.join(my_dict.keys()) ???????????? values = '"," '.join(my_dict.values()) ???????????? sql = "INSERT INTO %s (%s) VALUES (%s)" % (table, cols, '"'+values+'"') ???????????? try: ???????????????? result = self.cur.execute(sql) ???????????????? insert_id = self.db.insert_id() ???????????????? self.db.commit() ???????????????? #判斷是否執(zhí)行成功 ???????????????? if result: ???????????????????? return insert_id ???????????????? else: ???????????????????? return 0 ???????????? except MySQLdb.Error,e: ???????????????? #發(fā)生錯(cuò)誤時(shí)回滾 ???????????????? self.db.rollback() ???????????????? #主鍵唯一,無法插入 ???????????????? if "key 'PRIMARY'" in e.args[1]: ???????????????????? print self.getCurrentTime(),"數(shù)據(jù)已存在,未插入數(shù)據(jù)" ???????????????? else: ???????????????????? print self.getCurrentTime(),"插入數(shù)據(jù)失敗,原因 %d: %s" % (e.args[0], e.args[1]) ???????? except MySQLdb.Error,e: ???????????? print self.getCurrentTime(),"數(shù)據(jù)庫錯(cuò)誤,原因%d: %s" % (e.args[0], e.args[1]) |
數(shù)據(jù)庫建表SQL如下:
CREATE TABLE IF NOT EXISTS `iask_answers` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增ID',`text` text NOT NULL COMMENT '回答內(nèi)容',`question_id` int(18) NOT NULL COMMENT '問題ID',`answerer` varchar(255) NOT NULL COMMENT '回答者',`date` varchar(255) NOT NULL COMMENT '回答時(shí)間',`is_good` int(11) NOT NULL COMMENT '是否是最佳答案',PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE IF NOT EXISTS `iask_questions` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '問題ID',`text` text NOT NULL COMMENT '問題內(nèi)容',`questioner` varchar(255) NOT NULL COMMENT '提問者',`date` date NOT NULL COMMENT '提問時(shí)間',`ans_num` int(11) NOT NULL COMMENT '回答數(shù)量',`url` varchar(255) NOT NULL COMMENT '問題鏈接',PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | CREATE TABLE IF NOT EXISTS `iask_answers` ( ??`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增ID', ??`text` text NOT NULL COMMENT '回答內(nèi)容', ??`question_id` int(18) NOT NULL COMMENT '問題ID', ??`answerer` varchar(255) NOT NULL COMMENT '回答者', ??`date` varchar(255) NOT NULL COMMENT '回答時(shí)間', ??`is_good` int(11) NOT NULL COMMENT '是否是最佳答案', ??PRIMARY KEY (`id`) ) ENGINE=InnoDB??DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `iask_questions` ( ??`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '問題ID', ??`text` text NOT NULL COMMENT '問題內(nèi)容', ??`questioner` varchar(255) NOT NULL COMMENT '提問者', ??`date` date NOT NULL COMMENT '提問時(shí)間', ??`ans_num` int(11) NOT NULL COMMENT '回答數(shù)量', ??`url` varchar(255) NOT NULL COMMENT '問題鏈接', ??PRIMARY KEY (`id`) ) ENGINE=InnoDB??DEFAULT CHARSET=utf8; |
運(yùn)行的時(shí)候執(zhí)行如下命令即可
nohup python spider.py &| 1 | nohup python spider.py & |
代碼寫的不好,僅供大家學(xué)習(xí)參考使用,如有問題,歡迎留言交流。
運(yùn)行結(jié)果查看
我們把PHP文件和log文件放在同一目錄下,運(yùn)行PHP文件,便可以看到如下的內(nèi)容:
小伙伴們趕快試一下吧。
轉(zhuǎn)載請注明:靜覓 ? Python爬蟲實(shí)戰(zhàn)六之抓取愛問知識人問題并保存至數(shù)據(jù)庫
總結(jié)
以上是生活随笔為你收集整理的Python爬虫实战六之抓取爱问知识人问题并保存至数据库的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu Linux配置Nginx+
- 下一篇: 深度学习(九)caffe预测、特征可视化