隔壁的小姐姐讓我和她七夕呆一天,我想一天那么長,光聊天肯定很無聊,那么做點(diǎn)什么好呢?
想到這幾天正好在學(xué)Python爬蟲,于是我精心準(zhǔn)備了搭建Python爬蟲網(wǎng)站的教程,教小姐姐用Python,這樣肯定不會(huì)無聊了O(∩_∩)O哈哈~
01 基礎(chǔ)知識(shí)
為了讓小姐姐能聽懂,我提前跟她說了要預(yù)習(xí)的知識(shí)。

當(dāng)然,小姐姐也是有一定python基礎(chǔ)的,有了上面的準(zhǔn)備,相信我們一定能做一個(gè)深入的學(xué)習(xí)討論。
02 實(shí)現(xiàn)思路
這個(gè)項(xiàng)目主要由三個(gè)部分組成
- 爬蟲:爬取京東、1號店和淘寶的商品信息實(shí)現(xiàn)價(jià)格比較
- Flask:運(yùn)用Flask構(gòu)建Web應(yīng)用,顯示爬蟲結(jié)果
- pythonanywhere:通過pythonanywhere將Web應(yīng)用部署到云
03 爬蟲
第一步是用python爬蟲腳本抓取網(wǎng)頁上的商品信息,這里我們主要對京東、1號店和淘寶的商品信息進(jìn)行爬取。不同的網(wǎng)頁需要進(jìn)行不同的分析,但方法都是大同小異的。我們先以1號店為例看看如何編寫爬蟲代碼。
一、爬取1號店的商品數(shù)據(jù)
開始之前我們需要引入下面的python庫:
import requests
from lxml import html
import urllib.parse
接下來,我們定義函數(shù)crawler:
def crawler(word, products_list=[]):
這里的word就是我們需要搜索商品名稱,而products_list用來保存我們的爬取結(jié)果。
word = urllib.parse.quote(word)
當(dāng)我們查詢的商品名稱含有中文時(shí),是要對其進(jìn)行編碼處理的,urllib.parse.quote()函數(shù)可以幫助實(shí)現(xiàn)這一點(diǎn)。
當(dāng)我們在1號店搜索商品名稱時(shí),其實(shí)是發(fā)起了一個(gè)url請求,這個(gè)url中包含了我們要查詢的信息。因此我們需要將word參數(shù)加入到url中。
url = 'https://search.yhd.com/c0-0/k{0}'.format(word)
接下來的三步順其自然,獲取html源碼,將html源碼轉(zhuǎn)換為xpath對象,然后在html樹狀結(jié)構(gòu)中尋找包含商品信息的節(jié)點(diǎn)。
# 獲取html源碼
html_doc = requests.get(url).text
# xpath對象
selector = html.fromstring(html_doc)
# 商品列表
ul_list = selector.xpath('//div[@id="itemSearchList"]/div')
得到了當(dāng)前頁面的商品列表之后,我們需要對其進(jìn)行遍歷獲取其中每個(gè)商品的名稱、價(jià)格、購買鏈接和店鋪信息。
for li in ul_list:
# 名稱
title = li.xpath('div//p[@class="proName clearfix"]/a/@title')
# 鏈接
link = li.xpath('div//p[@class="proName clearfix"]/a/@href')
# 價(jià)格
price = li.xpath('div//p[@class="proPrice"]/em/@yhdprice')
# 店鋪
store = li.xpath('div//p[@class="storeName limit_width"]/a/@title')
最后將我們爬取到的結(jié)果,存入products_list中為最后的價(jià)格比較做準(zhǔn)備。
products_list.append({
'title': title[0],
'price': price[0],
'link': 'https:' + link[0],
'store': store[0],
'referer': '1號店'
})
二、爬取京東的商品數(shù)據(jù)
京東商品信息的爬取與1號店十分相似,需要注意的是京東網(wǎng)頁獲取html信息后,需要進(jìn)行utf-8編碼才能正常顯示頁面。
直接上源碼:
import requests
from lxml import html
def crawler(word, products_list=[]):
""" 爬取京東的商品數(shù)據(jù) """
url = 'https://search.jd.com/Search?keyword={0}&enc=utf-8'.format(word)
# 獲取HTML文檔
respons = requests.get(url)
respons.encoding = 'utf-8'
html_doc = respons.text
# 獲取xpath對象
selector = html.fromstring(html_doc)
# 找到列表的集合
ul_list = selector.xpath('//div[@id="J_goodsList"]/ul/li')
# 解析對應(yīng)的標(biāo)題,價(jià)格,鏈接,店鋪
for li in ul_list:
# 標(biāo)題
title = li.xpath('div/div[@class="p-name p-name-type-2"]/a/em/text() | '
'div/div[@class="p-name"]/a/@title')
# 購買鏈接
link = li.xpath('div/div[@class="p-name p-name-type-2"]/a/@href | '
'div/div[@class="p-name"]/a/@href')
# 價(jià)格
price = li.xpath('div/div[@class="p-price"]/strong/i/text() | '
'div/div[@class="p-price"]/strong/i/text()')
# 店鋪
store = li.xpath('div/div[@class="p-shop"]//a/text() | '
'div//a[@class="curr-shop"]/@title')
products_list.append({
'title': title[0],
'price': price[0],
'link': 'https:' + link[0],
'store': store[0],
'referer': '京東'
})
if __name__ == '__main__':
a = []
crawler('爬蟲', a)
三、爬取淘寶商品信息
淘寶商品信息的爬取就和前面兩者有很大不同了,這里我們無法用xpath尋找包含商品信息的節(jié)點(diǎn),查看網(wǎng)頁源代碼會(huì)發(fā)現(xiàn)根本就沒有包含商品信息的html標(biāo)簽。這主要是因?yàn)樘詫毷峭ㄟ^傳遞json數(shù)據(jù)來更新頁面數(shù)據(jù)的。
因此這里的url不是一個(gè)網(wǎng)址,而是一個(gè)api接口:
url = 'https://s.taobao.com/api?ajax=true&m=customized&sourceId=tb.index&q={0}'.format(word)
當(dāng)我們得到了淘寶傳遞的json數(shù)據(jù)后,后面的過程就很簡單了,在json中尋找目標(biāo)信息要比在html樹狀結(jié)構(gòu)中尋找方便多了。
源碼如下:
import requests
import urllib.parse
def crawler(word, products_list=[]):
""" 爬取淘寶網(wǎng)的商品數(shù)據(jù) """
word = urllib.parse.quote(word)
url = 'https://s.taobao.com/api?ajax=true&m=customized&sourceId=tb.index&q={0}'.format(word)
rest = requests.get(url).json()
pr_list = rest["API.CustomizedApi"]["itemlist"]["auctions"]
for bk in pr_list:
title = bk['raw_title']
price = bk['view_price']
link = bk['detail_url']
store = bk['nick']
products_list.append({
'title': title,
'price': price,
'link': 'https:' + link,
'store': store,
'referer': '淘寶'
})
if __name__ == '__main__':
a = []
crawler('python', a)
四、綜合比價(jià)
綜合比價(jià)需要我們導(dǎo)入前面三個(gè)爬蟲腳本,并按價(jià)格由低到高的排序得到最終結(jié)果。
from crawler_jd import crawler as jd
from crawler_yhd import crawler as yhd
from crawler_taobao import crawler as taobao
def main(word):
""" 比價(jià)工具整合 """
products_list = []
# 京東數(shù)據(jù)
print('京東網(wǎng)數(shù)據(jù)爬取完成')
jd(word, products_list)
# 1號店數(shù)據(jù)
print('1號店數(shù)據(jù)爬取完成')
yhd(word, products_list)
# 淘寶數(shù)據(jù)
print('淘寶網(wǎng)數(shù)據(jù)爬取完成')
taobao(word, products_list)
print('-------------------------開始排序---------------------------------')
# 排序書的數(shù)據(jù)
products_list = sorted(products_list, key=lambda item: float(item['price']), reverse=False)
for products in products_list:
print(products)
return products_list
if __name__ == '__main__':
word = input('請輸入商品名稱:')
main(word)
04 Flask
Flask提供了一組模塊,可以幫助我們構(gòu)建服務(wù)器端Web應(yīng)用,由于我們的爬蟲網(wǎng)站功能簡單,所以Flask這個(gè)輕量級的框架就夠了。
from flask import Flask, render_template, request
from crawler_product import main
app = Flask(__name__)
@app.route('/')
def entry_page() -> 'html':
return render_template('entry.html',
the_title='Welcome to PriceCompare!')
@app.route('/compare', methods=['POST'])
def search_products() -> str:
word = request.form['word']
title = '比價(jià)結(jié)果'
titles = ('商品', '價(jià)格', '鏈接', '店鋪', '來源')
results = main(word)
return render_template('results.html',
the_word=word,
the_title=title,
the_row_titles=titles,
the_data=results,)
app.run()
entry_page明確了Flask web應(yīng)用的初始頁面是entry.html,并向其中傳入了我們想要顯示的信息。
entry.html源碼:
{% extends 'base.html' %}
{% block body %}
<h2>{{ the_title }}</h2>
<form method='POST' action='/compare'>
<table>
<p>請輸入想要比價(jià)的商品:</p>
<tr><td>商品:</td><td><input name='word' type='TEXT' width='60'></td></tr>
</table>
<p>準(zhǔn)備好了,點(diǎn)擊這里:</p>
<p><input value='Do it!' type='SUBMIT'></p>
</form>
{% endblock %}
search_products接收了entry.html傳入的word參數(shù),并交由crawler_product進(jìn)行爬蟲,最后向results.html傳遞爬蟲結(jié)果。
results.html源碼:
{% extends 'base.html' %}
{% block body %}
<h2>{{ the_title }}</h2>
<p>你提交的商品名稱:</p>
<table>
<tr><td>關(guān)鍵字:</td><td>{{ the_word }}</td></tr>
</table>
<p>下面是 "{{ the_word }}" 的搜索比價(jià)結(jié)果:</p>
<table>
<tr>
{% for row_title in the_row_titles %}
<th>{{row_title}}</th>
{% endfor %}
</tr>
{% for products in the_data %}
<tr>
<td>{{products['title']}}</td>
<td>{{products['price']}}</td>
<td><a href={{products['link']}}>{{products['link']}}</a></td>
<td>{{products['store']}}</td>
<td>{{products['referer']}}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
到這里我們所有的代碼都已經(jīng)準(zhǔn)備完畢,我們可以看看整個(gè)項(xiàng)目的源碼結(jié)構(gòu):
crawler_jd.py、crawler_yhd.py、crawler_taobao.py分別為三個(gè)網(wǎng)頁的爬蟲腳本,通過crawler_product.py進(jìn)行綜合比較。
Flask_PriceCompaer.py是Flask Web應(yīng)用核心代碼,創(chuàng)建Flask對象并傳遞數(shù)據(jù)。
templates文件夾下的base.html是前端頁面的基模板,entry.html繼承了基模板負(fù)責(zé)網(wǎng)站進(jìn)入頁面的顯示,results.html與entry.html類似,負(fù)責(zé)網(wǎng)站結(jié)果頁面的顯示。
static文件夾下的hf.css就是普通的css文件,負(fù)責(zé)頁面的美化。
我們可以運(yùn)行Flas_PriceCompaer.py來看看網(wǎng)站的整體效果。
頁面入口:

查詢結(jié)果:

05 pythonanywhere
最后,只要10分鐘就可以把我們的Web應(yīng)用部署到云上,通過公網(wǎng)快捷地訪問Python爬蟲比價(jià)網(wǎng)站。
一、注冊Pythonanywhere
將網(wǎng)站源代碼打包壓縮,訪問pythonanywhere.com,并進(jìn)行注冊。
二、將文件上傳到云
三、解壓縮和安裝代碼
文件上傳后,點(diǎn)擊Open Bash console here,Pythonanywhere會(huì)彈出Linux控制臺(tái),我們執(zhí)行兩條命令:
unzip PriceCompaer.zip
mv PriceCompaer/* mysite/
將web應(yīng)用的代碼安裝到mysite文件夾。
四、創(chuàng)建一個(gè)初始Web應(yīng)用
點(diǎn)擊Add a new web app后,一路next,并選擇web framework為Flask以及相應(yīng)python版本。
五、配置Web應(yīng)用
接下來我們點(diǎn)擊下圖提示的位置:
修改from flask_app import app as application,將flask_app修改為我們自己的Flask Web應(yīng)用代碼,如Flask_PriceCompaer。同時(shí),我們也要查看Flask_PriceCompaer.py 文件,確保最后沒有app.run()。
六、運(yùn)行
配置完成后,就可以點(diǎn)擊那個(gè)綠色的Reload按鈕開始運(yùn)行了。
PS:要提醒的是,pythonanywhere免費(fèi)版只能訪問特定的網(wǎng)站,所以爬蟲程序無法運(yùn)行,想體檢完整結(jié)果請自行升級收費(fèi)版。
06 寫在最后
這篇文章主要是記錄自己的實(shí)現(xiàn)思路以及方法,其中的原理并沒有進(jìn)行詳細(xì)的闡述。雖然主要是因?yàn)槟菢訉懙脑捥哿?,但更重要的是,爬蟲、Flask和pythonanywhere網(wǎng)上都有大量的教程,在大神們的教程里浪費(fèi)時(shí)間才更有意義。