Python + SeaTable | 用 Python 從維基百科抓取冬奧會城市資訊並製作地圖

SeaTable開發者版發表於2021-12-17

用 Python 從維基百科、百度百科等網站抓取公開的資料,並儲存到表格中,這不是什麼難事兒。但在很多應用場景中,我們不再侷限於把抓取的資料儲存到表格,還需要更直觀的去視覺化。比如在本案例中,用 Python 從維基百科抓取往屆舉辦冬奧會的城市,然後製作出地圖、相簿,甚至進行靈活的共享協作。要實現這些,如果抓取資料後再用 Python 去做網頁來視覺化和共享,就會比較複雜,效率也不高,對於很多非專業人員來說,更是限制了發揮。而如果結合 SeaTable 表格來實現就會非常方便,誰都可以上手。它作為新型的線上協同表格和資訊管理工具,不僅能方便地管理各型別資料,還提供了豐富的資料視覺化功能,也有完善的 Python API 功能等。

本文就來分享如何用 Python 從維基百科中抓取城市資料, 然後自動填入到 SeaTable 表格中,並用 SeaTable 表格的視覺化外掛自動生成地圖、相簿等。下圖是冬奧會舉辦城市的基礎表格。

任務目標:通過每個城市的維基百科連結, 去查詢該城市對應的地理位置(經緯度)並填入到“經緯度“欄位, 同時在維基百科中把該城市的宣傳圖片下載一張,並上傳到“城市圖片“欄位。

自動獲取城市經緯度到表格“經緯度”欄位

從網頁中獲取資訊, 需要一些簡單的 python 爬蟲技術。該任務中採用 requests 和 beatifulsoup 的 Python 模組進行實現, 其中 requests 模組可以模擬線上請求,返回一段 html 的 DOM 樹, beatifulsoup 通過解析 DOM 樹來獲取標籤中的想要的資訊。以維基百科中的一個城市經緯度為例, 該 DOM 樹的結構如下:

只要在網頁中能看到的資訊, 通過 DOM 樹的原始碼都可以查詢到其位置所在, 通過簡單的解析可以提取出想要的內容。具體的解析方法可以參照 beautifulsoup文件。

以下給了一個通過 url 解析其經緯度資訊的程式碼:

import requests
from bs4 import BeautifulSoup
url = "https://en.wikipedia.org/wiki/Chamonix" # 維基百科的城市連結
# 請求該連結, 獲取其內容, 網頁內容是一段 DOM 樹
resp = requests.get(url)
# 把獲取的內容裝進 beatifulsoup解析器,以待解析
soup = BeautifulSoup(resp.content)

# 緯度, 找到 DOM 屬性 class 為 longitude 的結構, 獲取其標籤值
lon = soup.find_all(attrs={"class": "longitude"})[0].string
# 經度,  找到 DOM 屬性 class 為 latitude的結構, 獲取其標籤值
lat = soup.find_all(attrs={"class": "latitude"})[0].string

通過以上找出來的經度緯度的格式是標準的地理格式,寫成如45° 55′ 23.16″ N, 6° 52′ 10.92″ E, 存入 SeaTable 表格中需要將其轉換成十進位制的格式進行寫入。這裡則需要寫一個轉換邏輯進行轉換。

自動獲取城市圖片到表格“城市圖片”欄位

該任務中除了需要知道經緯度的資訊, 還需要把一張圖片下載下來,傳遞到表格中, 同樣的, 圖片也和經緯度一樣, 在 DOM 樹中也可以找到其原始資訊:

其中 img 標籤的 src 值就是我們需要的下載連結, 結合 SeaTable API 的檔案操作,可以輕鬆地把該圖片下載下來,然後上傳到表格當中。以下是該任務的完整程式碼:

import requests
from bs4 import BeautifulSoup
import re
from seatable_api import Base, context
import os
import time
'''
該指令碼演示了通過從維基百科舉行冬奧會的城市資料中摘取相關內容,解析,並把其填入 seatable 表格中的案例
資料包括地理位置的經緯度, 以及代表圖片
'''
SERVER_URL = context.server_url or 'https://cloud.seatable.cn/'
API_TOKEN  = context.api_token  or 'cacc42497886e4d0aa8ac0531bdcccb1c93bd0f5'
TABLE_NAME = "歷屆舉辦地"
URL_COL_NAME = "維基百科城市連結"
CITY_COL_NAME = "舉辦城市"
POSITION_COL_NAME = "經緯度"
IMAGE_COL_NAME = "城市圖片"

def get_time_stamp():
    return str(int(time.time()*10000000))

class Wiki(object):

    def __init__(self, authed_base):
        self.base = authed_base
        self.soup = None

    def _convert(self, tude):
        # 把經緯度格式轉換成十進位制的格式,方便填入表格。
        multiplier = 1 if tude[-1] in ['N', 'E'] else -1
        return multiplier * sum(float(x) / 60 ** n for n, x in enumerate(tude[:-1]))

    def _format_position(self, corninate):
        format_str_list = re.split("°|′|″", corninate)
        if len(format_str_list) == 3:
            format_str_list.insert(2, "00")
        return format_str_list

    def _get_soup(self, url):
        # 初始化DOM解析器
        resp = requests.get(url)
        soup = BeautifulSoup(resp.content)
        self.soup = soup
        return soup

    def get_tu_position(self, url):
        soup = self.soup or self._get_soup(url)

        # 解析網頁的DOM,取出經緯度的數值, 返回十進位制
        lon = soup.find_all(attrs={"class": "longitude"})[0].string
        lat = soup.find_all(attrs={"class": "latitude"})[0].string

        converted_lon = self._convert(self._format_position(lon))
        converted_lat = self._convert(self._format_position(lat))

        return {
            "lng": converted_lon,
            "lat": converted_lat
        }

    def get_file_download_url(self, url):
        # 解析一個DOM,取出其中一個圖片的下載連結

        soup = self.soup or self._get_soup(url)
        src_image_tag = soup.find_all(attrs={"class": "infobox ib-settlement vcard"})[0].find_all('img')
        src = src_image_tag[0].attrs.get('src')
        return "https:%s" % src

    def handle(self, table_name):
        base = self.base
        for row in base.list_rows(table_name):
            try:
                url = row.get(URL_COL_NAME)
                if not url:
                    continue
                row_id = row.get("_id")
                position = self.get_tu_position(url)
                image_file_downlaod_url = self.get_file_download_url(url)
                extension = image_file_downlaod_url.split(".")[-1]

                image_name = "/tmp/wik-image-%s-%s.%s" % (row_id, get_time_stamp(), extension)
                resp_img = requests.get(image_file_downlaod_url)
                with open(image_name, 'wb') as f:
                    f.write(resp_img.content)
                info_dict = base.upload_local_file(
                    image_name,
                    name=None,
                    relative_path=None,
                    file_type='image',
                    replace=True
                )

                row_data = {
                    POSITION_COL_NAME: position,
                    IMAGE_COL_NAME: [info_dict.get('url'), ]
                }
                base.update_row(table_name, row_id, row_data)
                os.remove(image_name)
                self.soup = None
            except Exception as e:
                print("error", row.get(CITY_COL_NAME), e)

def run():
    base = Base(API_TOKEN, SERVER_URL)
    base.auth()

    wo = Wiki(base)
    wo.handle(TABLE_NAME)

if __name__ == '__main__':
    run()

以下是通過執行指令碼自動化寫入資料的表格結果, 可以看出, 比起上網查詢然後手動填寫每一行資料, 指令碼的自動化操作則可以節省大量時間,並且準確和高效。

用 SeaTable 的地圖外掛自動生成城市地圖

有了前面獲取的城市經緯度資訊,我們就可以從 SeaTable 表格的“外掛”欄中一鍵新增地圖外掛,然後僅需簡單點選,就可以根據“經緯度”欄位把城市自動標記到地圖上了。並且還可以標記不同的標籤顏色、設定直接和懸浮顯示欄位等。相比於在表格中去單調的檢視各個城市,通過地圖視覺化顯然更加形象和直觀了。

用 SeaTable 的相簿外掛視覺化城市圖片

相簿外掛同樣可以放置到表格工具欄,方便隨時開啟檢視。在相簿外掛的設定裡,同樣僅需簡單點選,就可以根據表格中的“城市圖片”欄位,把圖片以相簿形式展示出來,並且還可以設定標題名稱、其他顯示欄位。這要比在表格中去瀏覽小圖,顯得更加美觀也更方便了,大大提升了瀏覽體驗。而且點選圖片還可以放大檢視。點選標題還可以直接進入檢視和編輯其在表格中的行內容。

另外,表格還支援靈活的共享協作許可權管控,能滿足細緻的多樣化的共享場景。比如想直接共享地圖、相簿給別人去檢視,還可以直接去表格外掛的外部應用裡新增“地圖”、“相簿”。更多使用,大家可以自行去體驗,這裡不多做介紹。

總結

作為新型的協同表格和資訊管理工具,SeaTable 不僅功能豐富,而且簡單易用。平時我們用 Python 實現一些程式時,就可以靈活結合 SeaTable 表格的功能,從而省去程式設計、開發、維護等時間和人力成本,快速又方便地實現更多有趣的事兒、更完善的應用。也讓工具的運用發揮出更大的價值。

相關文章