2017年2月10日

用python爬蟲爬科教館的歷屆科展資料

科展群傑廳是科教館將歷屆科展作品集結起來的資料庫型網站,什麼時候建置的?我只能確定是2008年以後,因為在那之前,我還特別去把生物科展的資料撈出來做成一個網頁連結。(見此文章:來客全國生物科展總匯吧

科展群傑廳建置之後,搜尋資料應該都很順利了吧?是有比以前好,但是還是有很多可以改進的地方。

首先是伺服器的處理速度,我用Google的PageSpeed Tools對科展群傑廳的分類連接進行分析,伺服器要花近10秒的時間,才能載入頁面。預設每個搜尋結果都是呈現10筆資料,每次換下一頁,又要花上近10秒的時間。如果搜尋時選擇全網站搜尋(全國科展和國際科展都一起找),搜尋時間又會花上更久,例如我搜尋「水蘊草」,則要花21.5秒才能呈現第一頁的結果。

第二是顯示模式,搜尋結果預設是顯示摘要的前100字,但是許多科展都沒有把摘要寫好,可能前五十個字都是廢話,因此還得再一一點入,才能看到比較完整的摘要。

每次讓學生上去查東西,都要花上很久的時間,實在太折騰人了,所以我決定了!
就寫一個python爬蟲去把頁面呈現的資訊全部都爬下來,像是標題、作者、摘要、連結...那些。(code就放在本篇文章最底下,是給我自己看的)

我把全部的科展資料都爬下來了,共計9648筆資料,存成csv檔放在雲端硬碟上,你可以直接用搜尋的功能找到想要查的關鍵字。你也可以下載之後,用excel開啟後進行自動篩選或搜尋,速度都會比你在網站上找要快。當你找到要細看的文章時,最後一個欄位是檔案連結位置,可以直接點選或利用瀏覽器下載。

在雲端硬碟上的歷屆科展彙整連結  https://goo.gl/KKX3SX





以下是技術細節:

在科展群傑廳的資料庫裡,每一篇作品都會有一個sid變數(隨便點一篇科展的頁面,看網址列的最後一個變數就是)。所以我就是用sid從1去抓到最後一筆,目前sid用到最大是 13353,所以等到今年(2017)的科展作品也上線時,我就只要從13354繼續往上抓,就可以找到更新的資料了。


我使用的電腦環境是linux,所以我要搜尋科展資料的話,還可以用命令列來搜尋或是直接下載檔案。科展資料彙整的檔案,我存檔為data.csv

如果只是要搜尋關鍵字,可以用grep 去找關鍵字的科展作品
cat data.csv |grep "水蘊"

如果同時要找兩個關鍵字的交集,就只要把前一個關鍵字的搜尋結果,再用pipeline送給第二個grep
cat data.csv |grep "水蘊"|grep "聲音"

如果想要把搜尋出來的結果格式化成為比較容易閱讀的樣子,可把搜尋結果 pipeline到awk
cat data.csv |grep "水蘊"|awk -F"," '{print  "================\n",$2,"\t",$4,"\n================\n",$12,"\n\n" }
結果就會像這樣


搜尋關鍵字後想要一次下載全部有關的作品說明書,可以用awk 把wget的命令寫出來,再送給bash去執行。wget 的-O參數是指定下載的檔案名稱為科展作品的標題,所以這段小程式自動會將pdf檔改名為該科展的標題。
cat data.csv |grep "水蘊"|awk -F"," '{print "wget " $13 " -O " $2".pdf"}' |bash



python 3.0的code如下,之後使用上要特別修改的地方,就是用藍色標示的文字部份。
=====================

#!/usr/bin/env python
#-*- coding: utf-8 -*-

from bs4 import BeautifulSoup
from urllib.request import urlopen


def getData(url2):
    print(url2)
    file = open("data-6000.csv","a")  
    line = str(url2) + ","
   
    url1 = 'http://science.ntsec.edu.tw/Science-Content.aspx?sid='  
    url = url1+str(url2)
    page = urlopen(url)

    soup = BeautifulSoup(page, "html.parser")
   

    #如果網頁標題有Error,代表沒有這份資料
    if "Error" in soup.title.text :
        return

    #印出網頁編碼
    #print(soup.original_encoding)

    #印出科展標題
    for science_title1 in soup.findAll("div", { "class" : "science_title1" }):
        title = science_title1.contents[0]
        title = title.replace(" ", "")
        title = title.replace(",", ",")
        title = title.replace(";", ";")
        title = title.replace("\"", "")
        title = title.replace(r"\n", "")
        print("標題",title)
        line = line + title + ","
        #如果科展標題是評語有"評語",代表不是科展
        if "評語" in title :
            return

    #印出基本資料的欄位名稱和內容
    scienceContent1 = soup.findAll("li", { "class" : "scienceContent1" })
    scienceContent1s = soup.findAll("span", { "class" : "scienceContent1s" })
   
    #如果科展類別的字數小於3,代表無此資料,則跳出副程式
    if len(scienceContent1s[0].contents) == 0 :
        return


   
    for dataName,data in zip(scienceContent1,scienceContent1s):
        dataName = dataName.contents[0][0:-3]
        dataName = dataName.replace(" ", "")
        dataName = dataName.replace(" ", "")
       
        #處理沒有得獎的科展
        if len(data) == 0:
            data = ""
        else:
            data = data.contents[0]
            data = data.replace(" ", "")
            #data = data.replace(r"\r", "")
            data = data.replace(",", ",")
            data = data.replace(";", ";")
        print(dataName,data)
        line = line + data + ","
    #印出摘要
    #abstractName = soup.findAll("div", { "class" : "science_title2" })
   
    for abstract in soup.findAll("div", { "class" : "scienceContent2_font" }):
        abstract = abstract.text
        abstract = abstract.replace("\n", "")
        abstract = abstract.replace(r"\r", "")
        abstract = abstract.replace(",", ",")
        abstract = abstract.replace(";", ";")
        abstract = abstract.replace('^M', "")
       
        print("摘要",abstract)
        line = line + abstract + ","
    print("===")

    for link in soup.findAll("a", { "id" : "ctl00_cphMainContent_aDownload" }):
        #如果連結有正確的話,才加入連結
        if "href" in str(link):
            link = link['href']
            link = "http://science.ntsec.edu.tw/"+link
            print("連結",link)
            line = line + link
    line = line + "\n"
   
    file.write(line)
    file.close()
for url2 in range(5832,6001,1):
    getData(url2)


你可能對這有興趣

Related Posts Plugin for WordPress, Blogger...