使用Selenium爬取京東電商數據(以手機商品為例)


進入京東(https://www.jd.com)后,我如果搜索特定的手機產品,如oppo find x2,會先出現如下的商品列表頁

如果點擊進入其中一個商品會進入到如下圖所示的商品詳情頁,可以看到用戶對該商品的評論:

 

這篇博客主要是記錄我怎么爬取商品列表頁和詳情頁,我使用Selenium,模擬瀏覽器實現自動化的用戶瀏覽操作,能在一定程度上規避反爬蟲(爬取平台對你進行屏蔽操作)的風險。總體來說,列表頁和詳情頁的爬取數據過程中用戶模擬行為如下所示:

 

一、商品列表頁數據爬取

首先,先介紹爬取列表頁所需要的實現的功能:

  • 設置模擬瀏覽器(open_browser)
  • 初始化參數(init_variable)
  • 解析並爬取列表單頁內容(parse_JDpage)
  • 翻頁(turn_JDpage)
  • 自動化爬取多頁(JDcrawl)

1. 設置模擬瀏覽器

def open_browser(self):
    # 若下列命令報錯,請進入下面鏈接下載chromedriver然后放置在/user/bin/下即可
    # https://chromedriver.storage.googleapis.com/index.html?path=2.35/
    self.options = webdriver.ChromeOptions()
    self.options.add_argument(self.user_agent)
    self.browser = webdriver.Chrome(options = self.options)
    # 隱式等待:等待頁面全部元素加載完成(即頁面加載圓圈不再轉后),才會執行下一句,如果超過設置時間則拋出異常
    try:
        self.browser.implicitly_wait(10)
    except:
        print("頁面無法加載完成,無法開啟爬蟲操作!")
    # 顯式等待:設置瀏覽器最長允許超時的時間
    self.wait = WebDriverWait(self.browser, 10)

上述代碼解釋:

  •  webdriver.ChromeOptions() :如果運行后報錯,請下載chomedriver至/user/bin/下即可解決問題。
  •  self.user_agent :用戶代理(user-agent)用於幫助網站識別請求用戶的瀏覽器類別,以便於網站發送相應的網頁數據。有人建議不加可以anti反爬蟲,但這里我是加上了(可右擊網頁‘檢查’里找到header后,從里面獲取user_agent,例如 "Mozilla/** (X11; Linux x**) AppleWebKit/*** (KHTML, like Gecko) Chrome/7*** Safari/**" )。
  • 關於Selenium中的等待有三種:強制等待,隱式等待和顯式等待。 (1) 強制等待就是 time.sleep() ,就等待完指定時間后便進行后續操作; (2) 隱式等待是 implicitly_wait() ,括號內設置的是頁面加載最長等待時間,如果頁面在規定時間內加載完成就直接進行下一步。如果頁面在規定時間都沒加載完,就會超時報錯。注意:隱式等待是作用在整個瀏覽器周期中,不需要像強制等待那樣需要多次設置。(3) 顯式等待是 WebDriverWait() ,它是讓程序每隔幾秒(默認poll_frequency=0.5, 0.5秒)看下,如果出現了所需元素就執行下一步,但如果間隔檢查一直沒檢測到所需元素且檢查總時長達到給的最長超時時間(即參數timeout,單位是秒),就會報無元素錯誤。如果隱式和顯式等待一起用,等待的最長的時間取兩者最大的時長。一般推薦在定位元素前檢查元素存在使用顯式等待。

 

2. 初始化參數

def init_variable(self, url_link, search_key, user_agent):
    # url_link為電商平台首頁,search_key為商品搜索詞
    self.url = url_link
    self.keyword = search_key
    self.isLastPage = False
    self.user_agent = user_agent

上述參數分別是:

  •  url :京東首頁鏈接。
  •  keyword :商品搜索詞,這里我用的是‘oppo find x2’。
  •  isLastPage :布爾值,當前頁面是否為頁末。
  •  user_agent :用戶代理。

 

3. 解析並爬取列表單頁內容

def parse_JDpage(self):
    try:
        # 定位元素並獲取元素下的字段值(商品標題,價格,評論數,商品鏈接)
        names = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//div[@class="gl-i-wrap"]/div[@class="p-name p-name-type-2"]/a/em')))
        prices = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//div[@class="gl-i-wrap"]/div[@class="p-price"]/strong/i')))
        comment_nums = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//div[@class="gl-i-wrap"]/div[@class="p-commit"]/strong')))
        links = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//li[@class="gl-item"]')))
        page_num = self.wait.until(EC.presence_of_element_located((By.XPATH, '//div[@class="page clearfix"]//input[@class="input-txt"]')))
        names = [item.text for item in names]
        prices = [price.text for price in prices]
        comment_nums = [comment_num.text for comment_num in comment_nums]
        links = ["https://item.jd.com/{sku}.html".format(sku = link.get_attribute("data-sku")) for link in links]
        page_num = page_num.get_attribute('value')
    except selenium.common.exceptions.TimeoutException:
        print('parse_page: TimeoutException 網頁超時')
        self.parse_JDpage()
    except selenium.common.exceptions.StaleElementReferenceException:
        print('turn_page: StaleElementReferenceException 某元素因JS刷新已過時沒出現在頁面中')
        print('刷新並重新解析網頁...')
        self.browser.refresh()
        self.parse_JDpage()
        print('解析成功')
    return names, prices, comment_nums, links, page_num

上述代碼解釋如下:

  • 用 try...except... 是因為爬蟲不是每次都能爬取順利的,有時候會因為各種原因而報錯,詳情可見selenium的常見異常,例如:網絡慢或京東反爬蟲給你空白信息,導致網頁超時。而某元素因為更新不及時,導致報錯 StaleElementReferenceException 。在不同情況使用不同的手段,我在爬取京東列表頁時,只要給夠等待時間,一般來說是不會報網頁超時的,而且京東對列表頁的爬蟲相對寬松些,不會像后面要爬的詳情頁那樣時不時反爬蟲給你空白信息,導致網頁超時。此外,對待元素沒出現在Dom(Document Object Model, 文檔對象模型,它將HTML文檔以樹結構表達)中,只要重新刷新再重爬就好了。
  •  EC.presence_of_all_elements_located 和 EC.presence_of_element_located ,這兩方法是判斷是否至少有一個元素或某個元素存在與Dom樹中,直白來說,就是檢查你要爬的元素字段在不在當前頁面上。
  •  By.XPATH :Selenium有很多元素定位方法,詳情請見Selenium八種元素定位方法。個人偏好用Xpath,易學且Chrome在檢查元素里還能直接右擊復制出Xpath。具體使用方法可參考python+selenium基礎之XPATH定位
  •  wait.until() :until是當定位的元素出現則說明該顯示等待檢查間隔內檢查成功,可以進行下面的代碼了。與之相反的是 wait.until_not() ,當定位元素消失則進行后續代碼,而不再等待。
  • 建議讀者結合真實場景內容去看代碼會更快理解以上內容。

 

4. 翻頁

def turn_JDpage(self):
    # 移到頁面末端並點擊‘下一頁’
    try:
        self.browser.find_element_by_xpath('//a[@class="pn-next" and @onclick]').click()
        time.sleep(1) # 點擊完等1s
        self.browser.execute_script("window.scrollTo(0, document.body.scrollHeight)")
        time.sleep(2) # 下拉后等2s
    # 如果找不到元素,說明已到最后一頁
    except selenium.common.exceptions.NoSuchElementException:
        self.isLastPage = True
    # 如果頁面超時,跳過此頁
    except selenium.common.exceptions.TimeoutException:
        print('turn_page: TimeoutException 網頁超時')
        self.turn_JDpage()
    # 如果因為JS刷新找不到元素,重新刷新
    except selenium.common.exceptions.StaleElementReferenceException:
        print('turn_page: StaleElementReferenceException 某元素因JS刷新已過時沒出現在頁面中')
        print('刷新並重新翻頁網頁...')
        self.browser.refresh()
        self.turn_JDpage()
        print('翻頁成功')

  上述代碼解釋:

  •  self.browser.execute_script("window.scrollTo(0, document.body.scrollHeight)") 是模擬瀏覽器向下滑動網頁,避免由於頁面展示不全而報錯。
  •  self.browser.refresh() :頁面刷新。
  •  time.sleep() :強制等待下,過快會被京東反爬蟲。

 

5. 自動化爬取多頁

def JDcrawl(self, url_link, search_key, user_agent, save_path):
    # 初始化參數
    self.init_variable(url_link, search_key, user_agent)
    df_names = []
    df_prices = []
    df_comment_nums = []
    df_links = []

    # 打開模擬瀏覽器
    self.open_browser()
    # 進入目標JD網站
    self.browser.get(self.url)
    # 在瀏覽器輸入目標商品名,然后搜索
    self.browser.find_element_by_id('key').send_keys(self.keyword)
    self.browser.find_element_by_class_name('button').click()

    # 開始爬取
    self.browser.execute_script("window.scrollTo(0, document.body.scrollHeight)")
    print("################\n##開啟數據爬蟲##\n################\n")
    while self.isLastPage != True:
        page_num = 0
        names, prices, comment_nums, links, page_num = self.parse_JDpage()
        print("已爬取完第%s頁" % page_num)
        df_names.extend(names)
        df_prices.extend(prices)
        df_comment_nums.extend(comment_nums)
        df_links.extend(links)
        self.turn_JDpage()

    # 退出瀏覽器
    self.browser.quit()

    # 保存結果
    results = pd.DataFrame({'title':df_names,
                            'price':df_prices,
                            'comment_num':df_comment_nums,
                            'url':df_links})
    results.to_csv(save_path, index = False)
    print("爬蟲全部結束,共%d條數據,最終結果保存至%s" % (len(results),save_path))

以上代碼就是將之前模塊組合在一起,這里不過多進行過多解釋。

 

二、商品詳情頁數據爬取

當我們爬取完列表頁數據后,可以通過依次進入各個商品詳情頁爬取用戶評論內容。雖然大體流程跟列表頁爬取流程差不多,但具體爬取過程的細節上還是有些不同的處理方法。

  • 清洗列表頁數據(clean_overview)
  • 設置模擬瀏覽器(open_browser)
  • 初始化參數(init_variable)
  • 解析並爬取詳情單頁內容(parse_JDpage)
  • 翻頁(turn_JDpage)
  • 自動化爬取多頁(JDcrawl_detail)

1. 清洗列表頁數據

因為爬取商品列表中的商品很多不是我們真正想要的爬取的內容,比如:我們不想看二手拍拍的商品,只想看oppo find x2而不適它的pro版本,另外也不想看oppo find x2的相關配件商品。所以,我們要對列表頁數據執行清洗過程:(1) 不要評論少的商品;(2) 不要商品名稱帶‘拍拍’標簽的商品;(3) 不要部分匹配搜索詞的商品;(4) 不要價格過低的商品。清洗代碼如下:

def clean_overview(self, csv_path, search_keyword):
    '''
        清洗數據
            1. 清洗掉少於11條評論的數據
            2. 清洗掉含‘拍拍’關鍵詞的數據
            3. 清洗掉不含搜索關鍵詞的數據
            4. 清洗掉價格過低的數據

        輸入:
            1. csv_path                (str): 爬取的overview商品展示頁下的結果文件路徑。
            2. search_keyword          (str): 爬取的overview商品展示頁下搜索關鍵詞。
        輸出:
            1. overview_df    (pd.DataFrame): 清洗好的overview數據
    '''
    overview_path = csv_path
    overview_df = pd.read_csv(overview_path)
    search_key = search_keyword

    # 1. 清洗掉少於11條評論的數據
    print("原始數據量:%s" % len(overview_df))
    drop_idxs = []
    comment_nums = overview_df['comment_num']
    for i, comment_num in enumerate(comment_nums):
        try:
            if int(comment_num[:-3]) in list(range(11)):
                drop_idxs.append(i)
        except:
            pass
    print("清洗掉少於11條評論的數據后的數據量:%s(%s-%s)" % (len(overview_df) - len(drop_idxs), len(overview_df), len(drop_idxs)))
    overview_df.drop(drop_idxs, axis = 0, inplace = True)
    overview_df = overview_df.reset_index(drop = True)

    # 2. 清洗掉含‘拍拍’關鍵詞的數據
    drop_idxs = []
    comment_titles = overview_df['title']
    for i, title in enumerate(comment_titles):
        try:
            if title.startswith('拍拍'):
                drop_idxs.append(i)
        except:
            pass
    print("清洗掉含‘拍拍’關鍵詞的數據后的數據量:%s(%s-%s)" % (len(overview_df) - len(drop_idxs), len(overview_df), len(drop_idxs)))
    overview_df.drop(drop_idxs, axis = 0, inplace = True)
    overview_df = overview_df.reset_index(drop = True)

    # 3. 清洗掉不含搜索關鍵詞的數據
    drop_idxs = []
    comment_titles = overview_df['title']
    for i, title in enumerate(comment_titles):
        if search_key.replace(" ","") not in title.lower().replace(" ",""):
            drop_idxs.append(i)
    print("清洗掉不含搜索關鍵詞的數據后的數據量:%s(%s-%s)" % (len(overview_df) - len(drop_idxs), len(overview_df), len(drop_idxs)))
    overview_df.drop(drop_idxs, axis = 0, inplace = True)
    overview_df = overview_df.reset_index(drop = True)

    # 4. 清洗掉價格過低/過高的數據
    drop_idxs = []
    comment_prices = overview_df['price']
    prices_df = {}
    for p in comment_prices:
        if p not in list(prices_df.keys()):
            prices_df[p] = 1
        else:
            prices_df[p] += 1
    # print("各價格下的商品數:", prices_df)
    # {4499: 89, 5999: 5, 6099: 1, 12999: 2, 6999: 1, 89: 1, 29: 1}
    # 通過上述結果,我們只要價位為4499的商品結果即可了
    for i, p in enumerate(comment_prices):
        if p != 4499.0:
            drop_idxs.append(i)
    print("清洗掉價格過低/過高的數據后的數據量:%s(%s-%s)" % (len(overview_df) - len(drop_idxs), len(overview_df), len(drop_idxs)))
    overview_df.drop(drop_idxs, axis = 0, inplace = True)
    overview_df = overview_df.reset_index(drop = True)
    return overview_df

 

2. 設置模擬瀏覽器

同樣地,我們先進行瀏覽器設置。注意:在這里我們的隱式等待和顯式等待都增加了,這里的原因是因為京東對詳情頁的爬取加重防守了,所以我們得給頁面加載和元素定位更多的等待和嘗試時間,不然會什么都爬不到。

def open_browser(self):
    '''設置瀏覽器'''
    # 若下列命令報錯,請進入下面鏈接下載chromedriver然后放置在/user/bin/下即可
    # https://chromedriver.storage.googleapis.com/index.html?path=2.35/
    self.options = webdriver.ChromeOptions()
    self.browser = webdriver.Chrome(options = self.options)
    # 隱式等待:等待頁面全部元素加載完成(即頁面加載圓圈不再轉后),才會執行下一句,如果超過設置時間則拋出異常
    try:
        self.browser.implicitly_wait(50)
    except:
        print("頁面無法加載完成,無法開啟爬蟲操作!")
    # 顯式等待:設置瀏覽器最長允許超時的時間
    self.wait = WebDriverWait(self.browser, 30)

 

3. 初始化參數

這里增加了兩個變量 isLastPage 和 ignore_page ,因為京東會自動折疊掉用戶默認評價(即忽略評價),如果點擊查看忽略評價會蹦出新的評論窗口,所以后續代碼需要這兩個變量幫助爬蟲正常進行。

def init_variable(self, csv_path, search_key, user_agent):
    '''初始化變量'''
    self.csv_path = csv_path # 商品總覽頁爬取結果文件路徑
    self.keyword = search_key # 商品搜索關鍵詞
    self.isLastPage = False # 是否為頁末
    self.ignore_page = False # 是否進入到忽略評論頁面
    self.user_agent = user_agent # 用戶代理,這里暫不用它

 

4. 解析並爬取詳情單頁內容

def parse_JDpage(self):
    try:
        time.sleep(10) # 下拉后等10s
        # 定位元素(用戶名,用戶等級,用戶評分,用戶評論,評論創建時間,購買選擇,頁碼)
        user_names = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//div[@class="user-info"]')))
        user_levels = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//div[@class="user-level"]')))
        user_stars = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//div[@class="comment-column J-comment-column"]/div[starts-with(@class, "comment-star")]')))
        comments = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//div[@class="comment-column J-comment-column"]/p[@class="comment-con"]')))
        order_infos = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, '//div[@class="comment-item"]//div[@class="order-info"]')))
        if self.ignore_page == False:
            # 如果沒進入忽略頁
            page_num = self.wait.until(EC.presence_of_element_located((By.XPATH, '//a[@class="ui-page-curr"]')))
        else:
            # 如果進入忽略頁
            page_num = self.wait.until(EC.presence_of_element_located((By.XPATH, '//div[@class="ui-dialog-content"]//a[@class="ui-page-curr"]')))
        # 獲取元素下的字段值
        user_names = [user_name.text for user_name in user_names]
        user_levels = [user_level.text for user_level in user_levels]
        user_stars = [user_star.get_attribute('class')[-1] for user_star in user_stars]
        create_times = [" ".join(order_infos[0].text.split(" ")[-2:]) for order_info in order_infos]
        order_infos = [" ".join(order_infos[0].text.split(" ")[:-2]) for order_info in order_infos]
        comments = [comment.text for comment in comments]
        page_num = page_num.text
    except selenium.common.exceptions.TimeoutException:
        print('parse_page: TimeoutException 網頁超時')
        self.browser.refresh()
        self.browser.find_element_by_xpath('//li[@data-tab="trigger" and @data-anchor="#comment"]').click()
        time.sleep(30)
        user_names, user_levels, user_stars, comments, create_times, order_infos, page_num = self.parse_JDpage()
    except selenium.common.exceptions.StaleElementReferenceException:
        print('turn_page: StaleElementReferenceException 某元素因JS刷新已過時沒出現在頁面中')
        user_names, user_levels, user_stars, comments, create_times, order_infos, page_num = self.parse_JDpage()
    return user_names, user_levels, user_stars, comments, create_times, order_infos, page_num

上述代碼解釋:

  • 注意:如果進入查看忽略頁,需要在彈出的忽略評論窗口進行‘下一頁’的點擊操作,這個大家可以自己去網站看看。
  • 另外一個很重要的一點就是在報錯‘網頁超時’的處理方式,我在爬蟲的時候遇到一個問題,就是京東的反爬蟲引起的,有時候當我進入某個商品詳情頁后,點擊進入評論區(存在評論),但是評論區會顯示 暫無評價 ,即使我加大了點擊評論區前的等待時間也沒用,但是后面我刷新瀏覽器再進入評論區就有了,所以我在Timeout報錯下采用了如下的解決方法:刷新頁面 -> 重點擊進入評論區 -> 等待30秒 -> 再爬蟲 -> 還報錯就繼續重復以上步驟

 

5. 翻頁

def turn_JDpage(self):
    # 移到頁面末端並點擊‘下一頁’
    try:
        if self.ignore_page == False:
            self.browser.find_element_by_xpath('//a[@class="ui-pager-next" and @clstag]').send_keys(Keys.ENTER)
        else:
            self.browser.find_element_by_xpath('//a[@class="ui-pager-next" and @href="#none"]').send_keys(Keys.ENTER)
        time.sleep(3) # 點擊完等3s
        self.browser.execute_script("window.scrollTo(0, document.body.scrollHeight)")
        time.sleep(5) # 下拉后等5s
    # 如果找不到元素
    except selenium.common.exceptions.NoSuchElementException:
        if self.ignore_page == False:
            try:
                # 如果有忽略評論的頁面但沒進入,則進入繼續翻頁
                self.browser.find_element_by_xpath('//div[@class="comment-more J-fold-comment hide"]/a').send_keys(Keys.ENTER)
                self.ignore_page = True
                print("有忽略評論的頁面")
            except:
                # 如果沒有忽略評論的頁面且最后一頁
                print("沒有忽略評論的頁面")
                self.ignore_page = True
                self.isLastPage = True
        else:
            # 如果有忽略評論的頁面且到了最后一頁
            print("沒有忽略評論的頁面")
            self.isLastPage = True
    except selenium.common.exceptions.TimeoutException:
        print('turn_page: TimeoutException 網頁超時')
        time.sleep(30)
        self.turn_JDpage()
    # 如果因為JS刷新找不到元素,重新刷新
    except selenium.common.exceptions.StaleElementReferenceException:
        print('turn_page: StaleElementReferenceException 某元素因JS刷新已過時沒出現在頁面中')
        self.turn_JDpage()

需要注意的是:

  • 翻頁要慢,太快容易被反爬蟲,所以等待時間一定要增加。
  • 當報錯是 NoSuchElementException ,即找不到‘下一頁’按鈕,可能會三種情況:(1) 正常展示的評論頁已到翻到頁末了;(2) 忽略評論的頁面已翻到頁末了;(3) 京東反爬蟲,不顯示‘下一頁’按鈕給你點擊。對於情況1,就是查看有沒有忽略評論,如果有就點擊查看並爬取它們,如果沒有就結束當前商品的爬取。對於情況2,直接結束爬取。對於情況3,這個我是抱着‘給我反爬蟲,那我就不爬這商品’的態度去做的,因為確實沒有好的手段,但抱着‘不爬完不罷休’心態的朋友可以把這種爬蟲失敗的商品先記錄下來,現階段爬完再重爬它們也行。

 

6. 自動化爬取多頁

def JDcrawl_detail(self, csv_path, search_key, user_agent):
    # 初始化參數
    self.init_variable(csv_path, search_key, user_agent)
    unfinish_crawls = 0 # 記錄因反爬蟲而沒有完全爬取的商品數
    # 清洗數據
    self.overview_df = self.clean_overview(self.csv_path, self.keyword)

    # 依次進入到單獨的商品鏈接里去
    for url in tqdm(list(self.overview_df['url'][3:])):
        df_user_names = []
        df_user_levels = []
        df_user_stars = []
        df_comments = []
        df_create_times = []
        df_order_infos = []

        # 打開模擬瀏覽器
        self.open_browser()
        # 進入目標網站
        self.browser.get(url)
        time.sleep(35)
        # 進入評論區
        self.browser.find_element_by_xpath('//li[@data-tab="trigger" and @data-anchor="#comment"]').click()
        time.sleep(15)
        # 開始爬取
        self.browser.execute_script("window.scrollTo(0, document.body.scrollHeight)")
        self.isLastPage = False
        self.ignore_page = False
        self.lastpage = 0
        print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + " 開啟數據爬蟲url:",url)
        while self.isLastPage != True:
            page_num = 0
            user_names, user_levels, user_stars, comments, create_times, order_infos, page_num = self.parse_JDpage()
            # 如果某頁因為反爬蟲無法定位到‘下一頁’元素,導致重復爬取同一頁,則保留之前的爬取內容,然后就不繼續爬這個商品了
            if self.lastpage != page_num:
                self.lastpage = page_num
                print("已爬取完第%s頁" % page_num)
                df_user_names.extend(user_names)
                df_user_levels.extend(user_levels)
                df_user_stars.extend(user_stars)
                df_comments.extend(comments)
                df_create_times.extend(create_times)
                df_order_infos.extend(order_infos)
                self.turn_JDpage()
            else:
                unfinish_crawls += 1
                self.browser.quit()
                break
        # 退出瀏覽器
        self.browser.quit()

        # 保存結果
        results = pd.DataFrame({'user_names':df_user_names,
                                'user_levels':df_user_levels,
                                'user_stars':df_user_stars,
                                'omments':df_comments,
                                'create_times':df_create_times,
                                'order_infos':df_order_infos})
        url_id = url.split('/')[-1].split('.')[0]
        save_path = r'/media/alvinai/Documents/comment_crawler/JD_results/Detail/' + str(url_id) + '.csv'
        results.to_csv(save_path, index = False)
        print("爬蟲結束,共%d條數據,結果保存至%s" % (len(results),save_path))

與列表頁爬取不同在於,這里是爬完一個商品詳情就保存它的結果文件,避免‘一損具損’的局面出現。另外等待時間也給多了(避免京東反爬蟲)。

 

給大家展示下爬取界面: 

 

我的部分爬取結果如下:

 

三、總結

1. 京東對列表頁的爬蟲的防守沒有詳情頁嚴格,所以在對詳情頁的時候要增加等待時間並且增加一些針對反爬蟲的操作。

2. 在我爬蟲詳情頁的過程中,京東的反爬蟲有以下兩個體現:(1) 點擊進入評論區后,不給我顯示評論(解決辦法:刷新瀏覽器重新進入評論區);(2) 評論區不給我顯示‘下一頁’按鈕,這樣我就沒法定位並點擊它實現翻頁(暫無完美解決方法,減少這種情況發生的手段是增加進入評論區和爬去內容過程中強制等待的時間,后期可以考慮記錄下這些網頁並重新爬取)。

3. 在我上面的代碼中,我增加了對忽略評論的爬取,原因是:盡管評論主體的文本內容沒有價值,但是用戶們的購買選擇是具有價值的,通過這些購買選擇,我們后面能進行用戶對於機型、顏色、內存搭配、套餐選擇的偏好分析。

4. 這是個人下班之余做的自我學習項目,與工作內容無關

5. 我的代碼落地性不強,原因是爬蟲速度慢,這也為了考慮到是個人項目,如果要速度上去,不僅可能要考慮多線程,還有購買代理ip等操作,那樣的話,就等於完全深入到爬蟲里面了,我不是我的目的,因為我只想要它的數據。而且真要落地那種爬蟲,代碼再優雅而離不開行為的‘野蠻’,對爬蟲平台造成大的服務器負擔反而不好。從爬蟲平台去考慮,他們要區分你是爬蟲還是正常瀏覽,一個常見的思路就是你的瀏覽行為是否符合真實用戶的瀏覽行為,這也是我為什么我的爬蟲等待時間比較長,因為只要你不增加平台服務器負擔,他們還是會‘讓‘着點你的。

6. 這些數據有什么用?有用,比如說(1)如上面第3點所說,可以進行自身或競品分析,從出售的機型版本、顏色、內存、套裝選擇、價格等進行統計性分析和對比; (2) 文本數據的觀點抽取,標簽生成,情感分析等,用以進行痛點和賣點的挖掘。這也是我接下來會想繼續嘗試的方向。

7. 完整代碼已開源至:https://github.com/AlvinAi96/COI

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM