這次我想爬取 majortests 中的英文列表。這個網站的組成蠻簡單的(雖然廣告很多),標籤也很一致整齊,很適合做為爬取單字的爬蟲練習。此為我的 github page 上 2021 年 2 月搬運過來之文章,並且有做一些語句上的修正。
前置作業-觀察
在爬網頁之前,要先觀察網頁的結構。這個網頁中的單字表有分 10 個單元,目的是把這幾個單字表的單字都通通抓取下來,因此我們會需要這 10 個單字表的網址。
實際查看後發現,這幾個單字表的網址變化蠻規律且簡單的:
從 https://www.majortests.com/word-lists/word-list-01.html
一直到 https://www.majortests.com/word-lists/word-list-10.html
,能夠藉由更改 word-list- 後方的數字達到造訪每個單字表的目的。
另外單字表在 html 標籤中的位置,對照實際的單字表,可以觀察到單字表在 table
標籤中,它們的 class 都是 wordlist
,而單字本身和解釋,分別在 tr
標籤中用 th
和 td
隔開,是一個很整齊的結構,爬起來不會太麻煩。
功能
接著思考爬蟲,有幾項基本的功能要設計:
- 生成網址
- 改變 header
- 獲取網頁 tag 結構
- 抓取單字
- 爬蟲
- 存成 csv 格式
要 import 的有:
from bs4 import BeautifulSoup import time import csv import requests
生成網址
因為標號是 01, 02, ….,09,雖有十個單字表,但這裡主要想實作爬蟲,因此將過程再弄得更簡單易點,這次我們先只抓取 9 個單字表。URL = "https://www.majortests.com/word-lists/word-list-0{0}.html"
設一個陣列 urls
,用迴圈生成編號從 1 到 9 的網址,並將生成的網址放進裡面。
def generate_urls(url, start, end): urls = [] for i in range(start, end + 1): urls.append(url.format(i)) return urls
改變 header
網站有可能會發現有奇怪的東西(例如一個 Python 程式)在拋出請求,為了不讓網站發現,需要一個 function 去將 header 更改成瀏覽器的樣子。
網路上查會發現有很多不同的寫法,複製任何一種查得到的 header 都可以,只要能順利讓網站接受即可。
def change_get(url): headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' 'AppleWebKit/537.36 (KHTML, like Gecko)' 'Chrome/63.0.3239.132 Safari/537.36'} return requests.get(url, headers = headers)
獲取網頁 tag 結構
這裡沒什麼特別的,僅是把 html 結構抓下來。
def get_html(str): return BeautifulSoup(str, "lxml")
抓取單字
接下來進入到重點部分,如何抓取單字。開始利用 BeautifulSoup 所獲得的 tag 結構,去挖需要的東西。先前我們已經分析過結構了,由於它們的 class 都是 wordlist
,本身很好鎖定,使用 find_all()
抓取。再來單字、解釋們分別在 tr
中的 th
和 td
標籤內。
def get_words(soup, file): words = [] count = 0 for table in soup.find_all(class_ = "wordlist"): count += 1 for word_entry in table.find_all("tr"): newWord = [] newWord.append(file) newWord.append("Group " + str(count)) newWord.append(word_entry.th.text) newWord.append(word_entry.td.text) words.append(newWord) return words
爬蟲
解決完單字處理後,來寫一小段簡單的爬蟲。利用以上的 function,將所要爬的網址改變 header 後,取得該網站結構,並且利用這個結構去找自己要的 tag,並儲存單字在自己設的陣列裡面。
def web_scraping(urls): eng_words = [] for url in urls: file = url.split("/")[-1].split(".")[-2].replace('-', ' ') #利用 url 字串改成檔案來源名稱 print("scrape the file : " + file) r = change_get(url) if r.status_code == requests.codes.ok: soup = get_html(r.text) #取得 html 字串 words = get_words(soup, file) #html 字串的處理 eng_words = eng_words + words print("wait for 5 seconds...") time.sleep(5) else: print("HTTP requests error") return eng_words
存成 csv 格式
上方差不多把事情做完了,記得將結果印出或是存成一個檔案。這裡就是採用先前存好的 file
和 words
,逐一寫入檔案內。
def to_csv(words, file): with open(file, "w+", newline = "", encoding = "utf-8") as fp: writer = csv.writer(fp) for word in words: writer.writerow(word)
最終主程式呼叫
if __name__ == "__main__": urls = generate_urls(URL, 1, 9) eng_words = web_scraping(urls) for item in eng_words: print(item) to_csv(eng_words, "words.csv")
當然要看一下執行結果囉:
結果抓完後發現有很多不會的單字QQ