1. 程式人生 > >爬蟲1.5-ajax資料爬取

爬蟲1.5-ajax資料爬取

目錄

爬蟲-ajax資料爬取

1. ajax資料

ajax (非同步JavaScript 和 XML)(讀作阿賈克斯),ajax可以時網頁實現非同步更新,一般使用的json資料互動,即在不重新載入整個頁面也可以對網頁的部分進行更新,ajax技術載入的資料在網頁原始碼中是看不到的,只能看到url載入的html部分

獲取ajax資料的兩種方式

1 分析ajax呼叫的介面,發現url的引數,再通過程式碼請求這個介面,直接拿到json資料,有時json資料可能是加密的,或者傳送post請求獲取json資料並且表單中的資料是按照一定規則寫的,這時需要分析js程式碼,就很麻煩了。

2 Selenium+chromedriver 獲取動態資料,Selenium 相當於可以模擬輸入、刪除和點選操作以及cookie chromedriver是一個驅動chrome瀏覽器的驅動程式,使用它可以驅動瀏覽器。這種方式可以直接獲取網頁中全部的程式碼,包括ajax技術載入的資料。例如有的網站點選“更多”按鈕後可以載入的資料,將全部被獲取。雖然這種方式簡單粗暴,但每次請求頁面時都會開啟一個頁面,載入資料和渲染頁面,顯得過於笨重。

2. selenium+chromedriver知識準備

chromedriver是谷歌瀏覽器的驅動程式,使用selenium可以操作它

chromedriver.exe需要放在一個非中文目錄下

2.1 selenium+chromedriver簡單操作

form selenium import webdriver
driver_path = r'D:\chromedriver\chromedriver.exe'  # 指明路徑
driver = webdriver.Chrome(executable_path=driver_path)  # 建立driver例項,將路徑傳入
driver.get("https://www.baidu.com")  # get方式開啟百度,注意一定要加https:// 否則會報錯
driver.page_source # 獲取原始碼,然後可以扔給正則表示式或者xpath解析,這裡可以直接獲取ajax資料
driver.close() #關閉一個頁面
driver.quit() #關閉整個瀏覽器

2.2 常用表單元素

input type='text/password/email/number' 文字框
button input [type='submit'] 按鈕 例如登陸
checkbox input type='checkbox' 例如記住密碼選項
select 下拉選單

2.3 模擬點選

driver.get('https://www.douban.com/')
remenberBtn = driver.find_element_by_id('form_remember')
remenberBtn.click()

2.4 行為鏈

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains  #匯入driver和行為鏈類
driver_path = r'D:\chromedriver\chromedriver.exe'   #指定路徑
driver = webdriver.Chrome(executable_path=driver_path)  #建立物件
driver.get('https://www.qq.com/')   #開啟網頁
moveTag = driver.find_element_by_xpath("//div[@class='more-txt']") #定位騰訊網中“更多”按鈕
clickTag = driver.find_element_by_xpath("//ul[@class='sub-list cf']/li[1]")  
#定位“獨家”按鈕
actions =ActionChains(driver)   # 建立行為鏈物件
actions.move_to_element(moveTag)   # 移動到定位點
actions.click(moveTag)  # 點選   好像不點選也可以
actions.move_to_element(clickTag)  # 移動到”獨家“按鈕
actions.click(clickTag) # 點選
actions.perform()  # 必須呼叫的函式   否則不會執行之前定義好的行為

# drver.find_element_by_xpath("") 意味著可以使用xpath語法找到想要點選的按鈕或輸入框
# 類似的還有id  class  css

2.5 頁面等待

1 隱式等待
driver.implicitly_wait(x)  # x是等待時間   x秒
2 顯示等待
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
driver_path = r'D:\chromedriver\chromedriver.exe'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://new.qq.com/ch/ori/')
try:
    WebDriverWait(driver, 10).until(         
        # 這裡使用WebDriverWait類填入引數   10代表等待10s未出現則丟擲異常
    EC.presence_of_element_located((By.CLASS_NAME, 's ')) 
        #這裡就是等待條件 即當class屬性=s 被載入後條件達成
)
finally:
    print("2222")   # 這樣的話當沒有等待到期望的條件就可以做其他事情

2.6 視窗

開啟多個視窗
driver.get('https://new.qq.com/ch/ori/')
driver.execute_script("window.open('https://www.douban.com/')") 
#使用JavaScript指令碼開啟新頁面
driver.get('https://new.qq.com/ch/ori/')
driver.execute_script("window.open('https://www.douban.com/')")   # 開啟兩個頁面
print(driver.current_url)   # 列印當前url
driver.switch_to.window(driver.window_handles[1]) #  切換視窗   視窗列表中第二個視窗的索引是1
print(driver.current_url)  # 列印當前url

2.7 代理ip

driver_path = r'D:\chromedriver\chromedriver.exe'
options = webdriver.ChromeOptions()  # 建立配置物件
options.add_argument("--proxy-server=http://221.6.32.206:41816")  # 寫入配置
driver = webdriver.Chrome(executable_path=driver_path, options=options)  # 寫入引數
driver.get('https://www.baidu.com/s?wd=ip')

3. selenium+chromedriver實戰拉勾網爬蟲程式碼

# 拉勾網的反爬太噁心了,於是用selenium+chromedriver來爬,結果還是被封了,我去

import re
import time
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from lxml import etree


class LagouSpider(object):
    def __init__(self):
        driver_path = r'D:\chromedriver\chromedriver.exe'  # 指定chromedriver 的路徑
        self.driver = webdriver.Chrome(executable_path=driver_path)
        self.base_url = 'https://www.lagou.com/jobs/list_python?px=default&city=%E6%88%90%E9%83%BD#filterBox'   # 拉勾網搜尋python的頁面url
        self.data = []  # 用來存放字典

    def get_url_list(self):
        while True:
            self.driver.get(self.base_url)  # 訪問拉勾網
            # 獲取頁面全部文字資訊,這裡可以直接獲取ajax技術返回的資料,
            # 如果用requests庫需要找到傳送json請求的資料包
            text = self.driver.page_source
            try:
                # 等待職位資訊的url出現,timeout=3s
                WebDriverWait(self.driver, 3).until(
                    EC.presence_of_element_located((By.CLASS_NAME, 'position_link'))
                )
                # 獲取url並呼叫get_info函式
                url_list = re.findall(r'<a class="position_link" href="(.*?)" target=.*?>', text, re.S)
                for i in url_list:
                    self.get_info(i)
            finally:
                # 判斷是否已到最後一頁
                if len(re.findall(r'class="pager_next pager_next_disabled"', text, re.S)) != 0:
                    self.driver.quit()
                    break
                # 找到下一頁的位置並點選
                else:
                    nextPageTag = self.driver.find_element_by_xpath("//div[@class='pager_container']/span[last()]")
                    nextPageTag.click()


    def get_info(self, url):
        # 開啟新視窗,移動driver,不移動無法獲取page_source
        self.driver.execute_script("window.open('{}')".format(url))
        self.driver.switch_to.window(self.driver.window_handles[1])
        try:
            # 提取資訊,因為職位描述資訊的標籤實在是太亂了,還是用xpath舒服一點
            html = etree.HTML(self.driver.page_source)
            work_name = re.findall(r'<div class="job-name" title="(.*?)">', self.driver.page_source, re.S)[0]            
            salary = re.findall(r'<span class="salary">(.*?)</span>', self.driver.page_source, re.S)[0]
            # 獲取職位描述資訊
            temp = html.xpath("//dd[@class='job_bt']//div/p/text()")
            describe = ''
            for i in temp:
                describe += i
            describe = re.subn(r'\s*', '', describe)
            temp = {
                'job_name': work_name,
                'describe': describe[0],
                'salary': salary
            }
            print(temp)
            # 放入列表中,方便寫入csv或者txt
            self.data.append(temp)
        except:
            # 出錯的頁面打印出來
            print(url)
            time.sleep(5)
        finally:
            # 關閉頁面,移動driver到最開始的base_url
            self.driver.close()
            self.driver.switch_to.window(self.driver.window_handles[0])
            time.sleep(1)

    def run(self):
        self.get_url_list()


if __name__ == '__main__':
    spider = LagouSpider()
    spider.run()