信息檢索課程設計sdu視點新聞全站Python爬蟲爬取+索引構建+搜索引擎查詢練習程序(1805)。
以前在gh倉庫總結的內容,沒想到被人轉載不帶出處,不如我自己來發一遍叭。
源代碼:Github
爬蟲功能使用Python的scrapy庫實現,並用MongoDB數據庫進行存儲。
索引構建和搜索功能用Python的Whoosh和jieba庫實現。(由於lucene是java庫,所以pyLucene庫的安裝極其麻煩,因此選用Python原生庫Whoosh實現,並使用jieba進行中文分詞。)
搜索網頁界面用django實現,頁面模板套用BootCDN。
1 要求
以下是檢索的基本要求:可以利用lucene、nutch等開源工具,利用Python、Java等編程語言,但需要分別演示並說明原理。
-
Web網頁信息抽取
以山東大學新聞網為起點進行網頁的循環爬取,保持爬蟲在view.sdu.edu.cn之內(即只爬取這個站點的網頁),爬取的網頁數量越多越好。 -
索引構建
對上一步爬取到的網頁進行結構化預處理,包括基於模板的信息抽取、分字段解析、分詞、構建索引等。 -
檢索排序
對上一步構建的索引庫進行查詢,對於給定的查詢,給出檢索結果,明白排序的原理及方法。
2 運行方式
-
運行
sduspider/run.py
來進行網絡爬蟲,這個過程將持續十多個小時,但可以隨時終止,在下次運行時繼續。 -
運行
indexbuilder/index_builder.py
來對數據庫中的72000條數據構建索引,該過程將持續幾個小時,但可以隨時終止。 -
如果不熟悉Whoosh庫的構建索引和query搜索功能,可以參考運行
indexbuilder/sample.py
。 -
運行
indexbuilder/query.py
來測試搜索功能。 -
運行
searchengine/run_server.py
打開搜索網頁服務器,在瀏覽器中打開127.0.0.1:8000進入搜索頁面執行搜索。
3 所需python庫
- scrapy
- requests
- pymongo
- whoosh
- jieba
- django
4 所需數據庫
- MongoDB
- Mongo Management Studio 可視化工具(可選)
5 爬蟲特性
爬蟲代碼位於sduspider/
目錄下。
5.1 爬取內容
爬蟲爬取以 http://www.view.sdu.edu.cn/info/ 打頭的所有新聞頁面的內容,這些內容包括:
Item | Item name |
---|---|
標題 | newsTitle |
鏈接 | newsUrl |
閱讀量 | newsCliek |
發布時間 | newsPublishTime |
文章內容 | newsContent |
# spider.py
# 爬取當前網頁
print('start parse : ' + response.url)
self.destination_list.remove(response.url)
if response.url.startswith("http://www.view.sdu.edu.cn/info/"):
item = NewsItem()
for box in response.xpath('//div[@class="new_show clearfix"]/div[@class="le"]'):
# article title
item['newsTitle'] = box.xpath('.//div[@class="news_tit"]/h3/text()').extract()[0].strip()
# article url
item['newsUrl'] = response.url
item['newsUrlMd5'] = self.md5(response.url)
# article click time
item['newsClick'] = box.xpath('.//div[@class="news_tit"]/p/span/script/text()').extract()[0].strip()
pattern = re.compile(r'\(.*?\)')
parameters = re.search(pattern, item['newsClick']).group(0)
parameters = parameters[1:-1].split(',')
parameters[0] = re.search(re.compile(r'\".*?\"'), parameters[0]).group(0)[1:-1]
parameters[1] = parameters[1].strip()
parameters[2] = parameters[2].strip()
request_url = 'http://www.view.sdu.edu.cn/system/resource/code/news/click/dynclicks.jsp'
request_data = {'clicktype': parameters[0], 'owner': parameters[1], 'clickid': parameters[2]}
request_get = requests.get(request_url, params=request_data)
item['newsClick'] = request_get.text
# article publish time
item['newsPublishTime'] = box.xpath('.//div[@class="news_tit"]/p[not(@style)]/text()').extract()[0].strip()[5:]
# article content
item['newsContent'] = box.xpath('.//div[@class="news_content"]').extract()[0].strip()
regexp = re.compile(r'<[^>]+>', re.S)
item['newsContent'] = regexp.sub('',item['newsContent']) # delete templates <>
# 索引構建flag
item['indexed'] = 'False'
# yield it
yield item
5.2 寬度優先搜索爬取
爬蟲基於寬度優先搜索,對http://www.view.sdu.edu.cn/區段的網址進行爬取,並將http://www.view.sdu.edu.cn/info/區段的新聞內容提取出來。
# settings.py
# 先進先出,廣度優先
DEPTH_PRIORITY = 1
SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.FifoMemoryQueue'
5.3 二分法去重
所有已經爬取過的網址都會以MD5特征的形式順序存儲在list中,當獲取新的url時,通過二分法查找list中是否存在該url的特征值,以達到去重的目的。
Scrapy庫自帶了查重去重的功能,但為了保證效率,自行編寫了二分法去重,但並未關閉scrapy庫自帶的去重功能。
# spider.py
# md5 check
md5_url = self.md5(real_url)
if self.binary_md5_url_search(md5_url) > -1: # 二分法查找存在當前MD5
pass
else:
self.binary_md5_url_insert(md5_url) # 二分法插入當前MD5
self.destination_list.append(real_url) # 插入爬蟲等待序列
yield scrapy.Request(real_url, callback=self.parse, errback=self.errback_httpbin)
5.4 斷點續爬
每爬取一定次數后都會將當前爬蟲狀態存儲在pause文件夾下,重新運行爬蟲時會繼續上一次保存的斷點進行爬取。Scrapy有自帶的斷點續爬功能(在settings.py中設置),但貌似在Pycharm中行不通。
# spider.py
# counter++,並在合適的時候保存斷點
def counter_plus(self):
print('待爬取網址數:' + (str)(len(self.destination_list)))
# 斷點續爬功能之保存斷點
if self.counter % self.save_frequency == 0: # 爬蟲經過save_frequency次爬取后
print('Rayiooo:正在保存爬蟲斷點....')
f = open('./pause/response.seen', 'wb')
pickle.dump(self.url_md5_seen, f)
f.close()
f = open('./pause/response.dest', 'wb')
pickle.dump(self.destination_list, f)
f.close()
self.counter = self.save_frequency
self.counter += 1 # 計數器+1
5.5 數據存入MongoDB
關系類數據庫不適用於爬蟲數據存儲,因此使用非關系類數據庫MongoDB。數據庫可以用可視化工具方便查看,例如Mongo Management Studio。
# pipelines.py
class MongoDBPipeline(object):
def __init__(self):
host = settings["MONGODB_HOST"]
port = settings["MONGODB_PORT"]
dbname = settings["MONGODB_DBNAME"]
sheetname = settings["MONGODB_SHEETNAME"]
# 創建MONGODB數據庫鏈接
client = pymongo.MongoClient(host=host, port=port)
# 指定數據庫
mydb = client[dbname]
# 存放數據的數據庫表名
self.post = mydb[sheetname]
def process_item(self, item, spider):
data = dict(item)
# self.post.insert(data) # 直接插入的方式有可能導致數據重復
# 更新數據庫中的數據,如果upsert為Ture,那么當沒有找到指定的數據時就直接插入,反之不執行插入
self.post.update({'newsUrlMd5': item['newsUrlMd5']}, data, upsert=True)
return item
6 索引構建特性
索引構建代碼位於indexbuilder/
目錄下。
6.1 斷點續構
構建倒排索引的過程比較緩慢,每小時只能構建10000條新聞的索引,因此在索引構建時及時存儲新構建的索引,以保證能夠斷點續構。
6.2 中文分詞
Whoosh自帶的Analyzer分詞僅針對英文文章,而不適用於中文。從jieba庫中引用的ChineseAnalyzer保證了能夠對Documents進行中文分詞。同樣,ChineseAnalyzer在search時也能夠對中文查詢query提取關鍵字並進行搜索。
# index_builder.py
from jieba.analyse import ChineseAnalyzer
analyzer = ChineseAnalyzer()
# 創建索引模板
schema = Schema(
newsId=ID(stored=True),
newsTitle=TEXT(stored=True, analyzer=analyzer),
newsUrl=ID(stored=True),
newsClick=NUMERIC(stored=True, sortable=True),
newsPublishTime=TEXT(stored=True),
newsContent=TEXT(stored=False, analyzer=analyzer), # 文章內容太長了,不存
)
6.3 Query類提供搜索API
Query類自動執行了從index索引文件夾中取倒排索引來執行搜索,並返回一個結果數組。
# query.py
if __name__ == '__main__':
q = Query()
q.standard_search('軟件園校區')
7 搜索引擎特性
搜索引擎代碼位於searchengine/
目錄下。
7.1 Django搭建Web界面
Django適合Web快速開發。result頁面繼承了main頁面,搜索結果可以按照result中的指示顯示在頁面中。在django模板繼承下,改變main.html中的頁面布局,result.html的布局也會相應改變而不必使用Ctrl+c、Ctrl+v的方式改變。
# view.py
def search(request):
res = None
if 'q' in request.GET and request.GET['q']:
res = q.standard_search(request.GET['q']) # 獲取搜索結果
c = {
'query': request.GET['q'],
'resAmount': len(res),
'results': res,
}
else:
return render_to_response('main.html')
return render_to_response('result.html', c) # 展示搜索結果
7.2 搜索迅速
第一次搜索時,可能因為倒排索引index的取出時間較長而搜索緩慢,但一旦index取出,對於70000余條新聞的搜索將非常迅速,秒出結果。
參考資料
[1]scrapy爬蟲框架入門實例
[2]筆記:scrapy爬取的數據存入MySQL,MongoDB
[3]搜索那些事 - 用Golang寫一個搜索引擎(0x00) --- 從零開始(分享自知乎網)
[4]Whoosh + jieba 中文檢索
[5]利用whoosh對mongoDB的中文文檔建立全文檢索
[6]Django 創建第一個項目
[7]Django模板系統(非常詳細)