爬蟲系列(十三) 用selenium爬取京東商品

這篇文章,咱們將經過 selenium 模擬用戶使用瀏覽器的行爲,爬取京東商品信息,仍是先放上最終的效果圖:html

一、網頁分析

(1)初步分析

本來博主打算寫一個可以爬取全部商品信息的爬蟲,但是在分析過程當中發現,不一樣商品的網頁結構居然是不同的python

因此,後來就放棄了這個想法,轉爲只爬取筆記本類型商品的信息web

若是須要爬取其它類型的商品信息,只需把提取數據的規則改變一下就好,有興趣的朋友能夠本身試試看呀chrome

好了,下面咱們正式開始!json

首先,用 Chrome 瀏覽器打開 筆記本商品首頁,咱們很容易發現該網頁是一個 動態加載 的網頁瀏覽器

由於剛打開網頁時只會顯示 30 個商品的信息,但是當咱們向下拖動網頁時,它會再次加載剩下 30 個商品的信息網絡

這時候咱們能夠經過 selenium 模擬瀏覽器下拉網頁的過程,獲取網站所有商品的信息less

>>> browser.execute_script("window.scrollTo(0,document.body.scrollHeight)")

(2)模擬翻頁

另外,咱們發現該網站一共有 100 個網頁ide

咱們能夠經過構造 URL 來獲取每個網頁的內容,可是這裏咱們仍是選擇使用 selenium 模擬瀏覽器的翻頁行爲學習

下拉網頁至底部能夠發現有一個 下一頁 的按鈕,咱們只需獲取並點擊該元素便可實現翻頁

>>> browser.find_element_by_xpath('//a[@class="pn-next" and @onclick]').click()

(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')

二、編碼實現

好了,分析過程很簡單,基本思路是使用 selenium 模擬瀏覽器的行爲,下面是代碼實現

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()

代碼中有幾個須要注意的地方,如今記錄下來便於之後學習:

一、self.fd = open('Jd.csv','w',encoding='utf-8',newline='')

在打開 csv 文件時,最好加上參數 newline='' ,不然咱們寫入的文件會出現空行,不利於後續的數據處理

二、self.browser.execute_script("window.scrollTo(0,document.body.scrollHeight)")

在模擬瀏覽器向下拖動網頁時,因爲數據更新不及時,因此常常出現 StaleElementReferenceException 異常

通常來講有兩種處理方法:

  • 在完成操做後使用 time.sleep() 給瀏覽器充足的加載時間
  • 捕獲該異常進行相應的處理

三、skus = [item.get_attribute('data-sku') for item in skus]

在 selenium 中使用 xpath 語法選取元素時,沒法直接獲取節點的屬性值,而須要使用 get_attribute() 方法

四、無頭啓動瀏覽器能夠加快爬取速度,只需在啓動瀏覽器時設置無頭參數便可

opt = webdriver.chrome.options.Options()
opt.set_headless()
browser = webdriver.Chrome(chrome_options=opt)

【爬蟲系列相關文章】

相關文章
相關標籤/搜索