使用selenium爬取淘宝
一、出现的问题
前段时间在使用selenium对淘宝进行模拟登陆的时候,输入完正好和密码,然后验证码无论如何都不能划过去。找了好久,原来是因为selenium在浏览器中运 行的时候会暴露一些特征变量,被识别出来是爬虫,所以无法进行登录操作。如在非selenium运行的时候"window.navigator.webdriver"是undefined,但是在 selenium运行的情况下,它是true。
二、解决方法
1、网上大部分的方案
启动浏览器的时候加上一些配置和手动把webdriver的属性设置为undefined。
option = ChromeOptions() option.add_experimental_option('excludeSwitches', ['enable-automation']) #option.add_argument('--headless') web= webdriver.Chrome(options=option) web.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": """Object.defineProperty(navigator, 'webdriver', {get: () => undefined})""", })
但是这个方案并不能有效的解决,于是又在网上找到另外的一种方案。
2、使用python的mitmproxy库进行操作。
mitmproxy 就是用于 MITM 的 proxy,MITM 即中间人攻击(Man-in-the-middle attack)。用于中间人攻击的代理首先会向正常的代理一样转发请求,保障服务端与客户端的通信,其次,会适时的查、 记录其截获的数据,或篡改数据,引发服务端或客户端特定的行为。
使用 pip install mitmproxy
新建一个py文件,命名随意,这里命名为modify_response.py
# coding: utf-8 # modify_response.py from mitmproxy import ctx def response(flow): """修改响应数据 """ if '/js/yoda.' in flow.request.url: # 屏蔽selenium检测 for webdriver_key in ['webdriver', '__driver_evaluate', '__webdriver_evaluate', '__selenium_evaluate', '__fxdriver_evaluate', '__driver_unwrapped', '__webdriver_unwrapped', '__selenium_unwrapped', '__fxdriver_unwrapped', '_Selenium_IDE_Recorder', '_selenium', 'calledSelenium', '_WEBDRIVER_ELEM_CACHE', 'ChromeDriverw', 'driver-evaluate', 'webdriver-evaluate', 'selenium-evaluate', 'webdriverCommand', 'webdriver-evaluate-response', '__webdriverFunc', '__webdriver_script_fn', '__$webdriverAsyncExecutor', '__lastWatirAlert', '__lastWatirConfirm', '__lastWatirPrompt', '$chrome_asyncScriptInfo', '$cdc_asdjflasutopfhvcZLmcfl_']: ctx.log.info('Remove "{}" from {}.'.format(webdriver_key, flow.request.url)) flow.response.text = flow.response.text.replace('"{}"'.format(webdriver_key), '"NO-SUCH-ATTR"') print(webdriver_key) flow.response.text = flow.response.text.replace('t.webdriver', 'false') flow.response.text = flow.response.text.replace('ChromeDriver', '')
然后在cmd中使用命令运行脚本:mitmdump.exe -p 端口号 -s modify_response.py
然后再执行selenium的脚本即可实现正常的通过selenium进行登录淘宝网站,之前设置的ChromeOptions也要加上。当然要设置代理。
三、代码实现
此次代码实现了自动登录,输入关键词,爬取淘宝商品的商品名称,店铺的省份,商品的价格和人气等信息,并将这些信息保存在CSV文件中,以方便进行数据的分析。具体代码如下:
from selenium import webdriver from selenium.webdriver import ChromeOptions from selenium.webdriver import ActionChains from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time import csv def main(): #登录设置 #会开会话 option = ChromeOptions() option.add_experimental_option('excludeSwitches', ['enable-automation']) #option.add_argument('--headless') web= webdriver.Chrome(options=option) web.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": """Object.defineProperty(navigator, 'webdriver', {get: () => undefined})""", }) web.get('https://login.taobao.com/member/login.jhtml') #输入账号和密码 web.find_element_by_xpath('//*[@id="fm-login-id"]').send_keys('xxxxxx') web.find_element_by_xpath('//*[@id="fm-login-password"]').send_keys('xxxxxxxx') web.find_element_by_xpath('//*[@id="login-form"]/div[4]/button').click() #进入首页 try: WebDriverWait(web,20).until(EC.element_to_be_clickable((By.XPATH,'//*[@id="J_SiteNavHome"]/div/a/span'))).click() except: pass # 搜索商品 goods=input('请输入您要搜索的商品:') WebDriverWait(web,20).until(EC.presence_of_element_located((By.XPATH,'//*[@id="q"]'))).send_keys(goods) WebDriverWait(web,20).until(EC.element_to_be_clickable((By.XPATH,'//*[@id="J_TSearchForm"]/div[1]/button'))).click() try: #查找共计页数 sum_page=web.find_element_by_xpath('//*[@id="mainsrp-pager"]/div/div/div/div[1]').text except: # 防止出现滑块的验证 WebDriverWait(web,5).until(EC.presence_of_element_located((By.XPATH,'//*[@id="nc_1_n1z"]'))) actoin = ActionChains(web) drag = web.find_element_by_xpath('//*[@id="nc_1_n1z"]') actoin.drag_and_drop_by_offset(drag,300,0).perform() WebDriverWait(web,20).until(EC.presence_of_element_located((By.XPATH,'//*[@id="mainsrp-pager"]/div/div/div/div[1]'))) sum_page = web.find_element_by_xpath('//*[@id="mainsrp-pager"]/div/div/div/div[1]').text print(goods,sum_page) #输入查找范围 min_page = int(input('请输入搜索的最小页数:')) max_page = int(input('请输入搜搜的最大页数:')) #解析和保存数据 f = open('C:/Users/sunshine/Desktop/课件/图片/爬取的数据/' + '淘宝' + goods+'.csv', 'w+', encoding='utf-8',newline='') csvwrite=csv.writer(f) csvwrite.writerow(('shop_name', 'goods_name', 'loc', 'prices', 'sum_body')) for min_page in range(min_page,max_page+1): try: if min_page!=1: key_=WebDriverWait(web, 20).until( EC.element_to_be_clickable((By.XPATH, '//*[@id="mainsrp-pager"]/div/div/div/div[2]/input'))) key_.clear() key_.send_keys(min_page) key_.send_keys(Keys.ENTER) #WebDriverWait(web,20).until(EC.element_to_be_clickable(( #By.XPATH,'//*[@id="mainsrp-pager"]/div/div/div/div[2]/span[3]'))).click() #web.find_element_by_xpath('//*[@id="mainsrp-pager"]/div/div/div/div[2]/span[3]').click() #web.execute_script("window.scrollTo(0,document.body.scrollHeight);") time.sleep(2) #WebDriverWait(web, 20).until( #EC.element_to_be_clickable((By.XPATH, '//*[@id="mainsrp-pager"]/div/div/div/div[2]/span[3]'))) else: web.execute_script("window.scrollTo(0,document.body.scrollHeight);") time.sleep(2) except Exception as e: xpath_=web.find_element_by_xpath('//*[@id="J_sufei"]/iframe') web.switch_to.frame(xpath_) action=ActionChains drag=web.find_element_by_xpath('//*[@id="nc_1__scale_text"]/span') action(web).drag_and_drop_by_offset(drag,300,0).perform() web.switch_to.frame(web.find_element_by_xpath('//*[@id="CrossStorageClient-ba26ffda-7fa9-44ef-a87f-63c058cd9d01"]')) print(e,'出现滑块验证') key_ = WebDriverWait(web, 20).until( EC.element_to_be_clickable((By.XPATH, '//*[@id="mainsrp-pager"]/div/div/div/div[2]/input'))) key_.clear() key_.send_keys(min_page) key_.send_keys(Keys.ENTER) #WebDriverWait(web, 20).until( #EC.element_to_be_clickable((By.XPATH, '//*[@id="mainsrp-pager"]/div/div/div/div[2]/span[3]'))).click() time.sleep(2) list = web.find_elements_by_xpath('//*[@id="mainsrp-itemlist"]/div/div/div[1]/div') for items in list: prices = items.find_element_by_xpath('./div[2]/div[1]/div[1]/strong').text prices = float(prices) goods_name=items.find_element_by_xpath('./div[2]/div[2]/a').text body=items.find_element_by_xpath('./div[2]/div[1]/div[2]').text if '万' in body: body=re.findall(r'\d+.\d+|\d+', body)[0] sum_body=float(body)*10000 elif len(body)!=0: body = re.findall(r'\d+.\d+|\d+', body)[0] sum_body = float(body) else: sum_body=None shop_name=items.find_element_by_xpath('./div[2]/div[3]/div[1]/a/span[2]').text loc=items.find_element_by_xpath('./div[2]/div[3]/div[2]').text[:3] #tuple=((shop_name,goods_name,loc,prices,sum_body)) print((shop_name,goods_name,loc,prices,sum_body)) csvwrite.writerow((shop_name,goods_name,loc,prices,sum_body)) print('===============第'+str(min_page)+'页已爬取完成!=======================') f.close() web.close() return goods