在天猫搜索一个关键词,然后抓取这个关键词下的相关店铺,由于taobao的反爬策略,只能爬取到第十页大概200个店铺的信息。
最终爬取的数据用excel保存,部分数据如下
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)
代码里面已经有注释,一些难点与未解决的点列举如下
sliderVerification用于处理出现滑动验证码的情况,在翻页查找靠后的店铺时(大概在第十页开始频繁出现),注释里面有模拟人工滑动验证的做法,但滑动后验证不成功,甚至在程序终止后人工去滑动也会一直失败,所以我选择了后退一步,回到上个页面然后继续点下一页。经过验证,这会导致上一页数据被重新爬取,我选择了最后在excel里删除重复项,读者可以选择在代码里去重,下面是滑动验证的界面
有时候还会遇到出现无法搜到相关店铺的页面,如下图
但其实是能搜到的,做法跟上面一样,返回上一个页面,继续点下一页,同样也会造成数据重复,好处就是程序不容易终止,多试几次就又能找到店铺了,这应该也是taobao的一个反爬策略
如果需要爬取的店铺数量不多的话上述代码可以解决,如果需要高一个数量级的店铺就需要想办法破解那个滑动验证码,如果已经有好的办法的话希望能一起讨论。