python爬虫获取天猫店铺信息(更新到2020年)

python爬虫获取天猫店铺信息

爬取需求

在天猫搜索一个关键词,然后抓取这个关键词下的相关店铺,由于taobao的反爬策略,只能爬取到第十页大概200个店铺的信息。

效果预览

最终爬取的数据用excel保存,部分数据如下

python爬虫获取天猫店铺信息(更新到2020年)_第1张图片

环境准备

  • python3

  • 合适版本的chromedriver 参考https://blog.csdn.net/BinGISer/article/details/88559532

  • 第三方库:selenium、pandas、BeautifulSoup(pip install bs4)

  • 绑定了taobao账号的微博账号与密码

  • 电脑与网络

代码

# 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里删除重复项,读者可以选择在代码里去重,下面是滑动验证的界面
    python爬虫获取天猫店铺信息(更新到2020年)_第2张图片

  2. 有时候还会遇到出现无法搜到相关店铺的页面,如下图

python爬虫获取天猫店铺信息(更新到2020年)_第3张图片
但其实是能搜到的,做法跟上面一样,返回上一个页面,继续点下一页,同样也会造成数据重复,好处就是程序不容易终止,多试几次就又能找到店铺了,这应该也是taobao的一个反爬策略

总结

如果需要爬取的店铺数量不多的话上述代码可以解决,如果需要高一个数量级的店铺就需要想办法破解那个滑动验证码,如果已经有好的办法的话希望能一起讨论。

你可能感兴趣的:(python学习)