1. selenium基礎
selenium部分可以去看我寫的selenium基礎部分,由於鏈接太多了這里就不發出來了。
代理ip:
有時候頻繁爬取一些網頁。服務器發現你是爬蟲后會封掉你的ip地址。這時候我們可以更改代理ip。更改代理ip不同的瀏覽器有不同的實現方式。這里使用我最常用的Chrome瀏覽器為例。
from selenium import webdriver chromeOptions = webdriver.ChromeOptions() # 設置代理 chromeOptions.add_argument("--proxy-server=http://202.20.16.82:10152") # 一定要注意,=兩邊不能有空格,不能是這樣--proxy-server = http://202.20.16.82:10152 driver = webdriver.Chrome(chrome_options = chromeOptions) # 查看本機ip,查看代理是否起作用 driver.get("http://httpbin.org/ip") print(driver.page_source) # 退出,清除瀏覽器緩存 driver.quit()
注意事項:
第一,選擇穩定的固定的代理IP。不要選擇動態代理IP。我們常用的爬蟲IP代理通常都是具有高度保密性質的高匿名動態IP,是通過撥號動態產生的,時效性非常的短,一般都是在3分鍾左右。
第二,選擇速度較快的代理IP。因為selenium爬蟲采用的是瀏覽器渲染技術,這種瀏覽器渲染技術速度就本身就很慢。如果選擇的代理IP速度較慢,爬取的時間就會進一步增加。
第三,要有足夠大的電腦內存。因為chrome占內存較大,在並發度很高的情況下,容易造成瀏覽器崩潰,也就是程序崩潰。
第四,在程序結束時,調用driver.quit( )清除瀏覽器緩存。
2. selenium爬蟲實例
選案例真的給我整吐了,開始想弄最常用的淘寶,結果一點搜索就要登錄,然后就是天貓,點擊下一頁就需要登錄,搞得我就爬了第一頁。最后還是京東好,什么都可以。
2.1 初步分析
像京東、淘寶、天貓這些網站都是動態加載,剛打開只會加載幾十條數據,當滑動條到達一定位置的時候,才會繼續加載。這時候我們可以通過selenium模擬瀏覽器下拉網頁的過程,獲取網站全部商品的信息。
browser.execute_script("window.scrollTo(0,document.body.scrollHeight)")
2.2 模擬翻頁
在前面,我們如果要爬取查詢的每一頁的內容,我們只能分析url,找規律,才能跳轉到下一頁,並獲取數據。
現在我們就可以使用xpath定位+selenium點擊,來模擬瀏覽器的翻頁行為了。
下拉網頁至底部可以發現有一個下一頁的按鈕,我們只需獲取並點擊該元素即可實現翻頁。
browser.find_element_by_xpath('//a[@class="pn-next" and @onclick]').click()
2.3 獲取數據
接下來,我們需要解析每一個網頁來獲取我們需要的數據,具體包括(可以使用selenium選擇元素):
商品 ID:browser.find_elements_by_xpath('//li[@data-sku]'),用於構造鏈接地址
商品價格:browser.find_elements_by_xpath('//div[@class="gl-i-wrap"]/div[2]/strong/i')
商品名稱:browser.find_elements_by_xpath('//div[@class="gl-i-wrap"]/div[3]/a/em')
評論人數:browser.find_elements_by_xpath('//div[@class="gl-i-wrap"]/div[4]/strong')
2.4 代碼實現
from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By import selenium.common.exceptions import json import csv import time class JdSpider(): def open_file(self): self.fm = input('請輸入文件保存格式(txt、json、csv):') while self.fm!='txt' and self.fm!='json' and self.fm!='csv': self.fm = input('輸入錯誤,請重新輸入文件保存格式(txt、json、csv):') if self.fm=='txt' : self.fd = open('Jd.txt','w',encoding='utf-8') elif self.fm=='json' : self.fd = open('Jd.json','w',encoding='utf-8') elif self.fm=='csv' : self.fd = open('Jd.csv','w',encoding='utf-8',newline='') def open_browser(self): self.browser = webdriver.Chrome() self.browser.implicitly_wait(10) self.wait = WebDriverWait(self.browser,10) def init_variable(self): self.data = zip() self.isLast = False def parse_page(self): try: skus = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//li[@class="gl-item"]'))) skus = [item.get_attribute('data-sku') for item in skus] links = ['https://item.jd.com/{sku}.html'.format(sku=item) for item in skus] prices = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//div[@class="gl-i-wrap"]/div[2]/strong/i'))) prices = [item.text for item in prices] names = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//div[@class="gl-i-wrap"]/div[3]/a/em'))) names = [item.text for item in names] comments = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//div[@class="gl-i-wrap"]/div[4]/strong'))) comments = [item.text for item in comments] self.data = zip(links,prices,names,comments) except selenium.common.exceptions.TimeoutException: print('parse_page: TimeoutException') self.parse_page() except selenium.common.exceptions.StaleElementReferenceException: print('parse_page: StaleElementReferenceException') self.browser.refresh() def turn_page(self): try: self.wait.until(EC.element_to_be_clickable((By.XPATH,'//a[@class="pn-next"]'))).click() time.sleep(1) self.browser.execute_script("window.scrollTo(0,document.body.scrollHeight)") time.sleep(2) except selenium.common.exceptions.NoSuchElementException: self.isLast = True except selenium.common.exceptions.TimeoutException: print('turn_page: TimeoutException') self.turn_page() except selenium.common.exceptions.StaleElementReferenceException: print('turn_page: StaleElementReferenceException') self.browser.refresh() def write_to_file(self): if self.fm == 'txt': for item in self.data: self.fd.write('----------------------------------------\n') self.fd.write('link:' + str(item[0]) + '\n') self.fd.write('price:' + str(item[1]) + '\n') self.fd.write('name:' + str(item[2]) + '\n') self.fd.write('comment:' + str(item[3]) + '\n') if self.fm == 'json': temp = ('link','price','name','comment') for item in self.data: json.dump(dict(zip(temp,item)),self.fd,ensure_ascii=False) if self.fm == 'csv': writer = csv.writer(self.fd) for item in self.data: writer.writerow(item) def close_file(self): self.fd.close() def close_browser(self): self.browser.quit() def crawl(self): self.open_file() self.open_browser() self.init_variable() print('開始爬取') self.browser.get('https://search.jd.com/Search?keyword=%E7%AC%94%E8%AE%B0%E6%9C%AC&enc=utf-8') time.sleep(1) self.browser.execute_script("window.scrollTo(0,document.body.scrollHeight)") time.sleep(2) count = 0 while not self.isLast: count += 1 print('正在爬取第 ' + str(count) + ' 頁......') self.parse_page() self.write_to_file() self.turn_page() self.close_file() self.close_browser() print('結束爬取') if __name__ == '__main__': spider = JdSpider() spider.crawl()
代碼中需要注意的地方:
1.self.fd = open('Jd.csv','w',encoding='utf-8',newline='')
在打開csv文件時,最好加上參數newline='',否則我們寫入的文件會出現空行,不利於后續的數據處理。
2.self.browser.execute_script("window.scrollTo(0,document.body.scrollHeight)")
在模擬瀏覽器向下拖動網頁時,由於數據更新不及時,所以經常出現StaleElementReferenceException異常,我們可以在操作中加入time.sleep()給瀏覽器充足的加載時間,或者就是捕獲該異常進行相應的處理了。
3.skus = [item.get_attribute('data-sku') for item in skus]
在selenium中使用xpath語法選取元素時,無法直接獲取節點的屬性值,而需要使用get_attribute()方法。
4.無頭啟動瀏覽器可以加快爬取速度,只需在啟動瀏覽器時設置無頭參數即可。
opt = webdriver.chrome.options.Options()
opt.set_headless()
browser = webdriver.Chrome(chrome_options=opt)