【python爬蟲實戰】使用Selenium webdriver採集山東招考數據

1、目標

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