日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python爬虫实战六之抓取爱问知识人问题并保存至数据库

發布時間:2025/3/21 python 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python爬虫实战六之抓取爱问知识人问题并保存至数据库 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

大家好,本次為大家帶來的是抓取愛問知識人的問題并將問題和答案保存到數據庫的方法,涉及的內容包括:

  • Urllib的用法及異常處理
  • Beautiful Soup的簡單應用
  • MySQLdb的基礎用法
  • 正則表達式的簡單應用

環境配置

在這之前,我們需要先配置一下環境,我的Python的版本為2.7,需要額外安裝的庫有兩個,一個是Beautiful Soup,一個是MySQLdb,在這里附上兩個庫的下載地址,

Beautiful Soup? ? ?MySQLdb

大家可以下載之后通過如下命令安裝

python setup.py install
1 python setup.py install

環境配置好之后,我們便可以開心地擼爬蟲了

框架思路

首先我們隨便找一個分類地址,外語學習 – 愛問知識人,打開之后可以看到一系列的問題列表。

我們在這個頁面需要獲取的東西有:

總的頁碼數,每一頁的所有問題鏈接。

接下來我們需要遍歷所有的問題,來抓取每一個詳情頁面,提取問題,問題內容,回答者,回答時間,回答內容。

最后,我們需要把這些內容存儲到數據庫中。

要點簡析

其實大部分內容相信大家會了前面的內容,這里的爬蟲思路已經融匯貫通了,這里就說一下一些擴展的功能

1.日志輸出

日志輸出,我們要輸出時間和爬取的狀態,比如像下面這樣:

[2015-08-10 03:05:20] 113011 號問題存在其他答案 我個人認為應該是櫻桃溝很美的

[2015-08-10 03:05:20] 保存到數據庫,此問題的ID為 113011

[2015-08-10 03:05:20] 當前爬取第 2 的內容,發現一個問題 百度有一個地方,花兒帶著芳香,水兒流淌奔騰是什么意思 多多幫忙哦 回答數量 1

[2015-08-10 03:05:19] 保存到數據庫,此問題的ID為 113010

所以,我們需要引入時間函數,然后寫一個獲取當前時間的函數

import time#獲取當前時間 def getCurrentTime(self):return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time()))#獲取當前時間 def getCurrentDate(self):return time.strftime('%Y-%m-%d',time.localtime(time.time()))
1 2 3 4 5 6 7 8 9 import time #獲取當前時間 def getCurrentTime(self): ????return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time())) ???? #獲取當前時間 def getCurrentDate(self): ????return time.strftime('%Y-%m-%d',time.localtime(time.time()))

以上分別是獲取帶具體時間和獲取日期的函數,在輸出時,我們可以在輸出語句的前面調用這函數即可。

然后我們需要將緩沖區設置輸出到log中,在程序的最前面加上這兩句即可

f_handler=open('out.log', 'w') sys.stdout=f_handler
1 2 f_handler=open('out.log', 'w') sys.stdout=f_handler

這樣,所有的print語句輸出的內容就會保存到out.log文件中了。

2.頁碼保存

爬蟲爬取過程中可能出現各種各樣的錯誤,這樣會導致爬蟲的中斷,如果我們重新運行爬蟲,那么就會導致爬蟲從頭開始運行了,這樣顯然是不合理的。所以,我們需要把當前爬取的頁面保存下來,比如可以保存到文本中,假如爬蟲中斷了,重新運行爬蟲,讀取文本文件的內容,接著爬取即可。

大家可以稍微參考一下函數的實現:

#主函數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(),"獲取到目錄頁面個數",self.total_num,"個"if not start_page:start_page = self.total_numfor x in range(1,start_page):print self.getCurrentTime(),"正在抓取第",start_page-x+1,"個頁面"try:self.getQuestions(start_page-x+1)except urllib2.URLError, e:if hasattr(e, "reason"):print self.getCurrentTime(),"某總頁面內抓取或提取失敗,錯誤原因", e.reasonexcept Exception,e: print self.getCurrentTime(),"某總頁面內抓取或提取失敗,錯誤原因:",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 ????#主函數 ????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(),"獲取到目錄頁面個數",self.total_num,"個" ????????if not start_page: ????????????start_page = self.total_num ????????for x in range(1,start_page): ????????????print self.getCurrentTime(),"正在抓取第",start_page-x+1,"個頁面" ????????????try: ????????????????self.getQuestions(start_page-x+1) ????????????except urllib2.URLError, e: ????????????????if hasattr(e, "reason"): ????????????????????print self.getCurrentTime(),"某總頁面內抓取或提取失敗,錯誤原因", e.reason ????????????except Exception,e:?? ????????????????print self.getCurrentTime(),"某總頁面內抓取或提取失敗,錯誤原因:",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()

這樣,不管我們爬蟲中途遇到什么錯誤,媽媽也不會擔心了

3.頁面處理

頁面處理過程中,我們可能遇到各種各樣奇葩的HTML代碼,和上一節一樣,我們沿用一個頁面處理類即可。

import re#處理頁面標簽類 class Tool:#將超鏈接廣告剔除removeADLink = re.compile('<div class="link_layer.*?</div>')#去除img標簽,1-7位空格,&nbsp;removeImg = re.compile('<img.*?>| {1,7}|&nbsp;')#刪除超鏈接標簽removeAddr = re.compile('<a.*?>|</a>')#把換行的標簽換為\nreplaceLine = re.compile('<tr>|<div>|</div>|</p>')#將表格制表<td>替換為\treplaceTD= re.compile('<td>')#將換行符或雙換行符替換為\nreplaceBR = re.compile('<br><br>|<br>')#將其余標簽剔除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()將前后多余內容刪除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 #處理頁面標簽類 class Tool: ???? ????#將超鏈接廣告剔除 ????removeADLink = re.compile('<div class="link_layer.*?</div>') ????#去除img標簽,1-7位空格,&nbsp; ????removeImg = re.compile('<img.*?>| {1,7}|&nbsp;') ????#刪除超鏈接標簽 ????removeAddr = re.compile('<a.*?>|</a>') ????#把換行的標簽換為\n ????replaceLine = re.compile('<tr>|<div>|</div>|</p>') ????#將表格制表<td>替換為\t ????replaceTD= re.compile('<td>') ????#將換行符或雙換行符替換為\n ????replaceBR = re.compile('<br><br>|<br>') ????#將其余標簽剔除 ????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()將前后多余內容刪除 ????????return x.strip()

我們可以用一段含有HTML代碼的文字,經過調用replace方法之后,各種冗余的HTML代碼就會處理好了。

比如我們這么一段代碼:

<articleclass="article-content"> <h2>前言</h2> <p>最近發現MySQL服務隔三差五就會掛掉,導致我的網站和爬蟲都無法正常運作。自己的網站是基于MySQL,在做爬蟲存取一些資料的時 候也是基于MySQL,數據量一大了,MySQL它就有點受不了了,時不時會崩掉,雖然我自己有網站監控和郵件通知,但是好多時候還是需要我來手動連接我 的服務器重新啟動一下我的MySQL,這樣簡直太不友好了,所以,我就覺定自己寫個腳本,定時監控它,如果發現它掛掉了就重啟它。</p> <p>好了,閑言碎語不多講,開始我們的配置之旅。</p> <p>運行環境:<strong>Ubuntu Linux 14.04</strong></p> <h2>編寫Shell腳本</h2> <p>首先,我們要編寫一個shell腳本,腳本主要執行的邏輯如下:</p> <p>顯示mysqld進程狀態,如果判斷進程未在運行,那么輸出日志到文件,然后啟動mysql服務,如果進程在運行,那么不執行任何操 作,可以選擇性輸出監測結果。</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 編程基礎</a></p> <p>shell腳本的后綴為sh,在任何位置新建一個腳本文件,我選擇在 /etc/mysql 目錄下新建一個 listen.sh 文件。</p> <p>執行如下命令:</p>
1 2 3 4 5 6 7 8 9 10 11 12 <article class="article-content"> <h2>前言</h2> <p>最近發現MySQL服務隔三差五就會掛掉,導致我的網站和爬蟲都無法正常運作。自己的網站是基于MySQL,在做爬蟲存取一些資料的時候也是基于MySQL,數據量一大了,MySQL它就有點受不了了,時不時會崩掉,雖然我自己有網站監控和郵件通知,但是好多時候還是需要我來手動連接我的服務器重新啟動一下我的MySQL,這樣簡直太不友好了,所以,我就覺定自己寫個腳本,定時監控它,如果發現它掛掉了就重啟它。</p> <p>好了,閑言碎語不多講,開始我們的配置之旅。</p> <p>運行環境:<strong>Ubuntu Linux 14.04</strong></p> <h2>編寫Shell腳本</h2> <p>首先,我們要編寫一個shell腳本,腳本主要執行的邏輯如下:</p> <p>顯示mysqld進程狀態,如果判斷進程未在運行,那么輸出日志到文件,然后啟動mysql服務,如果進程在運行,那么不執行任何操作,可以選擇性輸出監測結果。</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 編程基礎</a></p> <p>shell腳本的后綴為sh,在任何位置新建一個腳本文件,我選擇在 /etc/mysql 目錄下新建一個 listen.sh 文件。</p> <p>執行如下命令:</p>

經過處理后便會變成如下的樣子:

前 言 最近發現MySQL服務隔三差五就會掛掉,導致我的網站和爬蟲都無法正常運作。自己的網站是基于MySQL,在做爬蟲存取一些資料的時候也是基于 MySQL,數據量一大了,MySQL它就有點受不了了,時不時會崩掉,雖然我自己有網站監控和郵件通知,但是好多時候還是需要我來手動連接我的服務器重 新啟動一下我的MySQL,這樣簡直太不友好了,所以,我就覺定自己寫個腳本,定時監控它,如果發現它掛掉了就重啟它。 好了,閑言碎語不多講,開始我們的配置之旅。 運行環境:UbuntuLinux14.04 編寫Shell腳本 首先,我們要編寫一個shell腳本,腳本主要執行的邏輯如下: 顯示mysqld進程狀態,如果判斷進程未在運行,那么輸出日志到文件,然后啟動mysql服務,如果進程在運行,那么不執行任何操作,可以選擇性輸出監 測結果。 可能大家對于shell腳本比較陌生,在這里推薦官方的shell腳本文檔來參考一下 UbuntuShell編程基礎 shell腳本的后綴為sh,在任何位置新建一個腳本文件,我選擇在/etc/mysql目錄下新建一個listen.sh文件。 執行如下命令:
1 2 3 4 5 6 7 8 9 10 11 前言 最近發現MySQL服務隔三差五就會掛掉,導致我的網站和爬蟲都無法正常運作。自己的網站是基于MySQL,在做爬蟲存取一些資料的時候也是基于MySQL,數據量一大了,MySQL它就有點受不了了,時不時會崩掉,雖然我自己有網站監控和郵件通知,但是好多時候還是需要我來手動連接我的服務器重新啟動一下我的MySQL,這樣簡直太不友好了,所以,我就覺定自己寫個腳本,定時監控它,如果發現它掛掉了就重啟它。 好了,閑言碎語不多講,開始我們的配置之旅。 運行環境:UbuntuLinux14.04 編寫Shell腳本 首先,我們要編寫一個shell腳本,腳本主要執行的邏輯如下: 顯示mysqld進程狀態,如果判斷進程未在運行,那么輸出日志到文件,然后啟動mysql服務,如果進程在運行,那么不執行任何操作,可以選擇性輸出監測結果。 可能大家對于shell腳本比較陌生,在這里推薦官方的shell腳本文檔來參考一下 UbuntuShell編程基礎 shell腳本的后綴為sh,在任何位置新建一個腳本文件,我選擇在/etc/mysql目錄下新建一個listen.sh文件。 執行如下命令:

經過上面的處理,所有亂亂的代碼都會被處理好了。

4.保存到數據庫

在這里,我們想實現一個通用的方法,就是把存儲的一個個內容變成字典的形式,然后執行插入語句的時候,自動構建對應的sql語句,插入數據。

比如我們構造如下的字典:

#構造最佳答案的字典 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 #構造最佳答案的字典 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) ????????}

構造sql語句并插入到數據庫的方法如下:

#插入數據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()#判斷是否執行成功if result:return insert_idelse:return 0except MySQLdb.Error,e:#發生錯誤時回滾self.db.rollback()#主鍵唯一,無法插入if "key 'PRIMARY'" in e.args[1]:print self.getCurrentTime(),"數據已存在,未插入數據"else:print self.getCurrentTime(),"插入數據失敗,原因 %d: %s" % (e.args[0], e.args[1])except MySQLdb.Error,e:print self.getCurrentTime(),"數據庫錯誤,原因%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 #插入數據 ????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() ???????????????? #判斷是否執行成功 ???????????????? if result: ???????????????????? return insert_id ???????????????? else: ???????????????????? return 0 ???????????? except MySQLdb.Error,e: ???????????????? #發生錯誤時回滾 ???????????????? self.db.rollback() ???????????????? #主鍵唯一,無法插入 ???????????????? if "key 'PRIMARY'" in e.args[1]: ???????????????????? print self.getCurrentTime(),"數據已存在,未插入數據" ???????????????? else: ???????????????????? print self.getCurrentTime(),"插入數據失敗,原因 %d: %s" % (e.args[0], e.args[1]) ???????? except MySQLdb.Error,e: ???????????? print self.getCurrentTime(),"數據庫錯誤,原因%d: %s" % (e.args[0], e.args[1])

這里我們只需要傳入那個字典,便會構建出對應字典鍵值和鍵名的sql語句,完成插入。

5.PHP讀取日志

我們將運行結果輸出到了日志里,那么怎么查看日志呢?很簡單,在這里提供兩種方法

方法一:

PHP倒序輸出所有日志內容

<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>

此方法可以看到所有的輸入日志,但是如果日志太大了,那么就會報耗費內存太大,無法輸出。為此我們就有了第二種方法,利用linux命令,輸出后十行內容。

方法二:

<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秒刷新一次網頁來查看最新的日志。

源代碼放送

好了,閑言碎語不多講,直接上源碼了

spider.py
1 spider.py
#-*- coding:utf-8 -*-import urllib import urllib2 import re import time import types import page import mysql import sys from bs4 import BeautifulSoupclass Spider:#初始化def __init__(self):self.page_num = 1self.total_num = Noneself.page_spider = page.Page()self.mysql = mysql.Mysql()#獲取當前時間def getCurrentTime(self):return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time()))#獲取當前時間def getCurrentDate(self):return time.strftime('%Y-%m-%d',time.localtime(time.time()))#通過網頁的頁碼數來構建網頁的URLdef getPageURLByNum(self, page_num):page_url = "http://iask.sina.com.cn/c/978-all-" + str(page_num) +".html"return page_url#通過傳入網頁頁碼來獲取網頁的HTMLdef getPageByNum(self, page_num):request = urllib2.Request(self.getPageURLByNum(page_num))try:response = urllib2.urlopen(request)except urllib2.URLError, e:if hasattr(e, "code"):print self.getCurrentTime(),"獲取頁面失敗,錯誤代號", e.codereturn Noneif hasattr(e, "reason"):print self.getCurrentTime(),"獲取頁面失敗,原因", e.reasonreturn Noneelse:page = response.read().decode("utf-8")return page#獲取所有的頁碼數def getTotalPageNum(self):print self.getCurrentTime(),"正在獲取目錄頁面個數,請稍候"page = self.getPageByNum(1)#匹配所有的頁碼數,\u4e0b\u4e00\u9875是下一頁的UTF8編碼pattern = re.compile(u'<span class="more".*?>.*?<span.*?<a href.*?class="">(.*?)</a>\s*<a.*?\u4e0b\u4e00\u9875</a>',re.S)match = re.search(pattern, page)if match:return match.group(1)else:print self.getCurrentTime(),"獲取總頁碼失敗"#分析問題的代碼,得到問題的提問者,問題內容,回答個數,提問時間def getQuestionInfo(self, question):if not type(question) is types.StringType:question = str(question)#print questionpattern = re.compile(u'<span.*?question-face.*?>.*?<img.*?alt="(.*?)".*?</span>.*?<ahref="(.*?)".*?>(.*?)</a>.*?answer_num.*?>(\d*).*?</span>.*?answer_time.*?>(.*?)</span>',re.S)match = re.search(pattern, question)if match:#獲得提問者author = match.group(1)#問題鏈接href = match.group(2)#問題詳情text = match.group(3)#回答個數ans_num = match.group(4)#回答時間time = match.group(5)time_pattern = re.compile('\d{4}\-\d{2}\-\d{2}', re.S)time_match = re.search(time_pattern, time)if not time_match:time = self.getCurrentDate()return [author, href, text, ans_num, time]else:return None#獲取全部問題def getQuestions(self, page_num):#獲得目錄頁面的HTMLpage = self.getPageByNum(page_num)soup = BeautifulSoup(page)#分析獲得所有問題questions = soup.select("div.question_list ul li")#遍歷每一個問題for question in questions:#獲得問題的詳情info = self.getQuestionInfo(question)if info:#得到問題的URLurl = "http://iask.sina.com.cn/" + info[1]#通過URL來獲取問題的最佳答案和其他答案ans = self.page_spider.getAnswer(url)print self.getCurrentTime(),"當前爬取第",page_num,"的內容,發現一個問題",info[2],"回答數 量",info[3]#構造問題的字典,插入問題ques_dict = {"text": info[2],"questioner": info[0],"date": info[4],"ans_num": info[3],"url": url}#獲得插入的問題的自增ID insert_id = self.mysql.insertData("iask_questions",ques_dict)#得到最佳答案good_ans = ans[0]print self.getCurrentTime(),"保存到數據庫,此問題的ID為",insert_id#如果存在最佳答案,那么就插入if good_ans:print self.getCurrentTime(),insert_id,"號問題存在最佳答案",good_ans[0]#構造最佳答案的字典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)}#插入最佳答案if self.mysql.insertData("iask_answers",good_ans_dict):print self.getCurrentTime(),"保存最佳答案成功"else:print self.getCurrentTime(),"保存最佳答案失敗"#獲得其他答案other_anses = ans[1]#遍歷每一個其他答案for other_ans in other_anses:#如果答案存在if other_ans:print self.getCurrentTime(),insert_id,"號問題存在其他答案",other_ans[0]#構造其他答案的字典other_ans_dict = {"text": other_ans[0],"answerer": other_ans[1],"date": other_ans[2],"is_good": str(other_ans[3]),"question_id": str(insert_id)}#插入這個答案if self.mysql.insertData("iask_answers",other_ans_dict):print self.getCurrentTime(),"保存其他答案成功"else:print self.getCurrentTime(),"保存其他答案失敗"#主函數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(),"獲取到目錄頁面個數",self.total_num,"個"if not start_page:start_page = self.total_numfor x in range(1,start_page):print self.getCurrentTime(),"正在抓取第",start_page-x+1,"個頁面"try:self.getQuestions(start_page-x+1)except urllib2.URLError, e:if hasattr(e, "reason"):print self.getCurrentTime(),"某總頁面內抓取或提取失敗,錯誤原因", e.reasonexcept Exception,e: print self.getCurrentTime(),"某總頁面內抓取或提取失敗,錯誤原因:",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()spider = Spider() spider.main()
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() ???????? ????#獲取當前時間 ????def getCurrentTime(self): ????????return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time())) ???? ????#獲取當前時間 ????def getCurrentDate(self): ????????return time.strftime('%Y-%m-%d',time.localtime(time.time())) ???? ????#通過網頁的頁碼數來構建網頁的URL
page.py
1 page.py
#-*- 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()#獲取當前時間def getCurrentDate(self):return time.strftime('%Y-%m-%d',time.localtime(time.time()))#獲取當前時間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:if hasattr(e, "code"):print self.getCurrentTime(),"獲取問題頁面失敗,錯誤代號", e.codereturn Noneif hasattr(e, "reason"):print self.getCurrentTime(),"獲取問題頁面失敗,原因", e.reasonreturn None#傳入一個List,返回它的標簽里的內容,如果為空返回Nonedef getText(self, html):if not type(html) is types.StringType:html = str(html)#提取出<pre>標簽里的內容pattern = re.compile('<pre.*?>(.*?)</pre>', re.S)match = re.search(pattern, html)#如果匹配成功if match:return match.group(1)else: return None#傳入最佳答案的HTML,分析出回答者和回答時間def getGoodAnswerInfo(self, html):pattern = re.compile('"answer_tip.*?<a.*?>(.*?)</a>.*?<span class="time.*?>.*?\|(.*?)</span>', re.S)match = re.search(pattern, html)#如果匹配,返回回答者和回答時間if match:time = match.group(2)time_pattern = re.compile('\d{2}\-\d{2}\-\d{2}', re.S)time_match = re.search(time_pattern, time)if not time_match:time = self.getCurrentDate()else:time = "20"+timereturn [match.group(1),time]else:return [None,None]#獲得最佳答案def getGoodAnswer(self, page):soup = BeautifulSoup(page)text = soup.select("div.good_point div.answer_text pre")if len(text) > 0:#獲得最佳答案的內容ansText = self.getText(str(text[0]))ansText = self.tool.replace(ansText)#獲得最佳答案的回答者信息info = soup.select("div.good_point div.answer_tip")ansInfo = self.getGoodAnswerInfo(str(info[0]))#將三者組合成一個Listanswer = [ansText, ansInfo[0], ansInfo[1],1]return answerelse:#如果不存在最佳答案,那么就返回空return None#傳入回答者HTML,分析出回答者,回答時間def getOtherAnswerInfo(self, html):if not type(html) is types.StringType:html = str(html)pattern = re.compile('"author_name.*?>(.*?)</a>.*?answer_t">(.*?)</span>',re.S)match = re.search(pattern, html)#獲得每一個回答的回答者信息和回答時間if match:time = match.group(2)time_pattern = re.compile('\d{2}\-\d{2}\-\d{2}', re.S)time_match = re.search(time_pattern, time)if not time_match:time = self.getCurrentDate()else:time = "20"+timereturn [match.group(1),time]else:return [None,None]#獲得其他答案def getOtherAnswers(self, page):soup = BeautifulSoup(page)results = soup.select("div.question_box li.clearfix .answer_info")#所有答案,包含好多個List,每個List包含了回答內容,回答者,回答時間answers = []for result in results:#獲得回答內容ansSoup = BeautifulSoup(str(result))text = ansSoup.select(".answer_txt span pre")ansText = self.getText(str(text[0]))ansText = self.tool.replace(ansText)#獲得回答者和回答時間info = ansSoup.select(".answer_tj")ansInfo = self.getOtherAnswerInfo(info[0])#將三者組合成一個Listanswer = [ansText, ansInfo[0], ansInfo[1],0]#加入到answersanswers.append(answer)return answers#主函數def getAnswer(self, url):if not url:url = "http://iask.sina.com.cn/b/gQiuSNCMV.html"page = self.getPageByURL(url)good_ans = self.getGoodAnswer(page)other_ans = self.getOtherAnswers(page)return [good_ans,other_ans]page = Page() page.getAnswer(None)
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() ???? ????#獲取當前時間 ????def getCurrentDate(self): ????????return time.strftime('%Y-%m-%d',time.localtime(time.time())) ???? ????#獲取當前時間 ????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:
tool.py
1 tool.py
#-*- coding:utf-8 -*- import re#處理頁面標簽類 class Tool:#將超鏈接廣告剔除removeADLink = re.compile('<div class="link_layer.*?</div>')#去除img標簽,1-7位空格,&nbsp;removeImg = re.compile('<img.*?>| {1,7}|&nbsp;')#刪除超鏈接標簽removeAddr = re.compile('<a.*?>|</a>')#把換行的標簽換為\nreplaceLine = re.compile('<tr>|<div>|</div>|</p>')#將表格制表<td>替換為\treplaceTD= re.compile('<td>')#將換行符或雙換行符替換為\nreplaceBR = re.compile('<br><br>|<br>')#將其余標簽剔除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()將前后多余內容刪除return x.strip()
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 #處理頁面標簽類 class Tool: ???? ????#將超鏈接廣告剔除 ????removeADLink = re.compile('<div class="link_layer.*?</div>') ????#去除img標簽,1-7位空格,&nbsp; ????removeImg = re.compile('<img.*?>| {1,7}|&nbsp;') ????#刪除超鏈接標簽 ????removeAddr = re.compile('<a.*?>|</a>') ????#把換行的標簽換為\n ????replaceLine = re.compile('<tr>|<div>|</div>|</p>') ????#將表格制表<td>替換為\t ????replaceTD= re.compile('<td>') ????#將換行符或雙換行符替換為\n ????replaceBR = re.compile('<br><br>|<br>') ????#將其余標簽剔除 ????removeExtraTag = re.compile('<.*?>')
mysql.py
1 mysql.py
# -*- coding:utf-8 -*-import MySQLdb import timeclass Mysql:#獲取當前時間def getCurrentTime(self):return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time()))#數據庫初始化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(),"連接數據庫錯誤,原因%d: %s" % (e.args[0], e.args[1])#插入數據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()#判斷是否執行成功if result:return insert_idelse:return 0except MySQLdb.Error,e:#發生錯誤時回滾self.db.rollback()#主鍵唯一,無法插入if "key 'PRIMARY'" in e.args[1]:print self.getCurrentTime(),"數據已存在,未插入數據"else:print self.getCurrentTime(),"插入數據失敗,原因 %d: %s" % (e.args[0], e.args[1])except MySQLdb.Error,e:print self.getCurrentTime(),"數據庫錯誤,原因%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 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: ???? ????#獲取當前時間 ????def getCurrentTime(self): ????????return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time())) ???? ????#數據庫初始化 ????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(),"連接數據庫錯誤,原因%d: %s" % (e.args[0], e.args[1]) ????#插入數據 ????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() ???????????????? #判斷是否執行成功 ???????????????? if result: ???????????????????? return insert_id ???????????????? else: ???????????????????? return 0 ???????????? except MySQLdb.Error,e: ???????????????? #發生錯誤時回滾 ???????????????? self.db.rollback() ???????????????? #主鍵唯一,無法插入 ???????????????? if "key 'PRIMARY'" in e.args[1]: ???????????????????? print self.getCurrentTime(),"數據已存在,未插入數據" ???????????????? else: ???????????????????? print self.getCurrentTime(),"插入數據失敗,原因 %d: %s" % (e.args[0], e.args[1]) ???????? except MySQLdb.Error,e: ???????????? print self.getCurrentTime(),"數據庫錯誤,原因%d: %s" % (e.args[0], e.args[1])

數據庫建表SQL如下:

CREATE TABLE IF NOT EXISTS `iask_answers` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增ID',`text` text NOT NULL COMMENT '回答內容',`question_id` int(18) NOT NULL COMMENT '問題ID',`answerer` varchar(255) NOT NULL COMMENT '回答者',`date` varchar(255) NOT NULL COMMENT '回答時間',`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 '問題內容',`questioner` varchar(255) NOT NULL COMMENT '提問者',`date` date NOT NULL COMMENT '提問時間',`ans_num` int(11) NOT NULL COMMENT '回答數量',`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 '回答內容', ??`question_id` int(18) NOT NULL COMMENT '問題ID', ??`answerer` varchar(255) NOT NULL COMMENT '回答者', ??`date` varchar(255) NOT NULL COMMENT '回答時間', ??`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 '問題內容', ??`questioner` varchar(255) NOT NULL COMMENT '提問者', ??`date` date NOT NULL COMMENT '提問時間', ??`ans_num` int(11) NOT NULL COMMENT '回答數量', ??`url` varchar(255) NOT NULL COMMENT '問題鏈接', ??PRIMARY KEY (`id`) ) ENGINE=InnoDB??DEFAULT CHARSET=utf8;

運行的時候執行如下命令即可

nohup python spider.py &
1 nohup python spider.py &

代碼寫的不好,僅供大家學習參考使用,如有問題,歡迎留言交流。

運行結果查看

我們把PHP文件和log文件放在同一目錄下,運行PHP文件,便可以看到如下的內容:

小伙伴們趕快試一下吧。

轉載請注明:靜覓 ? Python爬蟲實戰六之抓取愛問知識人問題并保存至數據庫

總結

以上是生活随笔為你收集整理的Python爬虫实战六之抓取爱问知识人问题并保存至数据库的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产伦精品一区二区三区免费视频 | 瑟瑟网站免费 | 99国内揄拍国内精品人妻免费 | 泰坦尼克号3小时49分的观看方法 | 亚洲男人天堂2022 | 久久久久国产精 | 美女在线一区 | 亚洲男女网站 | 免费男女视频 | 中文字幕人妻一区二区三区 | 亚洲精选在线 | 色婷婷视频在线观看 | jizz日本大全 | 琪琪电影午夜理论片八戒八戒 | 色播导航 | 91区 | www欧美| 不卡的av在线播放 | 亚洲在线免费视频 | 成人小视频在线观看 | 久久永久免费 | 欧美日韩xxxx| 香蕉视频免费在线播放 | 国产又粗又长又黄的视频 | 国产无遮挡又黄又爽 | 91私密视频| 欧美a级成人淫片免费看 | 久久久久久亚洲av毛片大全 | 久久久无码人妻精品无码 | 91成人午夜| 毛片中文字幕 | 在线电影一区二区三区 | 成人h动漫精品一区 | 97av在线播放| 首尔之春在线看 | 亚洲精选在线观看 | 日本色综合网 | 三叶草欧洲码在线 | 国产av无毛 | 国产成a人亚洲精v品无码 | 无码人妻精品一区二区三区99日韩 | 亚洲大尺度视频 | 天堂av在线网 | 91福利视频在线 | 自拍在线视频 | 91高清无打码 | 91正在播放| xxx久久| 制服丝袜国产在线 | 亚洲一区二区在线播放 | 精品成人一区二区三区久久精品 | 色福利视频 | 欧美一区二区久久 | 亚洲国产精品久久人人爱 | 亚洲第一国产 | 婷婷丁香六月天 | 天天综合av | 亚洲国产精彩视频 | 日本www高清视频 | julia一区二区三区在线观看 | 精品在线不卡 | 欧美另类自拍 | 国产成人精品无码免费看在线 | 少妇人妻偷人精品无码视频新浪 | 91精品国产综合久久久久久久 | 国产高潮网站 | 可以看的毛片 | 色妞在线| 欧美午夜寂寞影院 | 日韩激情久久 | 亚洲第一av在线 | 自偷自拍亚洲 | 亚洲成人午夜电影 | 在线播放你懂的 | 天天天天 | 中文字幕av无码一区二区三区 | 精品人妻中文无码av在线 | 久久久www成人免费毛片 | 久久精品国产亚洲AV高清综合 | xxx国产在线观看 | 噜噜噜噜私人影院 | 国产无码精品在线播放 | 免费极品av一视觉盛宴 | 亚洲视频精品在线观看 | 看黄色的网站 | 麻豆精品自拍 | 99精品自拍 | 精品久久久久久久久久 | 日韩av电影网 | 蜜桃久久一区二区三区 | www在线免费观看 | 无码国产精品一区二区高潮 | 少妇视频在线 | 熟女少妇内射日韩亚洲 | 国产成人在线免费观看 | 欧美性jizz18性欧美 | 丰满人妻老熟妇伦人精品 | 动漫美女被到爽 | 91av久久 |