文集名字已經改成《蜘蛛結網》了,那么這個專題下不限于課程學習的練習題,也有自己的練手和札記。
最近去爬了一個留學服務網站,主要匯集了美國私立中學的信息,學習爬蟲不久,不過憑這門語言的經驗和一些小技巧基本能搞定。
點擊“院校”鏈接進入可以看到諸如所在州,城市,宗教等分類可供篩選。
-------------------------------------Channel 入口----------------------------------------
爬取的思路就是按州的分類來爬取,從URL中可以發現美國各州的縮寫包含在里面,那么就把所有州放入最初的state_list里面:
state_list = '''
MA
CT
... # 有省略,米國所有州縮寫
'''
----------------------------------------Link列表的獲取-----------------------------------
然后就在base_url的基礎上編輯每個州所有學校所在頁面的URL:
for page in range(1, page_num):
school_page = base_url + state + '&page=' + str(page)
這樣就可以寫一個get_school_link(state_list)函數獲取所有州下全部學校的link("href"標簽)。注意到頁面有每頁顯示10條,20條,50條記錄數等,這樣也可以設定我們最初的URL,這個網站上的所有學校數目是可以看到的,因此也就知道每頁50條記錄的話,page的數目設為50就綽綽有余了。當然也可以通過最后一頁無信息的特定標簽來確定爬取頁面到頭了。
把所有學校的link插入MongoDB數據庫中,后續調用爬取每個URL的頁面,并且也可以作為斷點續傳時候總的集合:
school_list_total.insert_one({'school_link': school_link})
------------------------------------------單個頁面的爬取------------------------------------
要獲取的學校信息較多,分別在“概況”,“學術”,“周邊”等link下,因此將每個link分別寫了一個函數準備爬取的時候調用:
學校概況(base_info(url)):
將爬出的數據存入字典base_data中:
base_data = {
'學校名稱': cn_name,
'英文名稱': eg_name,
'學校介紹': introduction[0].get_text().strip(),
'學校類型': school_type,
'建校時間': build_time[0].get_text(),
... # 更多信息省略,strip()是一個很好用的方法,去除字符串之間的空格
'教師學歷': tuition[6].get_text().strip() + '碩士以上',
'SAT分數': tuition[7].get_text().strip(),
}
當然為防止出錯,也要用異常處理的(try-except),否則程序跑著可能因為IndexError,ConnectionError等等類似問題停下,可能是由于網頁中對應的內容沒有,如果沒有我們就在except中將這個內容設為'N/A'。
其他相應的有AP_course(url),society_structure(url),uni_college_list(url), summer_school(url)等函數,分別存入字典course_data, society_info_data,society_info_data, uni_college_data, summer_school中,Python3.X里面字典的操作有合并更新一項,因此幾個字典可以合并一起插入數據庫中存儲。
def get_info(school_link):
try:
data = {'school_link': school_link}
base_data = base_info(school_link) # 學校基本信息
data.update(base_data) # 將學校信息在data中更新
course_data = course_info(school_link + '/academia') # AP課程信息
data.update(course_data)
uni_college_data = uni_college_info(school_link + '/academia') # 升學信息
data.update(uni_college_data)
society_data = society_info(base_url + school_link.split('/')[-1] + '/area') # 社會信息
data.update(society_data)
summer_school_data = summer_school(base_url + school_link.split('/')[-1] + '/summerschool') # 夏校信息
data.update(summer_school_data)
get_info_1.insert_one(data) # 將學校信息插入數據庫
print(school_link)
except (ConnectionError, ConnectionAbortedError):
pass
前面有所有學校的URL,這里我們爬取一所學校就存入一條完成的URL,如果中途服務器連接中斷就可以提取未爬過URL(兩者的差集)的斷點續傳:
db_urls = [item['school_link'] for item in school_list_total.find()]
index_urls = [item['school_link'] for item in get_info_1.find()]
x = set(db_urls)
y = set(index_urls)
rest_of_list = x-y
最后當然要用上proxy(最好有一個列表從里面隨機取),headers,time.sleep()這些小技巧應對反爬,進程池可以提高爬取效率,這樣就可以順利獲得想要的數據了。同時可以寫個小程序統計所用的時間和爬取的數據數目,此處略去一萬字。
-----------------------------------------把數據導出來-----------------------------------------
爬完之后數據是存儲在MongoDB中的呀,如圖所示:
我們想要這些數據為我們后續所用(這里比如我想看到全部的數據啊),那就導出,導出成json格式和csv格式都非常方便。只是注意導出成csv的時候需要指定字段名稱才能導出相應字段,而且打開后可能有亂碼,可以先用notepad++打開再改為ANSI編碼(csv編碼格式),然后保存為csv就沒有問題了。
----------------------------------最后的分割線--------------------------------------------------------
這次的對代碼塊的樣式有了改進,前面有網友吐槽排版,這次終于找到正確的打開方式了,附上一篇簡書上詳細教程
http://www.lxweimin.com/p/q81RER
小試牛刀,學無止境,不敢偷懶,后面還是會繼續學習更新的。