【python爬蟲實戰】使用Selenium webdriver採集山東招考數據
1、目標
- 目標:按地區、高校 採集2020年擬在山東招生的所有專業資訊
- 採集地址://xkkm.sdzk.cn/zy-manager-web/gxxx/selectAllDq#
2、Selenium webdriver說明
2.1 為什麼使用webdriver
Selenium Webdriver是通過各種瀏覽器的驅動(web driver)來驅動瀏覽器的,相遇對於使用requests庫直接對網頁進行解析,效率較低,本次使用webdriver庫主要原因是requests庫無法解析該網站
2.2 webdriver支援瀏覽器
- Google Chrome
- Microsoft Internet Explorer 7,8,9,10,11 for Windows Vista,Windows 7,Windows 8,Windows 8.1.
- Microsoft Edge
- Firefox
- Safari
- Opera
2.3 配置與使用說明
webdriver是通過各瀏覽器的驅動程式 來操作瀏覽器的,所以,要有各瀏覽器的驅動程式,瀏覽器驅動要與本地瀏覽器版本對應,常用瀏覽器驅動下載地址如下:
瀏覽器 | 對應驅動下載地址 |
---|---|
chrom(chromedriver.exe) | //npm.taobao.org/mirrors/chromedriver/ |
firefox(geckodriver.exe) | //github.com/mozilla/geckodriver/releases |
Edge | //developer.microsoft.com/en-us/micrsosft-edage/tools/webdriver |
Safari | //webkit.org/blog/6900/webdriver-support-in-safari-10/ |
本文使用Google的chrome瀏覽器,
chrome + webdriver的具體配置和操作說明見 //www.cnblogs.com/cbowen/p/13217857.html
3、採集
3.1 分析網站
-
進入網頁發現各省份地址相同、各高校地址相同,因此想按規律構造每個省份和每個學校的url,並用requests進行解析就無法實現了。
-
於是想到webdriver,來模擬人工操作,獲取當前頁面,再通過xpath定位到要獲取的數據單元。
先用chrome控制台獲取目標數據單元的xpath
通過手動調整xpath,很容易發現省份xpath的規律為
for province_id in rang(1, 33)
province_xpath = '//*[@id="div1"]/div/div[%s]/a' % province_id
再用同樣方法獲取高校的xpat,這裡就不貼截圖了,直接上結果
# sch_id為每個省份的高校id
# schid_xpath,province_xpath,schcode_xpath,school_xpath,subpage_xpath,schhome_xpath分別對應欄位序號、地區、學校程式碼、學校名稱、選考科目要求、學校主頁
schid_xpath = '//*[@id="div4"]/table/tbody/tr[%s]/td[1]/a' % school_id
province_xpath = '//*[@id="div4"]/table/tbody/tr[%s]/td[2]/a' % school_id
schcode_xpath = '//*[@id="div4"]/table/tbody/tr[%s]/td[3]/a' % school_id
school_xpath = '//*[@id="div4"]/table/tbody/tr[%s]/td[4]/a' % school_id
subpage_xpath = '//*[@id="div4"]/table/tbody/tr[%s]/td[5]/a' % school_id
schhome_xpath = '//*[@id="div4"]/table/tbody/tr[%s]/td[6]/a' % school_id
再用同樣方法獲取專業資訊的xpat,直接上結果
# major_id為每個高校專業序號,從1到最後一個專業序號
# i從1到4分別對應欄位「序號」、「層次」、「專業名稱」、「選考科目要求」
for i in range(1, 5):
major_xpath = '//*[@id="ccc"]/div/table/tbody/tr[%s]/td[%s]' % (major_id, i)
3.2 遍歷省份
- 遍歷省份很簡單,一共32個省份,直接用rang(1,33),省去用try來判斷。
- 在函數外啟動瀏覽器,並傳入WebDriver類wd,所有省份遍歷完成後關閉瀏覽器
- 後面要將數據寫入mysql,所有傳入了完成資料庫連接的connect對象conn,並在全部數據寫入後關閉conn連接。
def traverse_province(wd, conn):
"""
循環進入省份
:return:
"""
for province_id in range(1, 33):
province_xpath = '//*[@id="div1"]/div/div[%s]/a' % province_id
wd.find_element_by_xpath(province_xpath).click() # 點擊進入省份
time.sleep(1)
traverse_school(wd, conn) # 遍歷省份內的高校
wd.quit()
conn.close()
3.3 遍歷高校
- 用while True循環來遍歷當前頁所有的高校,用try-except來判斷是否成功捕捉高校資訊,失敗則終端while True循環。
- 獲取高校基本資訊放列表school_info中,傳入下層函數用於冗餘保存高校+專業 完整數據。
- 進入高校的子頁面後,需要重新定位當前操作頁面,wd.window_handles獲取當前瀏覽器所有子頁面句柄,wd.switch_to.window切換至指定頁面。
- 最內層函數traverse_major()會獲取專業數據,並將本層獲取的高校數據和專業數據寫入mysql。
- 在一個高校的全部專業數據寫入完成後,提交一次。
def traverse_school(wd, conn):
"""
遍歷高校資訊
:return:
"""
school_id = 1
while True:
school_info = []
try:
# 獲取高校資訊
for i in [1, 2, 3, 4, 6]:
school_xpath = '//*[@id="div4"]/table/tbody/tr[%s]/td[%s]' % (school_id, i)
text = wd.find_element_by_xpath(school_xpath).text
school_info.append(text)
# 進入高校子頁
wd.find_element_by_xpath('//*[@id="div4"]/table/tbody/tr[%s]/td[5]/a' % school_id).click()
wd.switch_to.window(wd.window_handles[-1]) # 切換到最後一個頁面
traverse_major(school_info, wd, conn) # 遍歷專業
wd.close() # 關閉當前頁
wd.switch_to.window(wd.window_handles[-1]) # 重新定位一次頁面
school_id += 1
except:
break
conn.commit() # 每個高校份提交一次
3.4 採集專業數據
- 將專業資訊結合上層函數傳入的高校資訊冗餘保存。
- 每個高校啟動一次游標。
- 本函數內僅使操作游標進行數據寫入,資料庫的連接在下面函數中,資料庫關閉在最外層函數中。
def traverse_major(school_info, wd, conn):
"""
遍歷專業資訊,最後結合高校資訊一併輸出
:param school_info: 上層函數傳遞進來的高校資訊
:return:
"""
major_id = 1
cursor = conn.cursor()
while True:
major_info = []
try:
for i in range(1, 5):
major_xpath = '//*[@id="ccc"]/div/table/tbody/tr[%s]/td[%s]' % (major_id, i)
text = wd.find_element_by_xpath(major_xpath).text
major_info.append(text)
print(school_info + major_info)
# 寫入mysql
insert_sql = '''
insert into sdzk_data
(school_id,province,school_code,school_name,school_home,major_id,cc,major_name,subject_ask)
values('%s','%s','%s','%s','%s','%s','%s','%s','%s')
''' % (school_info[0], school_info[1], school_info[2], school_info[3], school_info[4],
major_info[0], major_info[1], major_info[2], major_info[3])
cursor.execute(insert_sql)
major_id += 1
except:
break
cursor.close() # 每個高校都重新開啟一次游標
3.5 寫入mysql
- 該函數僅用於創建mysql連接,並創建表。
- 判斷表是否存在,存在則先刪除再創建。
- 函數返回connect類,用於其他函數使用。
- 關閉連接在最外層函數中,直到所有省份數據採集結束後才關閉連接。
def connect_mysql(config):
"""
連接資料庫,並創建表,如果表已存在則先刪除
:param config: mysql資料庫資訊
:return: 返回連接成功的connect對象
"""
create_sql = '''
CREATE table if NOT EXISTS sdzk_data
(school_id int(3),province varchar(20), school_code varchar(5),
school_name varchar(50), school_home varchar(100), major_id int(3),
cc varchar(5), major_name varchar(100), subject_ask varchar(50))
'''
# 判斷表是否存在,存在則刪除,然後創建
conn = pymysql.connect(**config)
cursor = conn.cursor()
cursor.execute('''show TABLEs like "sdzk_data"''')
if cursor.fetchall():
cursor.execute('''drop table sdzk_data''')
cursor.execute(create_sql)
cursor.close()
return conn
4、源碼
源碼地址://github.com/18686622933/sdzk_data