1. Python爬蟲+Flask,帶你創(chuàng)建車標(biāo)學(xué)習(xí)網(wǎng)站

        共 7787字,需瀏覽 16分鐘

         ·

        2020-09-26 02:37


        文化不分邊界

        人,為什么要讀書?舉個(gè)例子:
        當(dāng)看到天邊飛鳥,你會說:“落霞與孤鶩齊飛,秋水共長天一色?!倍皇牵骸芭P靠,好多鳥?!?
        當(dāng)你失戀時(shí)你低吟淺唱道:“人生若只如初見,何事秋風(fēng)悲畫扇?!倍皇乔f遍地悲喊:“藍(lán)瘦,香菇!”

        別人看車關(guān)注牌子,我看車關(guān)注寬敞不,睡著舒服不?可不管怎樣不能在人前丟份啊,所以我決定學(xué)習(xí)學(xué)習(xí)車標(biāo)!首先我們爬取車標(biāo)及其相關(guān)信息,然后通過Flask來做一個(gè)車標(biāo)學(xué)習(xí)網(wǎng)站。

        先來看看實(shí)現(xiàn)效果:


        車標(biāo)網(wǎng)數(shù)據(jù)爬蟲


        在網(wǎng)上找了半天車標(biāo)的數(shù)據(jù),最后看到了這個(gè)網(wǎng)站:

        車標(biāo)網(wǎng)? http://www.chebiaow.com/logo。


        網(wǎng)站將車系按照字母從A-Z進(jìn)行了排序,然后點(diǎn)擊每個(gè)車標(biāo)進(jìn)入詳細(xì)信息,那Audi做例子:


        有用的數(shù)據(jù)是哪些?品牌名稱、車標(biāo)圖片、成立時(shí)間、主要車型、官網(wǎng)。
        那么讓我們開始通過爬蟲,獲取車標(biāo)網(wǎng)下所有的汽車品牌及車標(biāo),最終入庫保存吧,開始!


        數(shù)據(jù)庫操作指南


        針對簡單的數(shù)據(jù),我習(xí)慣用python自帶的sqlite3進(jìn)行數(shù)據(jù)庫的存儲,簡單方便….那么如何管理我們的數(shù)據(jù)庫呢?推薦使用DBUtils!

        安裝:pip install DBUtils

        DBUtils is a suite of tools providing solid, persistent and pooled connections to a database that can be used in all kinds of multi-threaded environments like Webware for Python or other web application servers. The suite supports DB-API 2 compliant database interfaces and the classic PyGreSQL interface.

        簡而言之,DBUtils是一套為數(shù)據(jù)庫提供可靠,持久和池式連接的工具,可用于各種多線程環(huán)境。我們一般使用DBUtils.PooledDB來創(chuàng)建一批連接池進(jìn)行并發(fā)處理。常用參數(shù)如下:

        參數(shù)說明
        creator使用鏈接數(shù)據(jù)庫的模塊(sqllite3、pymysql…)
        maxconnections連接池允許的最大連接數(shù),0和None表示不限制連接數(shù)
        mincached初始化時(shí),鏈接池中至少創(chuàng)建的空閑的鏈接,0表示不創(chuàng)建
        maxcached鏈接池中最多閑置的鏈接,0和None不限制
        blocking連接池中如果沒有可用連接后,是否阻塞等待。True,等待;False,不等待然后報(bào)錯
        maxusage一個(gè)鏈接最多被重復(fù)使用的次數(shù),None表示無限制
        hostip
        user用戶名
        password密碼
        database數(shù)據(jù)庫名
        charset字符集(Mysql用的比較多,SQLite沒有)

        因?yàn)橹岸际悄肈BUtils鏈接Mysql數(shù)據(jù)庫的,這次默認(rèn)就直接改成sqlite3,結(jié)果簡單配置下,封裝上常用的方法…一跑程序掛了!Why?
        SQLite本身無法應(yīng)對多個(gè)線程并發(fā)訪問,由一個(gè)線程創(chuàng)建并訪問的sqlite的數(shù)據(jù)庫,無法允許另外一個(gè)線程進(jìn)行訪問,找解決辦法唄,最終找到通過設(shè)置check_same_thread=False,使SQLite支持多線程并發(fā)(但并發(fā)的效果很一般)。

        #?-*-?coding:?utf-8?-*-
        #?@Author???:?王翔
        #?@微信號???:?King_Uranus
        #?@公眾號????:?清風(fēng)Python
        #?@GitHub???:?https://github.com/BreezePython
        #?@Date?????:?2019/12/15?20:27
        #?@Software?:?PyCharm
        #?@version ?:Python 3.7.3
        #?@File?????:?db_maker.py


        import?sqlite3
        from?DBUtils.PooledDB?import?PooledDB


        class?DB_Maker:
        ????def?__init__(self):
        ????????self.POOL?=?PooledDB(
        ????????????check_same_thread=False,
        ????????????creator=sqlite3,??#?使用鏈接數(shù)據(jù)庫的模塊
        ????????????maxconnections=10,??
        ????????????mincached=2,??
        ????????????maxcached=5,??
        ????????????blocking=True,??
        ????????????maxusage=None,??
        ????????????ping=0,
        ????????????database='database.db',
        ????????)
        ????????self.check_db()

        ????def?check_db(self):
        ????????sql?=?"SELECT?name?FROM?sqlite_master?where?name=?"
        ????????if?not?self.fetch_one(sql,?('idiom',)):
        ????????????self.create_table()

        ????def?create_table(self):
        ????????print("create?table?...")
        ????????sql?=?"""create?table?idiom?(
        ????????????????????????[id]????????????integer?PRIMARY?KEY?autoincrement,
        ????????????????????????[name]?????????varchar?(10),
        ????????????????????????[speak]??????varchar?(30),
        ????????????????????????[meaning]??????varchar?(100),
        ????????????????????????[source]??????varchar?(100),
        ????????????????????????[example]??????varchar?(100),
        ????????????????????????[hot]??????int(10)
        ????????????????????)"""

        ????????self.fetch_one(sql)

        ????def?db_conn(self):
        ????????conn?=?self.POOL.connection()
        ????????cursor?=?conn.cursor()
        ????????return?conn,?cursor

        ????@staticmethod
        ????def?db_close(conn,?cursor):
        ????????cursor.close()
        ????????conn.close()

        ????def?fetch_one(self,?sql,?args=None):
        ????????conn,?cursor?=?self.db_conn
        ????????if?not?args:
        ????????????cursor.execute(sql)
        ????????else:
        ????????????cursor.execute(sql,?args)
        ????????record?=?cursor.fetchone()
        ????????self.db_close(conn,?cursor)
        ????????return?record

        ????def?fetch_all(self,?sql,?args):
        ????????conn,?cursor?=?self.db_conn
        ????????cursor.execute(sql,?args)
        ????????record_list?=?cursor.fetchall()
        ????????self.db_close(conn,?cursor)
        ????????return?record_list

        ????def?insert(self,?sql,?args):
        ????????conn,?cursor?=?self.db_conn
        ????????row?=?cursor.execute(sql,?args)
        ????????conn.commit()
        ????????self.db_close(conn,?cursor)

        本次有一個(gè)知識點(diǎn),我們需要將車標(biāo)圖片,存儲在數(shù)據(jù)庫中,那么如何在數(shù)據(jù)庫中存儲圖片,使用類型BLOB。舉一個(gè)簡單的數(shù)據(jù)庫圖片讀寫例子

        #?-*-?coding:?utf-8?-*-
        #?@Author???:?王翔
        #?@微信號???:?King_Uranus
        #?@公眾號????:?清風(fēng)Python
        #?@GitHub???:?https://github.com/BreezePython
        #?@Date?????:?2019/12/15?20:27
        #?@Software?:?PyCharm
        #?@version ?:Python 3.7.3
        #?@File?????:?show.py

        import?sqlite3

        db?=?sqlite3.connect('Car.db')
        cur?=?db.cursor()
        cur.execute("CREATE?TABLE?if?not?exists?image_save?(image?BLOB);")

        with?open('Audi.jpg',?'rb')?as?f:
        ????cur.execute("insert?into?image_save?values(?)",?(sqlite3.Binary(f.read()),))
        ????db.commit()

        cur.execute('select?image?from?image_save?limit?1')
        b?=?cur.fetchone()[0]

        with?open('1.jpg',?'wb')?as?f:
        ????f.write(b)

        我們創(chuàng)建一個(gè)image_save的測試表,然后將圖片讀取為二進(jìn)制字節(jié)的方式,通過sqlite3.Binary將二進(jìn)制文件存儲至數(shù)據(jù)庫。
        那么同樣的,我們將BLOB類型的圖片讀取出來后,進(jìn)行寫入,即可達(dá)到效果,來看看這個(gè)1.jpg是否正常:


        圖片下載小技巧

        看過了二進(jìn)制的存儲方式,大家肯定說明白了,網(wǎng)站獲取到圖片鏈接然后找著上面的例子下載到本地,然后再進(jìn)行二進(jìn)制的讀取后存儲數(shù)據(jù)庫即可,對嗎?不對…有什么問題呢?來看一個(gè)例子:


        這里Audi圖片的鏈接地址,我們通過requests來下載看看….

        import?requests
        r?=requests.get('http://img.chebiaow.com/thumb/cb/allimg/1303/1-1303061Z600520,c_fill,h_138,w_160.jpg')
        r.content
        b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01...'

        可以看到我們通過requests.get獲取到的content就已經(jīng)是二進(jìn)制數(shù)據(jù)了,為何還要存儲成圖片,在轉(zhuǎn)化呢?省去了我們保存圖片的多余過程。

        網(wǎng)頁分析

        針對A-Z的車標(biāo)排序,網(wǎng)站的url匹配關(guān)系很簡單:

        from?string?import?ascii_uppercase?as?au
        #?ascii_uppercase代表A-Z,當(dāng)然你可以不引入模塊自己生成也OK...
        for?uppercase?in?au:
        ????"http://www.chebiaow.com/logo/{}.html".format(au)

        可以看到在包含cb-list方法的ul下匹配所有l(wèi)i中的第一個(gè)a標(biāo)簽,然后拼接base_url即可。

        進(jìn)入品牌詳情界面后,我們針對左右欄目的設(shè)置,分別獲取所需標(biāo)紅的內(nèi)容

        最終存儲的數(shù)據(jù)庫如下:


        由于圖片是BLOB類型的二進(jìn)制文件,所以大家看到的是星星,最終獲取網(wǎng)站258份車輛信息(雖然我能認(rèn)識的不到20種…)
        這個(gè)中興看了半天還以為是搞錯了,沒想到是同名的…


        萬法同源

        一直覺得可能自己不太適合搞技術(shù),更適合在天橋底下支個(gè)攤子說書。技術(shù)的東西從來沒人關(guān)注,扯東扯西的文章莫名的火。之前的一篇文章MarkDown添加圖片的三種方式不管是在技術(shù)為主的CSDN還是娛樂為主的簡書,都莫名的火爆,看圖:


        其實(shí)文章沒什么含量,就是介紹了下markdown添加圖片的方式,唯一新奇的可能就是使用了base64的圖片二進(jìn)制轉(zhuǎn)化。
        ![avatar]\(data:image/png;base64,iVBORw0......)

        1. 使用python將圖片轉(zhuǎn)化為base64字符串

        import?base64
        f=open('723.png','rb')?#二進(jìn)制方式打開圖文件
        ls_f=base64.b64encode(f.read())?#讀取文件內(nèi)容,轉(zhuǎn)換為base64編碼
        f.close()
        print(ls_f)
        1. base64字符串轉(zhuǎn)化為圖片

        import?base64
        bs='iVBORw0KGgoAAAANSUhEUg....'?#?太長了省略
        imgdata=base64.b64decode(bs)
        file=open('2.jpg','wb')
        file.write(imgdata)
        file.close()

        通過request.get(url).content獲取的二進(jìn)制字符串,直接存儲至SQLite數(shù)據(jù)庫的BLOB字段中。如果我們需要顯示圖片,直接通過open函數(shù)的寫入數(shù)據(jù)即可生成原始的圖片。但是,如果我不想寫入圖片,而希望直接展示在web界面上呢?也可以通過markdown添加圖片的方式,使用base64的編碼來實(shí)現(xiàn)!

        Flask展示圖片例子


        我們先不通過讀取數(shù)據(jù)庫,而是直接獲取requests.get(url).content的方式測試Flask的圖片展示。
        HTML代碼:


        <html?lang="en">
        <head>
        ????<meta?charset="UTF-8">
        ????<title>Titletitle>
        head>
        <body>
        <img?src="data:;base64,{{?img?}}">
        body>
        html>

        Flask后臺代碼:

        from?flask?import?Flask,?render_template
        import?base64
        import?requests

        app?=?Flask(__name__)


        @app.route("/show")
        def?show_image():
        ????r?=?requests.get('http://img.chebiaow.com/thumb/cb/allimg/1303/1-1303061Z600520,c_fill,h_138,w_160.jpg')
        ????image?=?base64.b64encode(r.content).decode('ascii')
        ????return?render_template('index.html',?img=image)


        if?__name__?==?'__main__':
        ????app.run()


        圖片展示OK,使用這種方式,我們就沒必要將圖片文件先從數(shù)據(jù)庫中讀取生成后,再通過url_for('static',filename='x.png')的方式進(jìn)行顯示了。

        完善車標(biāo)app


        我們就把這些數(shù)據(jù)庫信息配合Flask完成一個(gè)簡單的車標(biāo)學(xué)習(xí)簡單網(wǎng)站吧,來看看實(shí)現(xiàn)效果:


        后臺Flask代碼:

        #?-*-?coding:?utf-8?-*-
        #?@Author???:?王翔
        #?@JianShu??:?清風(fēng)Python
        #?@Date?????:?2019/7/25?1:37
        #?@Software?:?PyCharm
        #?@version ?:Python 3.7.3
        #?@File?????:?app.py

        from?flask?import?Flask,?render_template,?g
        import?sqlite3
        import?random
        import?base64

        app?=?Flask(__name__)
        DATABASE?=?'static/db/car.db'
        app.secret_key?=?'Breeze?Python'


        def?connect_db():
        ????return?sqlite3.connect(DATABASE)


        @app.before_request
        def?before_request():
        ????g.db?=?connect_db()


        @app.teardown_request
        def?teardown_request(exception):
        ????if?hasattr(g,?'db'):
        ????????g.db.close()


        def?query_db(query,?args=()):
        ????cur?=?g.db.execute(query,?args)
        ????rv?=?[dict((cur.description[idx][0],?value)
        ???????????????for?idx,?value?in?enumerate(row))?for?row?in?cur.fetchall()]
        ????if?not?query.startswith('select'):
        ????????g.db.commit()
        ????return?rv[0]?if?rv?else?None


        @app.route('/car')
        @app.route('/')
        def?index():
        ????id?=?random.randint(1,?141)
        ????car_info?=?query_db('select?name,image,founded,models,website?from?car_logo?where?id={}'.format(id))
        ????car_info['image']?=?base64.b64encode(car_info['image']).decode('ascii')
        ????print(car_info)
        ????return?render_template('index.html',?car=car_info)


        if?__name__?==?'__main__':
        ????app.run(host='0.0.0.0',?port=7000)

        前臺HTML代碼就不再這里展示了…
        公眾號回復(fù)車標(biāo),下載整套爬蟲與Flask代碼及數(shù)據(jù)庫信息。


        推薦閱讀


        別找了,這是 Pandas 最詳細(xì)教程了

        用Python爬取了全國近5000家旅游景點(diǎn),分析國慶去哪玩



        THANKS

        - End -



        點(diǎn)個(gè)“在看”必升職加薪喔!
        瀏覽 43
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 天堂a√8蜜桃 | 亚洲精品一区二区口爆 | 日韩午夜无码中文字幕A片评价 | 成人乱人乱一区二区三区一级视频 | 做爱在线观看免费观看高清 |