目錄
1、目標
- 目標:按地區、高校 採集2020年擬在山東招生的所有專業資訊
- 採集地址:http://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) | http://npm.taobao.org/mirrors/chromedriver/ |
firefox(geckodriver.exe) | https://github.com/mozilla/geckodriver/releases |
Edge | https://developer.microsoft.com/en-us/micrsosft-edage/tools/webdriver |
Safari | https://webkit.org/blog/6900/webdriver-support-in-safari-10/ |
本文使用谷歌的chrome瀏覽器,
chrome + webdriver的具體配置和操作說明見 https://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