之前初學python時寫過一個練手的Demo,程式實現了在主機上根據關鍵詞和獲得贊同數爬取“掘金”中的文章:利用Python爬蟲過濾“掘金”的關鍵詞檢索結果。但是這個專案只是簡單地實現了功能,在很多方面都需要加強。現利用假期在這個程式地基礎上修改了一下,加入了Web支援,並且部署到了我私人的伺服器上,大家如果有興趣可以訪問使用:點我試用。
- 首先看一下使用方法和結果:
![搜尋輸入介面](https://i.iter01.com/images/f2b601bb75e620b5a968c074049b4b77fff044b48145e6636f06e997dcc902fc.png)
![搜尋結果](https://i.iter01.com/images/c28f89b22531b852271b80ee43567badd003374bec07ee03ab1627af1898f009.png)
如果想要獲取更多結果,繼續點選 '點選獲得更多結果。。'就好了,不過後臺設定了不斷獲取資料的限制(15頁搜尋結果,一般夠用了)。
1. 專案結構
![專案結構](https://i.iter01.com/images/939d2e39c3e84ff474aee0def2edfd0693b8043a77f1a1847aa349f6e32a1c78.png)
其中'main'包中是程式檔案,'static'放置靜態檔案,'templates'放置html模板檔案,'venv'是虛擬環境,'app.py'是主程式入口檔案,'requirements.txt'記錄程式所有依賴及版本號。
2. app.py
app.py負責構建Flask應用,且由於程式功能比較簡單,將檢視函式也放置其中,
app = Flask(__name__)
# set the secret key. keep this really secret:
app.secret_key = os.urandom(24)
# 帶爬取的url地址,不包含請求引數
ajax_base_url = 'https://search-merger-ms.juejin.im/v1/search'
# 根目錄,返回輸入截面
@app.route('/')
def index():
return render_template('input.html')
# 搜尋功能檢視函式
@app.route('/search')
def search():
try:
baseline = int(request.args.get('baseline')) # 從請求引數中獲取文章贊同數的下限值
except ValueError:
raise InvalidParameter('輸入框不能為空或者請不要在輸入框第二欄中輸入非數字字元!')
keyword = quote(request.args.get('keyword')) # 獲取搜尋的關鍵字, urllib.parse.quote() 複雜處理url中的中文
if keyword is None or len(keyword) == 0:
raise InvalidParameter('輸入框不能為空!')
params = {} # 對應的請求引數
params['query'] = keyword
params['page'] = '0'
params['raw_result'] = 'false'
params['src'] = 'web'
new_url = url_manager.build_ajax_url(ajax_base_url, params) # 構建請求地址
craw_json = crawler.craw_one_page(crawler.parse_from_json) # 選擇json解析器
datas = craw_json(new_url, baseline) # 進行下載、解析,獲得結果
if datas is None or len(datas) == 0:
return
return render_template('output.html', datas=datas, keyword=request.args.get('keyword'), baseline=baseline) # keyword傳原始值,否則next_page中再進行quote則會出錯
# 請求獲得更多資料
@app.route('/nextPage')
def next_page():
keyword = quote(request.args.get('keyword')) # 獲取搜尋的關鍵字, urllib.parse.quote() 複雜處理url中的中文
try:
baseline = int(request.args.get('baseline'))
req_page = int(request.args.get('req_page'))
except ValueError:
return redirect(url_for('index'))
if keyword is None or len(keyword) == 0:
return redirect(url_for('index'))
params = {} # 對應的請求引數
params['query'] = keyword
params['page'] = str(req_page)
params['raw_result'] = 'false'
params['src'] = 'web'
new_url = url_manager.build_ajax_url(ajax_base_url, params) # 構建請求地址
craw_json = crawler.craw_one_page(crawler.parse_from_json) # 選擇json解析器
datas = craw_json(new_url, baseline) # 進行下載、解析,獲得結果
# 將結果物件構成的列表轉完成json陣列
json_array = []
for data in datas:
json_array.append(data.__dict__)
return jsonify(json_array)
# 引數錯誤介面
@app.errorhandler(InvalidParameter)
def invalid_param(error):
return render_template('param-error.html', error_message=error.message), error.status_code
if __name__ == '__main__':
app.debug = True
app.run()
複製程式碼
app.py中主要包含了三個檢視函式:index(), search(), next_page();search()負責搜尋文章資料,next_page()負責獲取下一頁的文章資料。
3. 下載並解析資料
我們可以通過兩種不同的URL來獲取掘金的文章資訊,一種會返回html資料,一種會返回JSON資料。我們選擇第二種方式獲取JSON資料。下面首先介紹程式的下載器: downloader.py
import urllib.request
def download_json(url):
if url is None:
print('one invalid url is found!')
return None
response = urllib.request.urlopen(url)
if response.getcode() != 200:
print('response from %s is invalid!' % url)
return None
return response.read().decode('utf-8')
複製程式碼
通過該方法返回的是JSON的字串資料。接下來使用解析器來解析JSON資料: json_parser.py
# 將json字元創解析為一個物件
def json_to_object(json_content):
if json_content is None:
print('parse error!json is None!')
return None
return json.loads(str(json_content))
# 從JSON構成的物件中提取出文章的title、link、collectionCount等資料,並將其封裝成一個Bean物件,最後將這些物件新增到結果列表中
def build_bean_from_json(json_collection, baseline):
if json_collection is None:
raise ParseError('build bean from json error! json_collection is None!')
list = json_collection['d'] # 文章的列表
result_list = [] # 結果的列表
if list is None or len(list) == 0:
return []
for element in list:
starCount = element['collectionCount'] # 獲得的收藏數,即獲得的贊數
if int(starCount) >= baseline: # 如果收藏數不小於baseline,則構建結果物件並新增到結果列表中
title = element['title']
link = element['originalUrl']
result = ResultBean(title, link, starCount)
result_list.append(result) # 新增到結果列表中
print(title, link, starCount)
return result_list
複製程式碼
4. 爬取器
上面的下載、解析都可以看作是爬取過程中的工具,下面我們通過爬取模組將下載和解析過程結合起來: crawler.py
# 爬取一頁資訊
def craw_one_page(func):
def in_craw_one_page(new_url, baseline=10): # 預設baseline=10
print('begin to main..')
content = downloader.download_json(new_url) # 根據URL獲取網頁
datas = func(content, baseline) # 一次解析所得的結果
print('main end..')
return datas
return in_craw_one_page
def parse_from_json(content, baseline):
json_collection = json_parser.json_to_object(content)
results = json_parser.build_bean_from_json(json_collection, baseline)
return results
def parse_from_html(content, baseline):
html_parser.build_soup(content) # 使用BeautifulSoup將html網頁構建成soup樹
results = html_parser.build_bean_from_html(baseline)
return results
複製程式碼
這裡使用閉包修飾爬取函式,使我們可以傳入html或JSON對應的解析器。
完成
- github: github
- 部署: 通過Gunicorn部署flask應用(阿里雲伺服器:Ubuntu 16.04)