python爬蟲獲取天貓店鋪信息(更新到2020年)

python爬蟲獲取天貓店鋪信息

爬取需求

在天貓搜索一個關鍵詞,然後抓取這個關鍵詞下的相關店鋪,由於taobao的反爬策略,只能爬取到第十頁大概200個店鋪的信息。

效果預覽

最終爬取的數據用excel保存,部分數據如下

在這裏插入圖片描述

環境準備

代碼

# coding=utf-8
# @Time : 2020/6/11 19:51 
# @Author : mxz
# @File : main.py 
# @Software: PyCharm


import re
import time
import pandas as pd
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

from bs4 import BeautifulSoup


from fateadm_api import FateadmApi

PRED_TYPE= "30500"


class TmallShopSpider(object):
    def __init__(self, username, password, chromedriver_path):
        self.url = 'https://login.taobao.com/member/login.jhtml'  # 淘寶登錄地址
        self.username = username  # 接收傳入的 賬號
        self.password = password  # 接收傳入的 密碼
        options = webdriver.ChromeOptions()
        options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})  # 不加載圖片,加快訪問速度
        options.add_experimental_option('excludeSwitches', ['enable-automation'])  # 設置爲開發者模式,防止被各大網站識別出來使用了Selenium
        self.browser = webdriver.Chrome(executable_path=chromedriver_path, chrome_options=options)  # 接收傳入的 chromedriver地址 和設置好的 options
        self.browser.maximize_window()  # 設置窗口最大化
        self.wait = WebDriverWait(self.browser, 10)  # 設置一個智能等待爲10秒

    def login(self,on_login_web):
        if not on_login_web:
            self.browser.get(self.url)
        # username_password_button = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-box.no-longlogin.module-quick > .hd > .login-switch')))  # 用css選擇器選擇 用賬號密碼登錄按鈕
        # username_password_button.click()  # 點擊 用賬號密碼登錄按鈕
        weibo_button = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.weibo-login')))  # 用css選擇器選擇 用微博登錄按鈕
        weibo_button.click()  # 點擊 用微博登錄按鈕
        input_username = self.wait.until(EC.presence_of_element_located((By.XPATH, '//input[@name="username"]')))  # 用xpath選擇器選擇 賬號框
        input_username.send_keys(self.username)  # 輸入 賬號
        input_password = self.wait.until(EC.presence_of_element_located((By.XPATH, '//input[@name="password"]')))  # 用xpath選擇器選擇 密碼框
        input_password.send_keys(self.password)  # 輸入 密碼
        time.sleep(3)
        login_button = self.wait.until(EC.presence_of_element_located((By.XPATH, '//span[text()="登錄"]')))  # 用xpath選擇器選擇 登錄按鈕
        login_button.click()  # 點擊 登錄按鈕


    def getPageTotal(self):
        # 存在登錄後進入滑動驗證碼頁面的情況,此時無法獲取頁數,做法是回退一步再次登錄
        try:
            page_total = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.ui-page-skip > form')))  # 用css選擇器選擇 商品列表頁 總頁數框
            page_total = page_total.text
            page_total = re.match('.*?(\d+).*', page_total).group(1)  # 清洗
            return page_total
        except:
            if self.sliderVerification():
                # self.browser.back()
                self.login(on_login_web=True)
                return self.getPageTotal()


    def dropDown(self):
        # 模擬人類 向下滑動瀏覽(下拉有加速度)
        for i in range(1, 52):
            drop_down = "var q=document.documentElement.scrollTop=" + str(i*100)
            self.browser.execute_script(drop_down)
            time.sleep(0.01)
            if i == 5:
                time.sleep(0.7)
            if i == 15:
                time.sleep(0.5)
            if i == 29:
                time.sleep(0.3)
            if i == 44:
                time.sleep(0.1)
        # 直接下拉到最底部
        # drop_down = "var q=document.documentElement.scrollTop=10000"
        # self.browser.execute_script(drop_down)

    def nextPage(self):
        # 獲取 下一頁的按鈕 並 點擊
        next_page_submit = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.ui-page-next')))
        next_page_submit.click()

    def sliderVerification(self):
        # 滑塊驗證有滑動,但驗證失敗,失敗後人工滑動驗證也會失敗

        # 每次翻頁後 檢測是否有 滑塊驗證
        try:
            slider_button = WebDriverWait(self.browser, 5, 0.5).until(EC.presence_of_element_located((By.ID, 'nc_1_n1z')))
            # action = ActionChains(self.browser)
            # action.click_and_hold(slider_button).perform()
            # action.reset_actions()
            # # 模擬人類 向左拖動滑塊(拖動有加速度)
            # for i in range(100):
            #     action.move_by_offset(i*1, 0).perform()
            #     time.sleep(0.01)
            # action.reset_actions()

            # 嘗試出現驗證碼後返回上一頁
            self.browser.back()
            return True
        except:
            print('沒有檢測到滑塊驗證碼')
            return False

    def crawlShops(self, category):
        # self.login(on_login_web=False)
        self.browser.get('https://list.tmall.com/search_product.htm?q={0}'.format(category))  # 天貓商品列表頁地址,format()裏面輸入要爬取的類目
        shop_button=self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.fType-w ')))
        shop_button.click()
        self.login(on_login_web=True)
        page_total = self.getPageTotal()  # 獲取 商品列表頁 總頁數
        print(''.join(['爬取的類目一共有:', page_total, '頁']))
        shop_data=[]
        for page in range(2, int(page_total)):  # 遍歷 全部 商品列表頁
            page_frame = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.ui-page-skipTo')))  # 獲取 當前頁數框
            page_now = page_frame.get_attribute('value')  # 獲取 當前頁數
            print(''.join(['當前頁數:', page_now, ' ', '總頁數:', page_total]))
            html = self.browser.page_source  # 獲取 當前頁面的 源代碼
            soup=BeautifulSoup(html,'lxml')
            shop_list=soup.find_all(class_="shopHeader-info")
            for shop in shop_list:
                one_data={}
                one_data['種類']=category
                one_data["店名"]=shop.find(class_="sHi-title").text
                one_data["鏈接"]="https://list.tmall.com/"+shop.find(class_="sHi-title")['href']
                one_data["頁數"]=page_now
                shop_data.append(one_data)
                pd.DataFrame(shop_data).to_excel(category+".xlsx")

            self.dropDown()  # 執行 下拉動作
            self.nextPage()  # 執行 按下一頁按鈕動作
            time.sleep(1)
            self.sliderVerification()  # 檢測是否有 滑塊驗證
            time.sleep(1)
            self.checkNotFoundWebPage()
            time.sleep(1)
        pd.DataFrame(shop_data).to_excel(category+".xlsx")

    def checkNotFoundWebPage(self):
        # 翻頁時有時會出現無法找到相關店鋪的頁面,但其實還不是最後一頁店鋪,
        # 此時按返回上一步再次進入下一頁
        try:
            search_tip = WebDriverWait(self.browser, 5, 0.5).until(EC.presence_of_element_located((By.CSS_SELECTOR, '.searchTip-kw')))

            # 嘗試出現驗證碼後返回上一頁
            self.browser.back()
            return True
        except:
            print('沒有檢測到無法搜索店鋪的頁面')
            return False


username = ''  # 你的 微博賬號
password = ''  # 你的 微博密碼
chromedriver_path = 'chromedriver.exe'  # 你的 selenium驅動 存放地址
category = "職業裝"  # 你要爬取的 搜索關鍵詞

if __name__ == '__main__':
    a = TmallShopSpider(username, password, chromedriver_path)
    a.crawlShops(category)

難點

代碼裏面已經有註釋,一些難點與未解決的點列舉如下

  1. sliderVerification用於處理出現滑動驗證碼的情況,在翻頁查找靠後的店鋪時(大概在第十頁開始頻繁出現),註釋裏面有模擬人工滑動驗證的做法,但滑動後驗證不成功,甚至在程序終止後人工去滑動也會一直失敗,所以我選擇了後退一步,回到上個頁面然後繼續點下一頁。經過驗證,這會導致上一頁數據被重新爬取,我選擇了最後在excel裏刪除重複項,讀者可以選擇在代碼裏去重,下面是滑動驗證的界面
    在這裏插入圖片描述

  2. 有時候還會遇到出現無法搜到相關店鋪的頁面,如下圖

在這裏插入圖片描述
但其實是能搜到的,做法跟上面一樣,返回上一個頁面,繼續點下一頁,同樣也會造成數據重複,好處就是程序不容易終止,多試幾次就又能找到店鋪了,這應該也是taobao的一個反爬策略

總結

如果需要爬取的店鋪數量不多的話上述代碼可以解決,如果需要高一個數量級的店鋪就需要想辦法破解那個滑動驗證碼,如果已經有好的辦法的話希望能一起討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章