1. 程式人生 > 其它 >爬蟲入門教程⑩— 用漂亮的圖表展示爬取到的資料

爬蟲入門教程⑩— 用漂亮的圖表展示爬取到的資料

經過了前面的努力,我們成功獲取到了資料,並且學會了儲存,但是隻是用網頁展示出來,是不是有一些不夠美觀呢?

所以本節的內容是:資料的視覺化。拿到了資料卻不能使其簡單易懂並且足夠突出,那就是不是好的資料工程師。
作者:終可見丶
連結:https://www.jianshu.com/p/47908cd4f424
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
  • 本節需要做的準備

安裝pyecharts這個Python的圖表庫:在之前我們安裝了requests、lxml、bs4。所以只需要再在cmd裡面 pip3 install pyecharts==0.5.6 就OK啦,如果失敗,請仔細閱讀教程:

爬蟲入門教程⑥—安裝爬蟲常用工具包.

  ps:由於pyecharts升級到1.x版本,發生了較大的變化,所以本教程安裝時候指定了版本為0.5.6,否則程式碼會報錯。
如果報錯ImportError: cannot import name 'Page' from 'pyecharts'那就是沒有加版本限制導致安裝了最新版的pyecharts,需要先執行pip3 uninstall pyecharts,按y確認之後,再次執行 pip3 install pyecharts==0.5.6
後續會繼續更新教程的。


  • pyecharts簡介

這是百度echarts圖表庫,使用Python介面進行生成圖表的一個庫,非常炫酷。在之前繪圖基本上是用的【Matplotlib】這個庫,這個庫功能非常強大,但是缺點也比較明顯,api呼叫比較複雜,新手上手很慢也很難。於是在去年,

陳鍵冬大佬推出了一個簡單易用的繪相簿 pyecharts

我當時懷著試一試的心情使用了一下,哇,超好用的,對新手超友好的,程式碼和圖都寫出來了,非常詳細,同時配置項也非常清晰。一口氣畫5個圖都超快超簡單的~!

  • 確定視覺化的目標

這是很重要的一步,先確認哪些資料值得拿來視覺化,然後再去編寫程式碼。一部電影的資訊有:名字、上映日期、地區、型別、關注者數量。最明顯的當然是關注者數量排行榜(柱狀圖),除此之外我還想了幾個:
上映電影型別佔比(餅圖)
上映地區佔比(餅圖)
上映日期柱狀圖

  • 採集所有電影資訊

先上之前的程式碼:

import requests
from
bs4 import BeautifulSoup # 從bs4引入BeautifulSoup #請求網頁 # 舊版教程 # url = "https://movie.douban.com/cinema/later/chengdu/" # response = requests.get(url) # 2019-12-23更新,解決不能獲取到響應的問題 url = "https://movie.douban.com/cinema/later/chengdu/" # URL不變 # 新增偽裝成Chrome瀏覽器的header fake_headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36' } response = requests.get(url, headers=fake_headers) # 請求引數裡面把假的請求header加上 soup = BeautifulSoup(response.content.decode('utf-8'), 'lxml') all_movies = soup.find('div', id="showing-soon") # 先找到最大的div for each_movie in all_movies.find_all('div', class_="item"): # 從最大的div裡面找到影片的div # print(each_movie) # 輸出每個影片div的內容 all_a_tag = each_movie.find_all('a') all_li_tag = each_movie.find_all('li') movie_name = all_a_tag[1].text moive_href = all_a_tag[1]['href'] movie_date = all_li_tag[0].text movie_type = all_li_tag[1].text movie_area = all_li_tag[2].text movie_lovers = all_li_tag[3].text print('名字:{},連結:{},日期:{},型別:{},地區:{}, 關注者:{}'.format( movie_name, moive_href, movie_date, movie_type, movie_area, movie_lovers))

這是資料的基礎資訊,我們先全部拿到,然後放進一個list,方便後續的比較分析處理。同時我們在程式碼頂部,從pyecharts引入Page(在一張圖顯示多個圖表)、Pie(餅圖)、Bar(柱狀圖)。

# 視覺化爬取結果
import requests
from bs4 import BeautifulSoup  # 從bs4引入BeautifulSoup
from pyecharts import Page, Pie, Bar  # 引入繪圖需要的模組

#請求網頁
# 舊版教程
# url = "https://movie.douban.com/cinema/later/chengdu/"
# response = requests.get(url)

# 2019-12-23更新,解決不能獲取到響應的問題
url = "https://movie.douban.com/cinema/later/chengdu/"  # URL不變
# 新增偽裝成Chrome瀏覽器的header
fake_headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36'
}
response = requests.get(url, headers=fake_headers)  # 請求引數裡面把假的請求header加上

soup = BeautifulSoup(response.content.decode('utf-8'), 'lxml')

all_movies = soup.find('div', id="showing-soon")  # 先找到最大的div

# 先把所有的資料存到這個list裡面
all_movies_info = []
for each_movie in all_movies.find_all('div', class_="item"):  # 從最大的div裡面找到影片的div
    # print(each_movie)  # 輸出每個影片div的內容
    all_a_tag = each_movie.find_all('a')
    all_li_tag = each_movie.find_all('li')
    movie_name = all_a_tag[1].text
    moive_href = all_a_tag[1]['href']
    movie_date = all_li_tag[0].text
    movie_type = all_li_tag[1].text
    movie_area = all_li_tag[2].text
    movie_lovers = all_li_tag[3].text.replace('人想看', '') #  去掉除了數字之外的字
    # 把電影資料新增到list
    all_movies_info.append({'name': movie_name, 'date': movie_date, 'type': movie_type, 
                            'area': movie_area, 'lovers': movie_lovers})
    # print('名字:{},日期:{},型別:{},地區:{}, 關注者:{}'.format(
        # movie_name, movie_date, movie_type, movie_area, movie_lovers))
print(all_movies_info)  # 輸出一下檢查資料是否傳遞成功
  • 繪製關注者排行榜

處理邏輯:首先把所有的電影以關注者數量排個序,然後從所有電影裡面以獲取到電影的名字和電影的關注者數量,最後新增到柱狀圖裡。
sorted函式,第一個引數接受一個可以遍歷的物件,key引數接受一個匿名函式,用以指定以遍歷物件內的哪個元素作為排序的依據
以下程式碼新增到上一個示例程式碼後面即可。

# 繪製關注者排行榜圖

# i['name'] for i in all_movies_info 這個是Python的快捷方式,
# 這一句的作用是從all_movies_info這個list裡面依次取出每個元素,
# 並且取出這個元素的 name 屬性
sort_by_lovers = sorted(all_movies_info, key=lambda x: int(x['lovers']))
all_names = [i['name'] for i in sort_by_lovers]
all_lovers = [i['lovers'] for i in sort_by_lovers]

lovers_rank_bar = Bar('電影關注者排行榜')  # 初始化圖表,給個名字
# all_names是所有電影名,作為X軸, all_lovers是關注者的數量,作為Y軸。二者資料一一對應。
# is_convert=True設定x、y軸對調,。is_label_show=True 顯示y軸值。 label_pos='right' Y軸值顯示在右邊
lovers_rank_bar.add('', all_names, all_lovers, is_convert=True, is_label_show=True, label_pos='right')
lovers_rank_bar  # jupyter下直接顯示圖表在輸出框內

  • 繪製電影型別佔比圖

我們先從所有電影裡獲取所有的電影型別(一個電影可能有多個型別,比如動畫 / 奇幻 / 冒險,就需要先分割成3個);然後通過程式碼統計這些型別的數量,最後繪製成餅圖。程式碼同樣新增到之前的程式碼之後就OK。

# 繪製電影型別佔比圖
all_types = [i['type'] for i in all_movies_info]
type_count = {}
for each_types in all_types:
    # 把 愛情 / 奇幻 這種分成[愛情, 奇幻]
    type_list = each_types.split(' / ')
    for e_type in type_list:
        if e_type not in type_count:
            type_count[e_type] = 1
        else:
            type_count[e_type] += 1
# print(type_count) # 檢測是否資料歸類成功

type_pie = Pie('上映型別佔比', title_top=20)  # 因為型別過多影響標題,所以標題向下移20px
# 直接取出統計的型別名和數量並強制轉換為list。
type_pie.add('', list(type_count.keys()), list(type_count.values()), is_label_show=True)
type_pie  # jupyter下直接顯示

  • 繪製上映日期圖

類似於上一個步驟,我們同樣拿到所有的上映日期並做一個數量統計再新增到條形圖就OK了。程式碼同樣也是新增到上面的程式碼之後就OK了。

# 繪製電影上映日期柱狀圖
all_dates = [i['date'] for i in all_movies_info]
dates_count = {}
for date in all_dates:
    if date not in dates_count:
        dates_count[date] = 1
    else:
        dates_count[date] += 1
# print(dates_count)  # 輸出驗證資料是否正確

dates_bar = Bar('上映日期佔比')
dates_bar.add('',list(dates_count.keys()), list(dates_count.values()), is_label_show=True)
dates_bar  # jupyter下直接顯示

完整程式碼

要把所有的圖表都新增到一起一次性輸出,那麼就需要使用Page這個類,把圖表加進去,這些圖表就會按照新增順序,挨個展示。完整程式碼如下:
等下,遇到了問題,執行報錯了(獲取上映日期程式碼報錯list out of range:也就是我們要第四個元素,但是它只有3個元素,所以超過了range),發現是過了12點,豆瓣更新了2部電影,這兩部電影,沒有上映日期~!所以更改了一點程式碼以增強相容性。爬蟲就是這樣,網頁結構變化了,程式碼就要修改。所以這個工作還是可以做得比較久的。

# 視覺化爬取結果
import requests
from bs4 import BeautifulSoup  # 從bs4引入BeautifulSoup
from pyecharts import Page, Pie, Bar

#請求網頁
# 舊版教程
# url = "https://movie.douban.com/cinema/later/chengdu/"
# response = requests.get(url)

# 2019-12-23更新,解決不能獲取到響應的問題
url = "https://movie.douban.com/cinema/later/chengdu/"  # URL不變
# 新增偽裝成Chrome瀏覽器的header
fake_headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36'
}
response = requests.get(url, headers=fake_headers)  # 請求引數裡面把假的請求header加上

soup = BeautifulSoup(response.content.decode('utf-8'), 'lxml')

all_movies = soup.find('div', id="showing-soon")  # 先找到最大的div

all_movies_info = []
for each_movie in all_movies.find_all('div', class_="item"):  # 從最大的div裡面找到影片的div
    # print(each_movie)  # 輸出每個影片div的內容
    all_a_tag = each_movie.find_all('a')
    all_li_tag = each_movie.find_all('li')
    movie_name = all_a_tag[1].text
    moive_href = all_a_tag[1]['href']
    # 執行報錯 index out of range:是因為有電影沒顯示日期
    if len(all_li_tag) == 4:
        movie_date = all_li_tag[0].text
        movie_type = all_li_tag[1].text
        movie_area = all_li_tag[2].text
        movie_lovers = all_li_tag[3].text.replace('人想看', '')
    else:  # 網站結構改變,跟著改變程式碼
        movie_date = "未知"
        movie_type = all_li_tag[0].text
        movie_area = all_li_tag[1].text
        movie_lovers = all_li_tag[2].text.replace('人想看', '')
    all_movies_info.append({'name': movie_name, 'date': movie_date, 'type': movie_type, 
                            'area': movie_area, 'lovers': movie_lovers})
    # print('名字:{},日期:{},型別:{},地區:{}, 關注者:{}'.format(
        # movie_name, movie_date, movie_type, movie_area, movie_lovers))
# print(all_movies_info)  # 輸出一下檢查資料是否傳遞成功

page = Page() # 同一個網頁顯示多個圖

# 繪製關注者排行榜圖

# i['name'] for i in all_movies_info 這個是Python的快捷方式
# 這一句的作用是從all_movies_info這個list裡面依次取出每個元素,
# 並且取出這個元素的 name 屬性
sort_by_lovers = sorted(all_movies_info, key=lambda x: int(x['lovers']))
all_names = [i['name'] for i in sort_by_lovers]
all_lovers = [i['lovers'] for i in sort_by_lovers]
lovers_rank_bar = Bar('電影關注者排行榜')
lovers_rank_bar.add('', all_names, all_lovers, is_convert=True, is_label_show=True, label_pos='right')
page.add(lovers_rank_bar)

# lovers_rank_bar

# 繪製電影型別佔比圖
all_types = [i['type'] for i in all_movies_info]
type_count = {}
for each_types in all_types:
    # 把 愛情 / 奇幻 這種分成[愛情, 奇幻]
    type_list = each_types.split(' / ')
    for e_type in type_list:
        if e_type not in type_count:
            type_count[e_type] = 1
        else:
            type_count[e_type] += 1
# print(type_count) # 檢測是否資料歸類成功

type_pie = Pie('上映型別佔比', title_top=20)
type_pie.add('', list(type_count.keys()), list(type_count.values()), is_label_show=True)
# type_pie

page.add(type_pie)

# 繪製電影上映日期柱狀圖
all_dates = [i['date'] for i in all_movies_info]
dates_count = {}
for date in all_dates:
    if date not in dates_count:
        dates_count[date] = 1
    else:
        dates_count[date] += 1
# print(dates_count)  # 輸出驗證資料是否正確

dates_bar = Bar('上映日期佔比')
dates_bar.add('',list(dates_count.keys()), list(dates_count.values()), is_label_show=True)
# dates_bar

page.add(dates_bar)

page  # jupyter下自動顯示
簡單的分析
  • 關注者排行榜圖裡,大雄的金銀島,6.1上映,關注人數4700,當之無愧的最火最期待的電影,畢竟是這麼多快到中年的年輕人小時候很喜歡的動漫,並且也幾乎從來沒讓人失望過。
  • 上映電影型別圖裡,最多的是劇情類,不過這個標籤比較平常,所以略過。剩下最多的就是冒險和動漫了,這個也和最近的日子有關係,畢竟明天就兒童節了,所以大部分電影和兒童、少年沾邊了。
  • 上映日期也表明了,大部分電影在6.1和6.8上映。6.1兒童節,大家容易理解,6.8呢?6.8是高考結束的那天啊~!!!廣大高三學子終於解放了~!!!解放了肯定就要看看電影啊~作為一個即將畢業的老人,提前恭祝廣大學子高考金榜題名!

後記

爬蟲入門教程就到此為止了,非常高興能夠和大家一起分享知識。一起學習Python爬蟲,把這門實用又有趣的技術,通過簡單的程式碼,向各位展示出來。希望各位能從這個小小的爬蟲教程中,獲取到一點程式設計的樂趣,也瞭解一下為什麼Life is short, I use Python是Python的slogan。
所以我真的太喜歡Python了。本著分享技術的精神,向各位從爬蟲的概念,基礎知識,編碼,資料提取,資料視覺化,簡單資料分析 向大家介紹了Python爬蟲方面的簡單知識。如果這幾篇文章能夠讓你對程式碼,對爬蟲,對Python產生一點積極的作用,那我會覺得非常開心!

Python的禪宗三字經——蒂姆•彼得斯
優美勝於醜陋,
明瞭勝於晦澀,
簡潔勝於複雜,
複雜勝於凌亂,
間隔勝於緊湊,
可讀性很重要,
即便假借特例的實用性之名,也不可違背這些規則,
不要包容所有錯誤,除非你確定需要這樣做,
當存在多種可能,不要嘗試去猜測 ,
而是儘量找一種,最好是唯一一種明顯的解決方案,
雖然這並不容易,因為你不是 Python 之父。
做也許好過不做,但不假思索就動手還不如不做,
如果你無法向人描述你的方案,那肯定不是一個好方案;
反之亦然,名稱空間是一種絕妙的理念,我們應當多加利用

是的,在Python任何版本,程式碼裡面輸入import this,就會出現Python的格言。意在勸告人們寫Python要遵循的規則,也就是寫程式碼要pythonic~!

In [1]: import this
"""
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
"""