利用PyCharm拿取Package
PyCharm在開發Python專案時是很方便的IDE,隨時增加Package只需要利用快速鍵就會啓動pip進行安裝。但這次利用BeautifulSoup,就碰到了PyCharm一直想要用pip3拿取BeautifulSoup 3的情況,但怎麼安裝都會出現錯誤。只能自行手動的方式進行Package安裝,來規避這個問題
手動安裝成功後就可以開始利用BeautifulSoup進行後續的工作
拿取關卡資料
Sokoban.info上有著目前數量很完整的關卡資料,這些資料主要是放在CDATA裡,因此要利用web scraping的方式,將這些資料取回
<script type="text/javascript">
// <![CDATA[
var Board ="xxxx#####xxxxxxxxxx!xxxx# #xxxxxxxxxx!xxxx#$ #xxxxxxxxxx!xx### $##xxxxxxxxx!xx# $ $ #xxxxxxxxx!### # ## #xxx######!# # ## ##### ..#!# $ $ ..#!##### ### #@## ..#!xxxx# #########!xxxx#######xxxxxxxx!" ;
var BoardXMax = 19 ;
var BoardYMax = 11 ;
var CollectionId = 1 ;
var CollectionLevel = 1 ;
var HighScoreViewing= false ;
var MoveCount ,
MoveCountMax ;
//]]>
</script>
目前最適合用來進行web scraping的工作,非python莫屬。利用Beautiful Soup可以很順利的進行解析html裡各tag。參考了拿取CData的文章和討論
但怎麼樣都無法順利的抽取出CData裡的資料,最後只能用find_all加上regular expression,讓主要的script區留下
import re
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'html.parser')
scripts = soup.find_all("script", string=re.compile("var Board"))
參考了這些資料後,再利用regular expressin進行取代且搭配split,則可以讓關卡中的資料順利取出
- How to extract the substring between two markers?
- Using Regex for Text Manipulation in Python
- Split string using a newline delimiter with Python
- Regex to replace multiple spaces with a single space
利用requests拿取整份html後,進行抽取、取代的部份可以從這份gist看到程式碼
利用requests拿取時可以加入params的參考料可以從下述文章了解
params = {'130_1': ''}
r = requests.get('https://sokoban.info/', params)
print(r.text)
量化拿取關卡資料
利用requests加上BeautifulSoup,拿取單一關上可說是迎刃有餘,接下來要拿取全部的關卡,這就需要一些調整。首先,要知道到底有多少關以及接下來是一批批的拿取關卡,還是一關關的拿取,不論哪一種拿取方式,若是無法一次性完成,那要怎麼樣進行記錄,方便下次拿取剩下來的部份?
有多少關這樣的資訊似乎是每一頁(關)都有,資訊是放在option tag裡
<optgroup label="Thinking Rabbit">
<option value="1" selected="selected">Original & Extra (90)</option>
<option value="2">Boxxle 1 (108)</option>
<option value="3">Boxxle 2 (120)</option>
</optgroup>
<optgroup label="Aymeric du Peloux">
<option value="4">Mini Cosmos (40)</option>
<option value="5">Micro Cosmos (40)</option>
<option value="6">Pico Cosmos (20)</option>
<option value="7">Nabo Cosmos (40)</option>
</optgroup>
再次利用BeautifulSoup加上regular expression和下述的參考資料
抽取資料和文字取代的代碼看起來如下
options = soup.find_all("option")
for o in options:
index = o['value']
content = o.string
pattern = "\([0-9]*\)"
amount_with_parentheses = re.search(pattern, content).group(0)
amount = re.sub(r"\(|\)", '', amount_with_parentheses)
title = re.sub(r" \([0-9]*\)", '', content)
特別注意的是tile那的regular expressin,前方有三個空格,但第二個空格是nbsp,是直接從PyCharm的console裡回貼進來的。
拿取到關卡的資訊後,接下來就是要將這些資訊存下來,方便後續利用。自行產生檔案進行資料存取是個方法,延伸這個想法就進化成用File Db,存取的方式和自行處理類似,但在拿取、回放資料是應該是比較方便的。依據這樣的想方進行查訽,可以看到在python已經有現成的File Db也就是TinyDB。雖然有現成的File Db可用,但實際使用仍是要花些時間了解怎麼用
主要是利用table和document進行資料分類,先產生一個名為overview的table,確認裡面有所有關卡概要資料,也就是多少大關,每大關有多少小關。若是沒有此資訊,則先行拿取大關卡資料。
之後利用dictonary和list及其增減的操作,看哪些大關裡的小關資料需要被拿取。
- Python map() 函数
- Python 初學第五講 — 串列的基本用法
- Python 初學第九講 — 字典
- For loop with range
- How can I add new keys to a dictionary?
- How can I remove a key from a Python dictionary?
資料型別也要進行轉換
調整過後,雖然有些淩亂,但可以進行拿取各大關中的小關卡資料,並將其存下,方便日後使用。這個版本暫時可用,純以拿取資料而言,已不需要大幅度的調整,但這個版本中為了避免一次拿取太多資料而導致程式執行時卡住,暫定一次拿取20筆小關卡的資料。且io部份也沒有進行try catch的處理。待日後對python更了解且有時間進行優化時,或許會利用async等機制調整並加入exception處理。