教隔壁小姐姐搭建Python爬虫比价网站

隔壁的小姐姐让我和她七夕呆一天,我想一天那么长,光聊天肯定很无聊,那么做点什么好呢?

想到这几天正好在学Python爬虫,于是我精心准备了搭建Python爬虫网站的教程,教小姐姐用Python,这样肯定不会无聊了O(∩_∩)O哈哈~

最终效果展示

01 基础知识

为了让小姐姐能听懂,我提前跟她说了要预习的知识。

微信图片

当然,小姐姐也是有一定python基础的,有了上面的准备,相信我们一定能做一个深入的学习讨论。

02 实现思路

这个项目主要由三个部分组成

  • 爬虫:爬取京东、1号店和淘宝的商品信息实现价格比较
  • Flask:运用Flask构建Web应用,显示爬虫结果
  • pythonanywhere:通过pythonanywhere将Web应用部署到云
实现思路

03 爬虫

第一步是用python爬虫脚本抓取网页上的商品信息,这里我们主要对京东、1号店和淘宝的商品信息进行爬取。不同的网页需要进行不同的分析,但方法都是大同小异的。我们先以1号店为例看看如何编写爬虫代码。

一、爬取1号店的商品数据

开始之前我们需要引入下面的python库:

import requests
from lxml import html
import urllib.parse

接下来,我们定义函数crawler:

def crawler(word, products_list=[]):

这里的word就是我们需要搜索商品名称,而products_list用来保存我们的爬取结果。

word = urllib.parse.quote(word)

当我们查询的商品名称含有中文时,是要对其进行编码处理的,urllib.parse.quote()函数可以帮助实现这一点。
当我们在1号店搜索商品名称时,其实是发起了一个url请求,这个url中包含了我们要查询的信息。因此我们需要将word参数加入到url中。

url = 'https://search.yhd.com/c0-0/k{0}'.format(word)

接下来的三步顺其自然,获取html源码,将html源码转换为xpath对象,然后在html树状结构中寻找包含商品信息的节点。

# 获取html源码
html_doc = requests.get(url).text

# xpath对象
selector = html.fromstring(html_doc)

# 商品列表
ul_list = selector.xpath('//div[@id="itemSearchList"]/div')

得到了当前页面的商品列表之后,我们需要对其进行遍历获取其中每个商品的名称、价格、购买链接和店铺信息。

    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')
        # 价格
        price = li.xpath('div//p[@class="proPrice"]/em/@yhdprice')
        # 店铺
        store = li.xpath('div//p[@class="storeName limit_width"]/a/@title')

最后将我们爬取到的结果,存入products_list中为最后的价格比较做准备。

products_list.append({
                'title': title[0],
                'price': price[0],
                'link': 'https:' + link[0],
                'store': store[0],
                'referer': '1号店'
            })

二、爬取京东的商品数据

京东商品信息的爬取与1号店十分相似,需要注意的是京东网页获取html信息后,需要进行utf-8编码才能正常显示页面。
直接上源码:

import requests
from lxml import html


def crawler(word, products_list=[]):
    """ 爬取京东的商品数据 """
    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')

    # 解析对应的标题,价格,链接,店铺
    for li in ul_list:

        # 标题
        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')

        # 价格
        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寻找包含商品信息的节点,查看网页源代码会发现根本就没有包含商品信息的html标签。这主要是因为淘宝是通过传递json数据来更新页面数据的。

因此这里的url不是一个网址,而是一个api接口:

url = 'https://s.taobao.com/api?ajax=true&m=customized&sourceId=tb.index&q={0}'.format(word)

当我们得到了淘宝传递的json数据后,后面的过程就很简单了,在json中寻找目标信息要比在html树状结构中寻找方便多了。

源码如下:

import requests
import urllib.parse


def crawler(word, products_list=[]):
    """ 爬取淘宝网的商品数据 """
    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)

四、综合比价

综合比价需要我们导入前面三个爬虫脚本,并按价格由低到高的排序得到最终结果。

from crawler_jd import  crawler as jd
from crawler_yhd import crawler as yhd
from crawler_taobao import crawler as taobao


def main(word):
    """ 比价工具整合 """
    products_list = []

    # 京东数据
    print('京东网数据爬取完成')
    jd(word, products_list)

    # 1号店数据
    print('1号店数据爬取完成')
    yhd(word, products_list)

    # 淘宝数据
    print('淘宝网数据爬取完成')
    taobao(word, products_list)

    print('-------------------------开始排序---------------------------------')

    # 排序书的数据
    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提供了一组模块,可以帮助我们构建服务器端Web应用,由于我们的爬虫网站功能简单,所以Flask这个轻量级的框架就够了。

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 = '比价结果'
    titles = ('商品', '价格', '链接', '店铺', '来源')
    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应用的初始页面是entry.html,并向其中传入了我们想要显示的信息。
entry.html源码:

{% extends 'base.html' %}

{% block body %}

<h2>{{ the_title }}</h2>

<form method='POST' action='/compare'>
<table>
<p>请输入想要比价的商品:</p>
<tr><td>商品:</td><td><input name='word' type='TEXT' width='60'></td></tr>
</table>
<p>准备好了,点击这里:</p>
<p><input value='Do it!' type='SUBMIT'></p>
</form>

{% endblock %}

search_products接收了entry.html传入的word参数,并交由crawler_product进行爬虫,最后向results.html传递爬虫结果。
results.html源码:

{% extends 'base.html' %}

{% block body %}

<h2>{{ the_title }}</h2>

<p>你提交的商品名称:</p>
<table>
<tr><td>关键字:</td><td>{{ the_word }}</td></tr>
</table>

<p>下面是 "{{ the_word }}" 的搜索比价结果:</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 %}

到这里我们所有的代码都已经准备完毕,我们可以看看整个项目的源码结构:

项目结构

crawler_jd.pycrawler_yhd.pycrawler_taobao.py分别为三个网页的爬虫脚本,通过crawler_product.py进行综合比较。

Flask_PriceCompaer.py是Flask Web应用核心代码,创建Flask对象并传递数据。

templates文件夹下的base.html是前端页面的基模板,entry.html继承了基模板负责网站进入页面的显示,results.htmlentry.html类似,负责网站结果页面的显示。
static文件夹下的hf.css就是普通的css文件,负责页面的美化。

我们可以运行Flas_PriceCompaer.py来看看网站的整体效果。

页面入口:

entry

查询结果:

results

源码下载

05 pythonanywhere

最后,只要10分钟就可以把我们的Web应用部署到云上,通过公网快捷地访问Python爬虫比价网站。

一、注册Pythonanywhere

将网站源代码打包压缩,访问pythonanywhere.com,并进行注册。

二、将文件上传到云

文件上传

三、解压缩和安装代码

文件上传后,点击Open Bash console here,Pythonanywhere会弹出Linux控制台,我们执行两条命令:
unzip PriceCompaer.zip
mv PriceCompaer/* mysite/
将web应用的代码安装到mysite文件夹。

四、创建一个初始Web应用

创建Web应用

点击Add a new web app后,一路next,并选择web framework为Flask以及相应python版本。

五、配置Web应用

接下来我们点击下图提示的位置:

5.PNG

修改from flask_app import app as application,将flask_app修改为我们自己的Flask Web应用代码,如Flask_PriceCompaer。同时,我们也要查看Flask_PriceCompaer.py 文件,确保最后没有app.run()

六、运行

配置完成后,就可以点击那个绿色的Reload按钮开始运行了。

PS:要提醒的是,pythonanywhere免费版只能访问特定的网站,所以爬虫程序无法运行,想体检完整结果请自行升级收费版。

06 写在最后

这篇文章主要是记录自己的实现思路以及方法,其中的原理并没有进行详细的阐述。虽然主要是因为那样写的话太累了,但更重要的是,爬虫、Flask和pythonanywhere网上都有大量的教程,在大神们的教程里浪费时间才更有意义。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容

  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    aimaile阅读 26,478评论 6 427
  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    小迈克阅读 2,984评论 1 3
  • Python资源大全中文版,包括:Web框架、网络爬虫、模板引擎、数据库、数据可视化、图片处理等,由伯乐在线持续更...
    dxl1236阅读 4,655评论 2 33
  • 英文原版:https://github.com/vinta/awesome-python中文版:https://g...
    会灰的大飞狼阅读 3,604评论 1 56
  • 我总是怀揣着这样的认知:很多时候,哭和笑一样,都只是一瞬悲和喜的心境的释放。 直到有人一边笑着看我哭一边问我:“你...
    窝书阅读 256评论 1 5