科展群傑廳是科教館將歷屆科展作品集結起來的資料庫型網站,什麼時候建置的?我只能確定是2008年以後,因為在那之前,我還特別去把生物科展的資料撈出來做成一個網頁連結。(見此文章:
來客全國生物科展總匯吧)
最近幾年科教館的網頁已經有更新了,搜尋速度快很多了
科展群傑廳建置之後,搜尋資料應該都很順利了吧?是有比以前好,但是還是有很多可以改進的地方。
首先是伺服器的處理速度,我用Google的PageSpeed Tools對科展群傑廳的分類連接進行分析,伺服器要花近10秒的時間,才能載入頁面。預設每個搜尋結果都是呈現10筆資料,每次換下一頁,又要花上近10秒的時間。如果搜尋時選擇全網站搜尋(全國科展和國際科展都一起找),搜尋時間又會花上更久,例如我搜尋「水蘊草」,則要花21.5秒才能呈現第一頁的結果。
第二是顯示模式,搜尋結果預設是顯示摘要的前100字,但是許多科展都沒有把摘要寫好,可能前五十個字都是廢話,因此還得再一一點入,才能看到比較完整的摘要。
每次讓學生上去查東西,都要花上很久的時間,實在太折騰人了,所以我決定了!
就寫一個python爬蟲去把頁面呈現的資訊全部都爬下來,像是標題、作者、摘要、連結...那些。(code就放在本篇文章最底下,是給我自己看的)
我把全部的科展資料都爬下來了,放在雲端硬碟上,你可以直接用搜尋的功能找到想要查的關鍵字。你也可以下載之後,用excel開啟後進行自動篩選或搜尋,速度都會比你在網站上找要快。當你找到要細看的文章時,最後一個欄位是檔案連結位置,可以直接點選或利用瀏覽器下載。
在雲端硬碟上的歷屆科展彙整連結
科展資料彙整 (至2023)
以下是技術細節:
2024.09.01更新
科教館網頁架構改變,所以重新寫程式抓取2020-2023年的,然後跟舊的合併了。現在就是不是抓sid,而是在每年的架構下,先把該年所有的頁面連結爬出來,然後再爬資料出來。
在科展群傑廳的資料庫裡,每一篇作品都會有一個sid變數(隨便點一篇科展的頁面,看網址列的最後一個變數就是)。所以我就是用sid從1去抓到最後一筆,目前sid用到最大是 13353,所以等到今年(2017)的科展作品也上線時,我就只要從13354繼續往上抓,就可以找到更新的資料了。
2020.0426更新
目前已加入了2019年的資料,最後一筆的sid是16132。下次抓再從16133開始抓
我使用的電腦環境是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如下,之後使用上要特別修改的地方,就是用藍色標示的文字部份。
最底下淺灰色是舊網頁的抓法,已經淘汰。因為2020年時,網頁架構不一樣了,新的抓法如下
=====================
#!/usr/bin/env python
#-*- coding: utf-8 -*-
from bs4 import BeautifulSoup
#import urllib.request
from requests import get
def getData2(num):
print(num)
url_sid = num
url_site = 'https://www.ntsec.edu.tw/Science-Content.aspx?sid='
url = url_site + str(url_sid)
page = get(url)
#print(response.text)
soup = BeautifulSoup(page.text, "html.parser")
#s = soup.title.text
#檢查是否error
#如果Oops,代表沒有這份資料
if 'Oops' in soup.p.text :
print('no data')
return
#標題
title = soup.find(class_ = 'pageTitle')
title = title.get_text()
#print(title + ',')
line = str(num) + '\t'
line = line + title + '\t'
#id
contents = ['ctl00_cphMainContent_ddScienceCategory',
'ctl00_cphMainContent_ddScienceNumber',
'ctl00_cphMainContent_ddSubject',
'ctl00_cphMainContent_ddAward',
'ctl00_cphMainContent_ddSchool',
'ctl00_cphMainContent_ddTeacher',
'ctl00_cphMainContent_ddAuthor',
'ctl00_cphMainContent_ddKeyWord',
'ctl00_cphMainContent_dtNote']
for content in contents:
temp = soup.find(id = content)
#國際科展有些會缺指導老師的資料,另外大多數科展會缺少備註欄位
if temp != None :
temp = temp.get_text()
temp = temp.replace('\n',',')
temp = temp.replace('\t',',')
else:
temp = ''
#print(temp + ',')
line = line + temp + '\t'
#摘要
abstract = soup.find(class_ = 'pagePanel__title')
abstract = abstract.find_all_next('p')
abstract = abstract[0]
abstract = abstract.get_text()
abstract = abstract.replace('\n','')
#print(abstract + ',')
line = line + abstract + '\t'
#pdf link
for link in soup.findAll("a", { "id" : "ctl00_cphMainContent_rptAtt_ctl00_aAttFile" }):
link = "https://www.ntsec.edu.tw/" + link['href']
#print(link)
line = line + link
line = line + "\n"
return str(line)
file = open("data_16000-.csv","a")
for num in range(16000,17000,1):
line = getData2(num)
#print(line)
if line is not None:
file.write(line)
file.close()
=====================
#!/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)